spring data jpa为我们提供了JpaSpecificationExecutor接口,只要简单实现toPredicate方法就可以实现复杂的查询。JpaSpecification查询的关键在于怎么构建Predicates。
下面通过示例对其进行学习。
由运动员表(player)和助手表(assistant)表,它们的关系未一对多,即一个运动员可以有多个助手,一个助手只能服务一个运动员。
对应实体类如下:
PlayerEntity:
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "player")
public class PlayerEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 国籍
private String country;
// 运动员姓名
private String name;
// 运动种类
private String profession;
// 获得金牌次数
private int goldenNum;
// 是否决定参加奥运会
private boolean attend;
@OneToMany(targetEntity = AssistantEntity.class)
@org.hibernate.annotations.ForeignKey(name = "none")
@JoinColumn(name = "player_assistant", referencedColumnName = "name")
private List<AssistantEntity> list;
}
AssistantEntity:
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "assistant")
public class AssistantEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 助理名称
private String name;
// 助理年龄
private int age;
}
数据库已有数据情况如下:
① 使用and和or的简单复合查询
我们此处查询player,故有PlayerRepo
PlayerRepo:
@Repository
public interface PlayerRepo extends JpaRepository<PlayerEntity, Long>, JpaSpecificationExecutor {
}
Controller代码(按照代码规范,不应在Controller层写业务逻辑代码,此处为了方便展示,将业务逻辑代码写在Controller方法中)
@GetMapping("/jpatest7")
public List<PlayerEntity> jpatest7() {
Specification spec = (Specification) (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
Predicate countryPredicate = criteriaBuilder.equal(root.get("country"), "中国");
Predicate professionPredicate = criteriaBuilder.equal(root.get("profession"), "篮球");
Predicate goldenPredicate = criteriaBuilder.equal(root.get("goldenNum"), 5);
predicates.add(countryPredicate);
predicates.add(professionPredicate);
predicates.add(goldenPredicate);
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
};
List<PlayerEntity> list = playerRepo.findAll(spec);
return list;
}
记得注入playerRepo。
root.get("country"),对应着实体类的相关属性country,root.get属性对应的实体类,我认为从使用该spec的Repository对应的实体类相对应,比如此处的playerRepo对应的实体类PlayerEntity,实际上如果属性对不上,运行时会报错。root.get取得相应实体的操作字段。
而criteriaBuilder.equal和criteriaBuilder.and,则是用来构建复杂查询
此处示例对应的sql语句为:
select * from player where country = "中国" and profession = "篮球" and golden_num = 5;
Specification查询中用实体类属性,sql语句中用数据库字段。
访问接口结果如下:
修改
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
为
return criteriaBuilder.or(predicates.toArray(new Predicate[predicates.size()]));
此时对应的sql语句为select * from player where country = "中国" or profession = "篮球" or golden_num = 5;
表中所有数据都复合条件,都查出来,访问结果为: