0%

文章字数:1,阅读全文大约需要1分钟

1
URL resource = Test.class.getClassLoader().getResource("application.properties");

文章字数:286,阅读全文大约需要1分钟

主键除了自己设置,还有很多种方法可以让数据库自动生成。将注解写在实体类属性上就可以获得相应功能

注解

  • @GeneratorValue注解—-JPA通用策略生成器
  1. 参数GenerationType
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public enum GenerationType{    

    TABLE, //特定表生成 见3

    SEQUENCE, //数据库底层 见4

    IDENTITY, //自增序列生成

    AUTO //默认,自动选择

    }
  2. 参数generator:其它生成器的名字,配合@GenericGenerator使用
    1
    2
    3
    @GeneratedValue(generator="system-uuid")//关联system-uuid
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    //system-uuid策略是uuid 还可以是uuid.hex(16进制)共12种策略
  3. TABLE生成策略使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.TABLE,generator="table_gen")
    @TableGenerator(
    name = "table_gen",
    table="fendo_generator",
    pkColumnName="seq_name", //指定主键的名字
    pkColumnValue="fendos", //指定下次插入主键时使用默认的值
    valueColumnName="seq_id", //该主键当前所生成的值,它的值将会随着每次创建累加
    initialValue = 1, //初始化值
    allocationSize=1 //累加值
    )
  4. SEQUENCE使用

    部分数据库(Oracle,PostgreSQL,DB2)支持序列对象,因为其不支持主键自增,所以这是自增的补充方法

    1
    2
    3
    @Id  
    @GeneratedValue(strategy = GenerationType.SEQUENCE,generator="fendo_seq")
    @SequenceGenerator(name="fendo_seq", sequenceName="seq_name")
  • @GenericGenerator注解—-自定义主键生成策略
    可选部分
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    GENERATORS.put("uuid", UUIDHexGenerator.class);  

    GENERATORS.put("hilo", TableHiLoGenerator.class);

    GENERATORS.put("assigned", Assigned.class);

    GENERATORS.put("identity", IdentityGenerator.class);

    GENERATORS.put("select", SelectGenerator.class);

    GENERATORS.put("sequence", SequenceGenerator.class);

    GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);

    GENERATORS.put("increment", IncrementGenerator.class);

    GENERATORS.put("foreign", ForeignGenerator.class);

    GENERATORS.put("guid", GUIDGenerator.class);

    GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated

    GENERATORS.put("sequence-identity", SequenceIdentityGenerator.class);

文章字数:474,阅读全文大约需要1分钟

java.awt.Robot 提供模拟点击,模拟用户输入等操作

引入

1
2
3
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;

键盘操作

  1. 按下按键
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* keycode:KeyEvent.VK_PAGE_UP
*/
public static final void pressSingleKeyByNumber(int keycode) {
try {
/** 创建自动化测试对象 */
Robot robot = new Robot();
/**按下按键*/
robot.keyPress(keycode);
/**松开按键*/
robot.keyRelease(keycode);
/**可以稍作延时处理*/
robot.delay(500);
} catch (AWTException e) {
e.printStackTrace();
}
}
  1. 组合键
    1
    2
    3
    4
    5
    6
    7
    //先按下control再按下其它,松开其它再松开control
    Robot robot = new Robot();
    robot.keyPress(KeyEvent.VK_CONTROL);
    robot.keyPress(keycode);
    robot.keyRelease(keycode);
    robot.keyRelease(KeyEvent.VK_CONTROL);
    robot.delay(100);

鼠标操作

  1. 点击
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

/**
* 模拟用户单击屏幕指定区域,默认单击屏幕最中央
*
* @param x:x坐标
* @param y:y坐标
*/
public static final void clickScreenByXY(Integer x, Integer y) {
try {
/**创建工具包对象*/
Toolkit toolkit = Toolkit.getDefaultToolkit();
/**创建自动化对象*/
Robot robot = new Robot();
/**利用工具包对象获取屏幕分辨率*/
if (x == null) {
x = toolkit.getScreenSize().width / 2;
}
if (y == null) {
y = toolkit.getScreenSize().height / 2;
}
/**
* 移动鼠标到指定位置
* 然后按下鼠标左键,再松开,模拟单击操作
*/
robot.mouseMove(x, y);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(100);
} catch (AWTException e) {
e.printStackTrace();
}
}
  1. 移动鼠标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

