上篇文章我们分享了下Spring-data-jpa的基本使用方法,但在实际使用中可能会有更复杂的用法,现在我们来看看他的更高级玩法,并做了完美的封装,使用起来更加智能简单。Spring-data-jpa为我们提供了JpaSpecificationExecutor接口,只要简单实现toPredicate方法就可以实现复杂的查询,下面就来看看具体如何使用。
首先我们来看看JpaSpecificationExecutor这个类
public interface JpaSpecificationExecutor<T> {
T findOne(Specification<T> var1);
List<T> findAll(Specification<T> var1);
Page<T> findAll(Specification<T> var1, Pageable var2);
List<T> findAll(Specification<T> var1, Sort var2);
long count(Specification<T> var1);
}
可以看出他是个接口,里面提供了基本的使用方法,其中重要的一个参数是Specification<T>我们再来看看这个这个参数
public interface Specification<T> {
Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}
发现他也是一个接口,并且只有一个方法,因此这个方法就是我们查询的关键所在,实际他就是向我们提供的构建查询条件的关键接口,我们只要实现他的方法按照JPA 2.0 criteria api写好查询条件就可以了,接下来看看具体如何来怎么使用
由上一篇我么知道使用Spring-data-jpa我们需要继承JpaRepository接口,为了做复杂查询还需继承JpaSpecificationExecutor接口,因此我们可以建一个BaseRepository类来同时继承这两个类
/**
* 基类的数据访问接口(继承了CrudRepository,PagingAndSortingRepository,
* JpaSpecificationExecutor的特性)
*
* Created by mj on 2017/12/17.
*/
@NoRepositoryBean
public abstract interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T,ID>, JpaSpecificationExecutor<T> {
/**
*
* 使用QBL进行查询列表
*
* @author mj 2016年10月26日
* @param query
* @return
*/
public abstract List<T> findAll(BaseQuery query);
/**
*
* 封装分页查询
*
* @author mj 2016年10月26日
* @param query
* @param pageable
* @return
*/
public abstract Page<T> findAll(BaseQuery query, Pageable pageable);
/**
*
* 封装排序查询
*
* @author mj 2016年10月26日
* @param query
* @param sort
* @return
*/
public abstract List<T> findAll(BaseQuery query, Sort sort);
/**
*
* 使用QBL定位记录
*
* @author mj 2016年10月26日
* @param query
* @return
*/
public abstract T findOne(BaseQuery query);
/**
*
* 更新方法
*
* @author mj 2016年10月26日
* @param t
* @param updateFileds
* @param where
* @return
*/
public abstract int update(T t, BaseQuery where, String... updateFileds);
/**
*
* 根据唯一主键更新方法
*
* @author mj 2016年10月26日
* @param t
* @param id
* @param updateFileds
* @return
*/
public abstract int updateById(T t, ID id, String... updateFileds);
}
其中里面的一些方法暂时不用关心是对jpa的一些功能扩展加强后面会讲到,然后我们来构建查询条件,通过以上讲述我们只需创建 一个类来实现Specification<T>接口即可
/**
* 自定义Query语言转Specification
*
* @version
* @author mj 2016年10月26日 下午3:46:50
* @param
*
*/
public class QueryToSpecification implements Specification {
private BaseQuery query;
public QueryToSpecification(BaseQuery query) {
super();
this.query = query;
}
/**
* 【请在此输入描述文字】
*
* (non-Javadoc)
* @see Specification#toPredicate(Root, CriteriaQuery, CriteriaBuilder)
*/
@Override
public Predicate toPredicate(Root root, CriteriaQuery cquery, CriteriaBuilder cb) {
return BaseQueryPredicateBuilder.getPredicate(root, cb, cquery,this.query);
}
}
由此可看出我们实现了Specification<T>接口的toPredicate方法,里面的参数Root就是我们的实体类,CriteriaQuery为基本查询,CriteriaBuilder为查询条件构建,其中BaseQueryPredicateBuilder.getPredicate(root, cb, cquery,this.query);为具体的构建条件实现,我们继续来构建这个方法
/**
* query转换builder类
*
*