Spring-Data-JPA中使用Specification实现动态查询

      最近项目技术选型db框架选择了使用JPA,刚开始时,使用jpa进行一些单表简单的查询非常轻松,大家写的不亦乐乎大笑,后来在遇到多条件动态查询的业务场景时,发现现有的JpaRepository提供的方法和自己写@Query已经满足了不了需求,难不成要对所有的条件和字段进行判断,再写很多个dao方法惊恐?后面查到jpa提供了围绕Specification这个类的一系列类,来用于实现动态查询。

       首先,毫无疑问需要定义一个dao接口,这个接口除了继承JpaRepository之外,还需要继承JpaSpecificationExecutor。

public interface FileInfoDao extends JpaRepository<FileInfo, Long>, JpaSpecificationExecutor<FileInfo>{
}

       这是我用于查询文件信息的dao,我们可以看到JpaSpecificationExecutor有以下这些方法:

       T findOne(Specification<T> spec);
	List<T> findAll(Specification<T> spec);
	Page<T> findAll(Specification<T> spec, Pageable pageable);
	List<T> findAll(Specification<T> spec, Sort sort);
	long count(Specification<T> spec);

findOne(spec):根据条件查询获取一条数据;

findAll(spec) :根据条件查询获取全部数据;

findAll(specification, pageable) : 根据条件分页查询数据,pageable设定页码、一页数据量,同时返回的是Page类对象,可以通过getContent()方法拿到List集合数据;

findAll(specification, sort) : 根据条件查询并返回排序后的数据;

count(spec) : 获取满足当前条件查询的数据总数;

       现在光定义了dao没用,人家就是需要你往方法里传Specification的对象,Specification是一个接口,肯定没法直接获取对象。

public interface Specification<T> {
	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

        所以我自定义了一个类实现类这个接口。

public class SinoSpecification<T> implements Specification<T> {
	
	private TableQueryParam param;
	
	private Class<T> clazz;
	
	public SinoSpecification(TableQueryParam param, Class<T> clazz) {
		this.param = param;
		this.clazz = clazz;
	}
	
	@Override
	public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
		Field[] fields =  clazz.getDeclaredFields();
		Map<String, Object> conditions = param.getCondition();
		List<Predicate> predicates = new ArrayList<>();
		for (int i = 0; i < fields.length; i++) {
			fields[i].setAccessible(true);
			String name = fields[i].getName();
			if(conditions.containsKey(name)) {
				if(ObjectUtils.isEmpty(conditions.get(name))) {
					continue;
				}
				predicates.add(cb.like(root.get(name), "%"+conditions.get(name)+"%"));
			}
		}
		return cb.and(predicates.toArray(new Predicate[predicates.size()]));
	}

}
        TableQueryParam是我自定义的用于封装查询条件的类,这个类有一个condition属性是条件集合。在这个类中,我是通过反射获取实体类的字段属性,再去和TableQueryParam中的conditon里的条件进行比对,如果条件中包含,则将这个条件以及查询的值一起构造加入进Predicate,cb.like(arg1, arg2)这个方法,就代表模糊查询,sql中的Like,arg1参数代表要查询的字段,arg2代表查询的字段的值,转换成sql就是 arg1 LIKE arg2。当然,我这个自定义的Specification主要是用于模糊查询的,而它不仅仅支持模糊,还可以通过CriteriaBuilder里的方法来实现各种组合查询,例如cb.equal()进行等于查询,cb.greaterThan()、cb.lessThan()来实现大于小于,等等,可以说非常丰富。

        定义好Specification之后,就可以调用前面说的findAll方法,将自定义的specification对象放进去即可。

猜你喜欢

转载自blog.csdn.net/wetsion/article/details/79920227