/**
* 自动将鼠标移动到指定的位置
* 如果参数x与y为null,则默认将鼠标放在屏幕右侧中间隐藏
*
* @param x:x坐标 ,左上角 为0----设定值超过屏幕分辨率也没关系
* @param y:y坐标 ,左上角 为0----设定值超过屏幕分辨率也没关系
*/
public static final void mouseMoveToXY(Integer x, Integer y) {
try {
/**创建工具包对象*/
Toolkit toolkit = Toolkit.getDefaultToolkit();
/**创建自动化对象*/
Robot robot = new Robot();
/**利用工具包对象获取屏幕分辨率*/
if (x == null) {
x = toolkit.getScreenSize().width;
}
if (y == null) {
y = toolkit.getScreenSize().height / 2;
}
/**
* 移动鼠标到指定位置
* robot.delay(100);延时100毫秒
*/
robot.mouseMove(x, y);
robot.delay(100);
} catch (AWTException e) {
e.printStackTrace();
}
}

常用按键

按键都在 KeyEvent类中,和键盘对应


文章字数:846,阅读全文大约需要3分钟

构造器参数太多

  1. 需要解决成员变量太多,使用多个构造方法不直观的问题
  2. 直接使用get set无法限制bean中那些数据是必要的
  • 解决方法:Builder模式
  • 内部使用一个子类作为构造器
  • 构造器的get set返回本身,这样可以链式调用
  • 最终的构造方法里检测必要的字段是否有值

不需要实例化的对象,构造器私有

  • xxxUtil xxTools 内部都是static方法,不需要实例化
  • 防止使用者实例该对象

不要创建多余对象

  • 自动装箱导致的多余对象
    1
    2
    3
    4
    Long sum = 0L;
    for(long i = 0; i<xx; i++) {
    sum = sum + i; // sum是包装类型,i是基本类型,所以这里会先自动装箱成对象
    }
  • 使用static

避免使用终结方法

  • finaliza()方法虚拟机不保证此方法会被按时执行或是执行
  • 释放资源必须显式释放,不能依赖此方法

类和成员变量可访问性最小化

  • 主要作用是为了解耦
  • 只向外暴露接口,实体间调用只依赖与接口,具体实现解耦

可变性最小化

  • 类尽量做成不可变类
  • 类不可变就线程安全了
  • 类成员变量私有,不提供成员变量设置,返回对象时返回复制的值等

优先使用复合,而不是继承

  • 使用父类的方法会破坏父类的包装性
  • 例如父类实现类内部相互调用,重写一个方法都可能影响其他方法
  • 复合就是内部包含一个类,使用这个类完成具体方法

接口优于抽象类

  • 一般来说,接口只能定义类将会有的行为,抽象类里可以具体实现
  • 接口更为纯粹,不依赖于具体实现,而抽象类可以有实现。并且支持多实现
  • 可以先定义一个接口,再声明一个抽象类,实现类继承这两个(jdk的set等都是这么实现的)

可变参数的使用

  • 如果可变参数中至少要一个参数
1
int sum(int a, int... args)

返回零长度的数组和集合,而不是null

  • 客户端可以用判空

优先使用标准异常

  • 代码重用,使用jdk已经提供的
  • IlegalAraumentException 参数异常

使用枚举而不 是常量

  • 还可以枚举内使用另一个内部枚举当作构造方法参数,以使枚举有不同的性质
  • 即策略枚举

局部变量作用域最小化

  • 即不要一开始就声明局部变量,要到使用的时候再使用
  • 防止不应该使用局部变量的地方能够修改局部变量

精确计算不适用float和double

  • 使用 int long BigDecimal

