818 spring data jpa 一些接口与方法

时间:2021-01-18 16:23:12


spring jpa 主要分为三个类:
org.springframework.data.jpa.repository.JpaRepository<T, ID>
org.springframework.data.jpa.repository.JpaSpecificationExecutor<T>
org.springframework.data.repository.CrudRepository<T, ID>
这三个类的实现类都是
org.springframework.data.jpa.repository.support.SimpleJpaRepository<T, ID>(这个类挺牛逼的)
其中
JpaRepository主要是findall findpage findone这样的方法
JpaSpecificationExecutor主要是findall(spec) findpage(spec) findone(spec)这样的方法
spec-->查询条件,需要接口org.springframework.data.jpa.domain.Specification<T>这个接口
并且实现
org.springframework.data.jpa.domain.Specification.toPredicate(Root<T>, CriteriaQuery<?>, CriteriaBuilder)
这个接口中有三个参数:
Root
CriteriaQuery
CriteriaBuilder
其中 Root是查询结果的一个实体对象,也就是查询结果返回的主要对象,其中一对多OR多对一就是从这个对象开始计算的,具体层级关系
javax.persistence.TupleElement<X>
javax.persistence.criteria.Selection<X>
javax.persistence.criteria.Expression<T>
javax.persistence.criteria.Path<X>
javax.persistence.criteria.From<Z, X>
javax.persistence.criteria.Root<X>
这几个接口主要用于描述一个数据库对象与实体对象的对应关系,就不多说了。
然后是javax.persistence.criteria.CriteriaQuery<T>,这个是JPA标准,主要是构建查询条件的,里面的方法都是各种查询方式:distinct、select、where、groupby、having、orderby这些方法,想必大家都知道这些是组件SQL语句的基本方法。
接下来是javax.persistence.criteria.CriteriaBuilder,这个接口主要是用来进行一些函数操作,不过我们这里先关注JPA标准中Hibernate的两个实现方法:
1.and org.hibernate.ejb.criteria.CriteriaBuilderImpl.and(Predicate...)
2.or org.hibernate.ejb.criteria.CriteriaBuilderImpl.or(Predicate...)
这两个方法都有一个关键的接口:Predicate(javax.persistence.criteria.Predicate);
这个接口,同为Expression(javax.persistence.criteria.Expression<Boolean>)的子接口,可以肯定也是字段相关的表达式,在实际操作中,这个接口也是作为关联各种Predicate的核心操作接口,and方法是将各个条件作为and来拼接,进行查询;or是将各条件作为or来拼接,进行查询。
然后说如何生成一个Predicate接口,生成的核心类还是上面的CriteriaBuilder(javax.persistence.criteria.CriteriaBuilder),在这个接口中,很多方法都是返回Predicate接口的,其中包含between、gt(大于)、lt(小于)、not(非)等等操作,将所需要的这些查询条件都放在一个array数组(或者采用java.util.ArrayList.toArray(T[])-->cb.and(predicateList.toArray(new Predicate[predicates.size()]))),然后调用上一步说的and或者or方法进行拼接,当然,如果一个查询既有and又有or,用这种方式还没有具体尝试过,有待考证。
说了这么多,先总结一下
Specification的toPredicate这个方法声明了
Root:查询哪个表
CriteriaQuery:查询哪些字段,排序是什么
CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
Predicate(Expression):单独每一条查询条件的详细描述
这些,就是有条件查询的使用方式,springJPA和hibernate的完美结合(如果只用springJPA,根本了解不到这里)
来个简单的例子吧:
/** 查询名字包含icarus,或者手机号包含188的用户(如果return中的or改为and,就是查询名字包含icarus,并且手机号包含188的用户) */
// query = cb.createQuery(User.class);// query是这么出来的
List<Predicate> predicateList = new ArrayList<>();
root = query.from(User.class);
Path<String> nameExp = root.get("name");
Predicate p1 = cb.like(nameExp, "%icarus%");
Path<String> phoneExp = root.get("phone");
Predicate p2 = cb.like(phoneExp, "%188%");
predicateList.add(p1);
predicateList.add(p2);
return cb.or(predicateList.toArray(new Predicate[predicates.size()]));
如果想提炼抽象类,则需要将返回predicateList这个方法给抽象化,不过这时候就无法提供OR和AND双重选择了。
当然,通过其它参数判断也可以,不过优势也不大。
如果想通过判断,推荐Predicate这个类里面的枚举值AND和OR
在CriteriaBuilder中,还有asc、desc两个方法,返回Order(此处order在CriteriaQuery中使用,和下面PageRequest说的order、sort不同);还有sum、count、avg、max、min这几个方法,聚合函数理论上是返回值中才会出现的,这里出现,暂且还真不知道什么用处,不过肯定有用,还望高人指点一下。
以上,是关于springJPA在进行条件查询时使用的方式。不能说简单了,因为方法并没有直接写SQL的优势,好的是跨平台,相信hibernate的夸数据库执行是很给力的
P.S. 这种设计模式在以前做一个日本的项目时遇见过,每一个查询条件都采用一个方法累加的手段,好的一方面是方法式增加查询条件、不用管实现方式、可使用于不同的数据库,针对专注java开发的程序员比较友好一些;坏处也很明显,繁琐、效率低,这些毋庸置疑,毕竟从HQL到SQL还需要很长的转换。
还没完,继续
刚才只是说了条件查询中的Specification,包含的内容相当多,然后再说下查询结果相关的内容。
findall和findall(spec)这两个方法比较简单,返回对象是List<T>
其中T在findall中指定的是JpaRepository中的第一个泛型对象,
在findall(spec)中指定的是JpaSpecificationExecutor中的第一个泛型对象,
也就是与数据库对应的实体类一样(spec还应该和root对应,因为Specification也需要泛型,应该在写Specification的时候就指定了)
findone和findone(spec)返回对象是T,T也同findall
额外在说一下findall(sort)和findall(pageable),其返回结果是org.springframework.data.domain.Page<T>
P.S.(因为findall(spec,sort)和findall(spec,pageable)和这两个区别不是很大,就不在额外赘述了,自行理解吧)
在这里又出来两个参数:sort和pageable
sort:实质就是一组order(此order不是上面CriteriaBuilder中通过asc和desc生成的order接口,而是sort的一个内部公开类(理解成一个普通类就行),是一个实现类),sort的生成可以使用一个order数组、order列表、属性数组、属性列表。采用order的好处是可以自定义排序方向(order由属性和方向组成),若采用属性,则默认使用ASC升序排列
说完了sort,我们再来说说pageable
pageable指的是org.springframework.data.domain.Pageable,这个接口有4个方法,
getPageNumber-->获取页码
getPageSize -->分页大小
getOffset -->偏移量
getSort -->获取排序信息
对于pageable的产生,也很容易,采用org.springframework.data.domain.PageRequest这个实现类,将构造方法生成出来即可,没有第二个实现类。将sort、pageNumber、getPageSize设置好即可
偏移量不允许设置,这个是在查询的时候,通过计算页码和分页大小进行自动设置的
对于方法返回值:Page,实现类org.springframework.data.domain.PageImpl<T>也很简单,仅仅是在List的基础上增加了总页码、总大小等一系列常用内容,看代码即可
以下,是spring官方对jpa的文档,英文能力不错的可以阅读原文。
 http://docs.spring.io/spring-data/jpa/docs/1.8.0.RELEASE/reference/html/ 
不过原文大多讲的是应用方向的