目录
Jpa
元模型
Criteria查询
CriteriaBuilder 安全查询创建工厂
CriteriaQuery 安全查询主语句
Root
Predicate 过滤条件
Predicate 多个过滤条件
jpa
Spring data JPA简介
Spring data JPA是Spring在ORM框架,以及JPA规范的基础上,封装的一套JPA应用框架,并提供了一整套的数据访问层解决方案。
Spring data JPA提供给用户使用的,主要有以下几个接口:
Repository:仅仅是一个标识,表明任何继承它的均为仓库接口类,方便Spring自动扫描识别
CrudRepository:继承Repository,实现了一组CRUD相关的方法
PagingAndSortingRepository:继承CrudRepository,实现了一组分页排序相关的方法
JpaRepository:继承PagingAndSortingRepository,实现一组JPA规范相关的方法
JpaSpecificationExecutor:比较特殊,不属于Repository体系,实现一组JPA Criteria查询相关的方法
JpaSpecificationExecutor接口
该接口提供了对JPA Criteria查询的支持。注意,这个接口很特殊,不属于Repository体系,而Spring data JPA不会自动扫描识别,所以会报找不到对应的Bean,我们只需要继承任意一个继承了Repository的子接口或直接继承Repository接口,Spring data JPA就会自动扫描识别,进行统一的管理。
Criteria 查询:是一种类型安全和更面向对象的查询
这个接口基本是围绕着Specification接口来定义的, Specification接口中只定义了如下一个方法:
Predicate toPredicate(Root<T> root,CriteriaQuery<?> query, CriteriaBuilder cb);
要理解这个方法,以及正确的使用它,就需要对JPA2.0的Criteria查询有一个足够的熟悉和理解,因为这个方法的参数和返回值都是JPA标准里面定义的对象。
Criteria查询基本概念
Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。
CriteriaQuery接口:代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by等
注意:CriteriaQuery对象只对实体类型或嵌入式类型的Criteria查询起作用
Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似:
1:Root实例是类型化的,且定义了查询的FROM子句中能够出现的类型。
2:查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。
3:Criteria查询,可以有多个查询根。
4:AbstractQuery是CriteriaQuery 接口的父类,它提供得到查询根的方法。
CriteriaBuilder接口:用来构建CritiaQuery的构建器对象
Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合。
Criteria查询
基本对象的构建
1:通过EntityManager的getCriteriaBuilder或EntityManagerFactory的getCriteriaBuilder方法可以得到CriteriaBuilder对象
2:通过调用CriteriaBuilder的createQuery或createTupleQuery方法可以获得CriteriaQuery的实例
3:通过调用CriteriaQuery的from方法可以获得Root实例
过滤条件
1:过滤条件会被应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。
2:这些条件使用 CriteriaQuery.where 方法应用到CriteriaQuery 对象上
3:CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建Predicate对象。
4:复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。
构建简单的Predicate示例:
Predicate p1=cb.like(root.get(“name”).as(String.class),“%”+uqm.getName()+“%”);
Predicate p2=cb.equal(root.get("uuid").as(Integer.class), uqm.getUuid());
Predicate p3=cb.gt(root.get("age").as(Integer.class), uqm.getAge());
构建组合的Predicate示例:
Predicate p = cb.and(p3,cb.or(p1,p2));
当然也可以形如前面动态拼接查询语句的方式,比如:
1. Specification<UserModel> spec = new Specification<UserModel>() {
2. public Predicate toPredicate(Root<UserModel> root,
3. CriteriaQuery<?> query, CriteriaBuilder cb) {
4. List<Predicate> list = new ArrayList<Predicate>();
5.
6. if(um.getName()!=null && um.getName().trim().length()>0){
7. list.add(cb.like(root.get("name").as(String.class), "%"+um.getName()+"%"));
8. }
9. if(um.getUuid()>0){
10. list.add(cb.equal(root.get("uuid").as(Integer.class), um.getUuid()));
11. }
12. Predicate[] p = new Predicate[list.size()];
13. return cb.and(list.toArray(p));
14. }
15. };
也可以使用CriteriaQuery来得到最后的Predicate,示例如下:
1. Specification<UserModel> spec = new Specification<UserModel>() {
2. public Predicate toPredicate(Root<UserModel> root,
3. CriteriaQuery<?> query, CriteriaBuilder cb) {
4. Predicate p1 = cb.like(root.get("name").as(String.class), "%"+um.getName()+"%");
5. Predicate p2 = cb.equal(root.get("uuid").as(Integer.class), um.getUuid());
6. Predicate p3 = cb.gt(root.get("age").as(Integer.class), um.getAge());
7. //把Predicate应用到CriteriaQuery中去,因为还可以给CriteriaQuery添加其他的功能,比如排序、分组啥的
8. query.where(cb.and(p3,cb.or(p1,p2)));
9. //添加排序的功能
10. query.orderBy(cb.desc(root.get("uuid").as(Integer.class)));
11.
12. return query.getRestriction();
13. }
14. };
多表联接
n多表连接查询稍微麻烦一些,下面演示一下常见的1:M,顺带演示一下1:1
n使用Criteria查询实现1对多的查询
1:首先要添加一个实体对象DepModel,并设置好UserModel和它的1对多关系,如下:
@Entity
@Table(name="tbl_user")
public class UserModel {
@Id
private Integer uuid;
private String name;
private Integer age;
@OneToMany(mappedBy = "um", fetch= FetchType. LAZY, cascade = {
CascadeType. ALL})
private Set<DepModel> setDep;
//省略getter/setter
}
@Entity
@Table(name="tbl_dep")
public class DepModel {
@Id
private Integer uuid;
private String name;
@ManyToOne()
@JoinColumn(name ="user_id", nullable = false)
//表示在tbl_dep里面有user_id的字段
private UserModel um = new UserModel();
//省略getter/setter
}
2:配置好Model及其关系后,就可以在构建Specification的时候使用了,示例如下:
Specification<UserModel> spec = newSpecification<UserModel>() {
public PredicatetoPredicate(Root<UserModel> root, CriteriaQuery<?> query,CriteriaBuilder cb) {
Predicate p1 =cb.like(root.get("name").as(String.class),"%"+um.getName()+"%");
Predicate p2 =cb.equal(root.get("uuid").as(Integer.class), um.getUuid());
Predicate p3 =cb.gt(root.get("age").as(Integer.class), um.getAge());
SetJoin<UserModel,DepModel> depJoin =root.join(root.getModel().getSet("setDep",DepModel.class) ,JoinType.LEFT);
Predicate p4 =cb.equal(depJoin.get("name").as(String.class), "ddd");
//把Predicate应用到CriteriaQuery去,因为还可以给CriteriaQuery添加其他的功能,比如排序、分组啥 的
query.where(cb.and(cb.and(p3,cb.or(p1,p2)),p4));
//添加分组的功能
query.orderBy(cb.desc(root.get("uuid").as(Integer.class)));
return query.getRestriction();
}};
接下来看看使用Criteria查询实现1:1的查询
1:在UserModel中去掉setDep的属性及其配置,然后添加如下的属性和配置:
@OneToOne()
@JoinColumn(name = "depUuid")
private DepModel dep;
public DepModel getDep() {
return dep;}
public void setDep(DepModel dep) {
this.dep= dep; }
2:在DepModel中um属性上的注解配置去掉,换成如下的配置:
@OneToOne(mappedBy = "dep", fetch= FetchType. EAGER, cascade = {
CascadeType. ALL})
3:在Specification实现中,把SetJoin的那句换成如下的语句:
Join<UserModel,DepModel> depJoin =
root.join(root.getModel().getSingularAttribute("dep",DepModel.class),JoinType.LEFT);
//root.join(“dep”,JoinType.LEFT); //这句话和上面一句的功能一样,更简单
编写接口如下:
1. public interface SpecificationExecutonExeorRepository extends CrudRepository<User, Integer>, JpaSpecificaticutor<User> {
}
Service类:
1. @Service
2. public class SpecificationExecutorRepositoryManager {
3. @Autowired
4. private SpecificationExecutorRepository dao;
5. /**
6. * 描述:根据name来查询用户
7. */
8. public User findUserByName(final String name){
9. return dao.findOne(new Specification<User>() {
10.
11. @Override
12. public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query,
13. CriteriaBuilder cb) {
14. Predicate predicate = cb.equal(root.get("name"), name);
15. return predicate;
16. }
17. });
18. }
19.
20. /**
21. * 描述:根据name和email来查询用户
22. */
23. public User findUserByNameAndEmail(final String name, final String email){
24. return dao.findOne(new Specification<User>() {
25.
26. @Override
27. public Predicate toPredicate(Root<User> root,
28. CriteriaQuery<?> query, CriteriaBuilder cb) {
29. List<Predicate> list = new ArrayList<Predicate>();
30. Predicate predicate1 = cb.equal(root.get("name"), name);
31. Predicate predicate2 = cb.equal(root.get("email"), email);
32. list.add(predicate1);
33. list.add(predicate2);
34. // 注意此处的处理
35. Predicate[] p = new Predicate[list.size()];
36. return cb.and(list.toArray(p));
37. }
38. });
39. }
40.
41. /**
42. * 描述:组合查询
43. */
44. public User findUserByUser(final User userVo){
45. return dao.findOne(new Specification<User>() {
46.
47. @Override
48. public Predicate toPredicate(Root<User> root,
49. CriteriaQuery<?> query, CriteriaBuilder cb) {
50. Predicate predicate = cb.equal(root.get("name"), userVo.getName());
51. cb.and(predicate, cb.equal(root.get("email"), userVo.getEmail()));
52. cb.and(predicate, cb.equal(root.get("password"), userVo.getPassword()));
53. return predicate;
54. }
55. });
56. }
57.
58. /**
59. * 描述:范围查询in方法,例如查询用户id在[2,10]中的用户
60. */
61. public List<User> findUserByIds(final List<Integer> ids){
62. return dao.findAll(new Specification<User>() {
63.
64. @Override
65. public Predicate toPredicate(Root<User> root,
66. CriteriaQuery<?> query, CriteriaBuilder cb) {
67. return root.in(ids);
68. }
69. });
70. }
71.
72. /**
73. * 描述:范围查询gt方法,例如查询用户id大于9的所有用户
74. */
75. public List<User> findUserByGtId(final int id){
76. return dao.findAll(new Specification<User>() {
77.
78. @Override
79. public Predicate toPredicate(Root<User> root,
80. CriteriaQuery<?> query, CriteriaBuilder cb) {
81. return cb.gt(root.get("id").as(Integer.class), id);
82. }
83. });
84. }
85.
86. /**
87. * 描述:范围查询lt方法,例如查询用户id小于10的用户
88. */
89. public List<User> findUserByLtId(final int id){
90. return dao.findAll(new Specification<User>() {
91.
92. @Override
93. public Predicate toPredicate(Root<User> root,
94. CriteriaQuery<?> query, CriteriaBuilder cb) {
95. return cb.lt(root.get("id").as(Integer.class), id);
96. }
97. });
98. }
99.
100. /**
101. * 描述:范围查询between方法,例如查询id在3和10之间的用户
102. */
103. public List<User> findUserBetweenId(final int start, final int end){
104. return dao.findAll(new Specification<User>() {
105.
106. @Override
107. public Predicate toPredicate(Root<User> root,
108. CriteriaQuery<?> query, CriteriaBuilder cb) {
109. return cb.between(root.get("id").as(Integer.class), start, end);
110. }
111. });
112. }
113.
114. /**
115. * 描述:排序和分页操作
116. */
117. public Page<User> findUserAndOrder(final int id){
118. Sort sort = new Sort(Direction.DESC, "id");
119. return dao.findAll(new Specification<User>() {
120.
121. @Override
122. public Predicate toPredicate(Root<User> root,
123. CriteriaQuery<?> query, CriteriaBuilder cb) {
124. return cb.gt(root.get("id").as(Integer.class), id);
125. }
126. }, new PageRequest(0, 5, sort));
127. }
128.
129. /**
130. * 描述:只有排序操作
131. */
132. public List<User> findUserAndOrderSecondMethod(final int id){
133. return dao.findAll(new Specification<User>() {
134.
135. @Override
136. public Predicate toPredicate(Root<User> root,
137. CriteriaQuery<?> query, CriteriaBuilder cb) {
138. cb.gt(root.get("id").as(Integer.class), id);
139. query.orderBy(cb.desc(root.get("id").as(Integer.class)));
140. return query.getRestriction();
141. }
142. });
143. }
144. }
测试类:
1. @RunWith