当心字符串连接的性能

  • 每一次拼接都需要将字符串复制一遍

控制方法的大小

  • 方法尽量再50-80行

常用性能指标

  • 并发数约等于在线人数的5%-15%
  • 吞吐量 每分钟、没秒钟处理数量
  • 一般平均响应时间短,吞吐量就大,反之小。但是吞吐量大,响应时间未必短

常见性能优化

  • 避免过早优化
  • 性能测试,定位性能瓶颈
  • 分而治之,逐步优化

文章字数:94,阅读全文大约需要1分钟

FlushMode提供了4种缓存模式:MANUAL、COMMIT、AUTO和ALWAYS。使用setFlushModel切换

  • MANUAL 必须手动调用flush()
  • COMMIT 事物提交才会
  • AUTO 事务提交或者手动刷新,都能将脏数据同步到数据库。除此之外,某些查询出现的时候也会导致缓存刷新。(不过不是很智能)
  • ALWAYS总是,一般不用

文章字数:95,阅读全文大约需要1分钟

  1. spring在创建时会搜索所有的JpaRepository,并使用SimpleJpaRepository作为其实现类
  2. SimpleJpaRepository的构造方法接收一个EntityManager,是spring在创建初期就注入了EntityManagerFactory,并生成了一个EntityManager并传入
  3. EntityManager创建Query对象
  4. Query对象getResultList()获取结果(获取一次就关闭了,所以需要及时保存结果)

文章字数:204,阅读全文大约需要1分钟

jpa提供了分页查询的接口,之前使用limit进行分页,但是某些数据库(SQLServer)不支持limit语句,为了兼容于是想到jpa自带的方法

查询

1
2
3
4
5
6
//排序方式,根据id升序
Sort sort = new Sort(Sort.Direction.ASC,"id");
//从第0页,每页数据10条,按照sort指定的方式排序
Pageable pageable= new PageRequest(0,10,sort );
//查询
Page<Model> modelPage = modelDao.findAll(pageable);

结果使用

1
2
3
4
5
6
7
8
9
10
11
12
getTotalPages()  总共有多少页
getTotalElements() 总共有多少条数据
getNumber() 获取当前页码
getSize() 每页指定有多少元素
getNumberOfElements() 当前页实际有多少元素
hasContent() 当前页是否有数据
getContent() 获取当前页中所有数据(List<T>)
getSort() 获取分页查询排序规则
isFirst() 当前页是否是第一页
isLast() 当前页是否是最后一页
hasPrevious() 是否有上一页
hasNext() 是否有下一页

文章字数:691,阅读全文大约需要2分钟

实体间关联有@OneToOne@OneToMany or @ManyToOneManyToMany三种关系。其中,联机操作时只能One端操作Many端,代码层面则是cascade = CascadeType.ALL只能写在One端。One端删除,修改可以影响到Many端。反之则不允许。

一、@OneToOne关系映射

一个人有一个地址,就是一对一关系。其中people表的地址id对应地址表的id

1.1 通过外键关联一对一关系

people(id,name,addressId
address(id,phone,city,address)

People.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
@Getter
@Setter
public class People{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

@Column(name = "name", nullable = true, length = 20)
private String name;

//People是关系的维护端,当删除 people,会级联删除 address。默认不联级。
@OneToOne(cascade=CascadeType.ALL)
//加入表中的字段(name)addressId,参考对应Address的id(referencedColumnName )
@JoinColumn(name = "addressId", referencedColumnName = "id")
private Address address;
}

Address.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "phone", nullable = true, length = 11)
private String phone;
@Column(name = "city", nullable = true, length = 6)
private String city;
@Column(name = "address", nullable = true, length = 100)
private String address;
}

1.2 通过关联表保持
People(id,name)
Address(id,address)
PeopleAddr(PeopleId,addrId)关联表

People.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class People {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

@Column(name = "name", nullable = true, length = 20)
private String name;

@OneToOne(cascade=CascadeType.ALL)//People是关系的维护端
@JoinTable(name = "PeopleAddr",//关系表名
joinColumns = @JoinColumn(name="PeopleId"),//对应当前主键的
inverseJoinColumns = @JoinColumn(name = "addressId"))//对应关联表的id的字段
private Address address;//关联表-地址实体
}

