最近工作中由于要求只能用hibernate+jpa 与数据库进行交互,在简单查询中,jpa继承CrudRepository接口 ,然后利用jpa的方法命名规范进行jpql查询,然而在进行复杂查询时,需要继承JpaSpecificationExecutor接口 利用Specification 进行复杂查询,由于我自己就遇到了这一问题,查了好多资料,虽然有方法,但是都没有一个详细的讲解,以至于知道方法而不能很好的利用jpa复杂查询的方便之处。我将举几个栗子,来详细的说一下我自己在使用jpa多表复杂查询的场景和想法。
例子1:
以一个实体类User中的几个属性进行筛选。
1. 名字
2. ID
3. 手机号
这是一个单表的多条件复杂查询,由于是在几个属性中进行筛选,其中的属性的个数不知道有多少个,所以只需要利用Specification 查询就可以很方便的实现这个需求。 下面请看代码:
场景:页面上通过条件筛选,查询用户列表
这里有3个条件 在页面上我设置的id分别为searchName,searchId,searchMobile。 由于这个是user表 所以userRepository 继承JpaSpecificationExecutor接口,随后我创建了一个封装条件的类
public class PageParam<T> {
private Integer pageSize = 10;
private Integer pageNumber = 1;
private String searchName;
private String searchMobile;
private String searchId;
}
由于我这个方法是直接分页的 所以pageNumber 和pageSize 也可以直接写入到这个类中,用于方便接收参数,主要是对下面3个参数的封装
Specification<T> specification = new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<Predicate>();
if (StringUtils.isNotBlank(searchName)) {
list.add(cb.like(root.get("name").as(String.class), "%" + searchName + "%"));
}
if (StringUtils.isNotBlank(searchId)) {
list.add(cb.equal(root.get("id").as(Long.class), searchId));
}
if (StringUtils.isNotBlank(searchMobile)) {
list.add(cb.like(root.get("mobile").as(String.class), "%" + searchMobile + "%"));
}
Predicate[] p = new Predicate[list.size()];
return cb.and(list.toArray(p));
};
};
这里因为都是一个表,所以只要root.get(‘N ‘)这个N对应所要查的 属性的名字就好,属性名 属性名 重要的事情说三遍。
再接下来看一组多表的查询
例子2:
这里有4张表
public class Living {
Long id;
@ManyToOne
@JsonIgnore
@JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
public Actor actor;
@ManyToOne
@JsonIgnore
@JoinColumn(name = "regionId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
public Region region;
}
public class Actor {
Long id;
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@JoinColumn(name = "actorId")
@org.hibernate.annotations.ForeignKey(name = "none")
List<Living> livings = new ArrayList<>();
@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@org.hibernate.annotations.ForeignKey(name = "none")
@JoinColumn(name = "userDetailId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
UserDetail userDetail;
@Column(nullable = false)
@Enumerated(value = EnumType.ORDINAL)
ActorType actorType = ActorType.A;
public enum ActorType{
A,B,C
}
}
public class UserDetail {
Long id;
@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@org.hibernate.annotations.ForeignKey(name = "none")
@JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
Actor actor;
String truename;
}
public class Region {
Long id;
String name;
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@JoinColumn(name = "regionId")
@org.hibernate.annotations.ForeignKey(name = "none")
List<Living> Livings;
}