二、@OneToMany 和 @ManyToOne

一个班级有很多同学(一对多),班级被删除班上的同学的数据也没了(联级删除,默认没有)。删除同学的数据不会影响到班级(many不能影响one)

myClass.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Author {
@Id // 主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
private Long id; //id

@NotEmpty(message = "姓名不能为空")
@Column(nullable = false, length = 20)
private String name;

//指定Article的author字段关联id,mappedBy还代表放弃维护关系。即当自身修改删除时不会触发关联对象的相应操作。对应的,由对方维护。即文章修改了,作者对象下的文章列表也会相应修改。
//fetch=FetchType.LAZY 懒加载,不会马上加载。对于马上要用的可以设置马上加载
//mappendBy和JoinColumn相比一个放弃维护,另一个是建立联系(一端放弃,另一端建立)
@OneToMany(mappedBy = "author",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
private List<Article> articleList;//文章列表
}

Article.java

1
2
3
4
5
6
@Entity
public class Article {
@ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)//可选属性optional=false,表示author不能为空。删除文章,不影响用户
@JoinColumn(name="author_id")//设置在article表中的关联字段(外键)
private Author author;//所属作者
}

三、@ManyToMany

  • 由其中一端维护关系
  • 被维护的一端无法直接删除,需要解除关系后才能删除。或者直接删除维护的一端,关系自动解除。
  • User.getAuthority().remove(authority)解除关系
  • User.setAuthorities(authorities)绑定关系
  • 多对多一般不设置联级操作

用户和权限组的例子就是多对多
User.java

1
2
3
4
@ManyToMany
@JoinTable(name = "user_authority",joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "authority_id"))
private List<Authority> authorityList;
  • 不使用@JoinTable注解,默认生成的关联表名称为主表表名+下划线+从表表名user_authority
  • 关联到主表的外键名:主表名+下划线+主表中的主键列名user_id
  • 关联到从表的外键名:主表中用于关联的属性名+下划线+从表的主键列名authority_id
  • 主表就是关系维护端对应的表,从表就是关系被维护端对应的表

Authority.java

1
2
@ManyToMany(mappedBy = "authorityList")
private List<User> userList;

文章字数:107,阅读全文大约需要1分钟

作用

用于数据库属性和java实体类直接转换,例如枚举,在数据库中存储或者读取都需要转换,此方法就是自动转换的方法。

使用

  1. 实现一个AttributeConverter<X, Y>接口的类
1
2
3
4
5
6
7
8
9
10
11
12

public interface AttributeConverter<X,Y> {
/**
* 转换需要存入数据库的数据
*/
public Y convertToDatabaseColumn (X attribute);

/**
* 从数据库读出,写入实体类的数据
*/
public X convertToEntityAttribute (Y dbData);
}
  1. jpa实体类字段上使用
    1
    2
    3

    @Convert(converter = XxxConverter.class)
    private myEnum test;

文章字数:249,阅读全文大约需要1分钟

搭建一个基础的jpa

依赖

pom.xml中加入依赖

1
2
3
4
5
6
7
8
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

model层

主要作用是和数据库映射的实体类

1
2
3
4
5
6
7
8
9
10
11
//用lombok或者自己写get和set
@Getter
@Setter
@Entity //标注给spring
public class User{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private String id;//id,自增
private String name;//其它属性自动关联

}

Dao

数据库操作,一个model对应一个Dao

1
2
3
4
//<实体类model,id类型>
public interface UserDao extends JpaRepository<User, String> {

}

继承之后基本的增删改查功能都具备了

配置

application.properties

1
2
3
4
5
6
7
8
9
10
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 每次运行创建新的表
spring.jpa.properties.hibernate.hbm2ddl.auto=create
# validate 每次运行时验证表结构
# create 每次新建
# create-drop 加载创建,退出删除
# update 追加更新