spring之spring data jpa(入门二)

Repository:

public interface Repository<T, ID extends Serializable> {
 
}

 这个接口只是一个空的接口,目的是为了统一所有Repository的类型,其接口类型使用了泛型,泛型参数中T代表实体类型,ID则是实体中id的类型。

 

 

CrudRepository:

持久层接口定义的比较多,且每个接口都需要声明相似的CURD的方法,直接继承Repository就比较繁琐.这时就可以继承CrudRepository,它会自动为域对象创建CRUD的方法,供业务层直接调用.

 

public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
 
    <S extends T> S save(S entity);
 
    <S extends T> Iterable<S> save(Iterable<S> entities);
 
    T findOne(ID id);
 
    boolean exists(ID id);
 
    Iterable<T> findAll();
 
    Iterable<T> findAll(Iterable<ID> ids);
 
    long count();
 
    void delete(ID id);
 
    void delete(T entity);
 
    void delete(Iterable<? extends T> entities);
 
    void deleteAll();
}

 该接口中的方法大多是访问数据库中常用的方法,如果需要,直接继承这个接口即可.但是CrudRepository也有副作用,它可能暴露不希望暴露给业务层的方法.这种情况下,只能退回继承Repository接口,在接口中显示声明需要暴露给业务层的方法.

 

 

PagingAndSortingRepository:

分页查询和排序是持久层常用的功能,Spring Data 为此提供了 PagingAndSortingRepository 接口,它继承自 CrudRepository 接口,在 CrudRepository 基础上新增了两个与分页有关的方法。

 

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
 
    Iterable<T> findAll(Sort sort);
 
    Page<T> findAll(Pageable pageable);
}

 但是,我们很少会将自定义的持久层接口直接继承自 PagingAndSortingRepository,而是在继承 Repository 或 CrudRepository 的基础上,在自己声明的方法参数列表最后增加一个 Pageable 或 Sort 类型的参数,用于指定分页或排序信息即可,这比直接使用 PagingAndSortingRepository 提供了更大的灵活性.

 

这些Repository都是spring-data-commons提供给我们的核心接口,spring-data-commons是Spring Data的核心包。这个接口中为我们提供了数据的分页方法,以及排序方法.

 

针对spring-data-jpa又提供了一系列repository接口,其中有JpaRepository和JpaSpecificationExecutor.

 

JpaRepository :

 

public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
 
    List<T> findAll();
 
    List<T> findAll(Sort sort);
 
    <S extends T> List<S> save(Iterable<S> entities);
    void flush();
 
    T saveAndFlush(T entity);
 
    void deleteInBatch(Iterable<T> entities);
 
    void deleteAllInBatch();
}

 这个类继承自PagingAndSortingRepository,看其中的方法,可以看出里面的方法都是一些简单的操作,并未涉及到复杂的逻辑。当你在处理一些简单的数据逻辑时,便可继承此接口.

 

其实在业务中往往会涉及到多张表的查询,以及查询时需要的各种条件,此时就需要复杂业务查询了.spring data jpa提供了JpaSpecificationExecutor.

 

JpaSpecificationExecutor:

public interface JpaSpecificationExecutor<T> {
 
    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);
}

 在这个接口里面出现次数最多的类就是Specification.class,而这个类主要也就是围绕Specification来打造 的,Specification.class是Spring Data JPA提供的一个查询规范,只需围绕这个规范来设置查询条件便可.

 

Specification接口:

 

public interface Specification<T> {
	/**
	 * </pre>
	 * JPA规范中的
	 * @param root  ROOT查询中的条件表达式
	 * @param query 条件查询设计器
	 * @param cb	条件查询构造器
	 * @return
	 * </pre>
	 */
	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

 在使用复杂对象查询时,实现该方法用JPA去构造对象查询便可.

 

Spring Data JPA 在后台为持久层接口创建代理对象时,会解析方法名字,并实现相应的功能。除了通过方法名字以外,它还可以通过如下两种方式指定查询语句:

 

  1. Spring Data JPA 可以访问 JPA 命名查询语句。只需要在定义命名查询语句时,为其指定一个符合给定格式的名字,Spring Data JPA 便会在创建代理对象时,使用该命名查询语句来实现其功能.
  2. 还可以直接在声明的方法上面使用 @Query 注解,并提供一个查询语句作为参数,Spring Data JPA 在创建代理对象时,便以提供的查询语句来实现其功能.

 三种创建查询的方式

1.通过解析方法名创建查询

框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。

 

直接在接口中定义查询方法,如果是符合规范的,可以不用写实现,目前支持的关键字写法如下:

 

KeyWord Sample JPQL snippet
​And findByLastnameAndFirstname where  x.lastname =?1  and  x.firstname =?2
Or findByLastnameOrFirstname where   x.lastname = ?1 or  x.firstname = ?2
Between findByStartDateBetween where  x.startData between 1? and 2?
LessThan findByAgeLessThan where  x.age < ?1
GreaterThan findByAgeGreaterThan where   x.age >?1
After findByStartDateAfter where   x.startDate > ?1
Before findByStartDateBefor where   x.startDate < ?1
IsNull findByAgeIsNull where   x.age is  null
IsNotNull,NotNull findByAge(Is)NotNull where   x.age is not null
Like findByFirstnameLike where   x.firstname like ?1
NotLike findByFirstnameNotLike where   x.firstname not like ?1
StartingWith findByFirstnameStartingWith where   x.firstname like ?1(如%?)
EndingWith findByFirstnameEndingWith where   x.firstname like ?1(如?%)
Containing findByFirstnameContaining where   x.fistaname like ?1(如%?%)
OrderBy findByFirstnameOrderByAgeDesc where   x.firstname  =?1 Order by x.age desc
Not findByLastnameNot where   x.lastname <>?1
In findByAgeIn(collegetion<Age> ages where   x.age in ?1
NotIn findByAgeNotIn(collegetion<Age> ages where   x.age  not  in  ?1
True findByActiveTrue where   x.active = true
false findbyActiveFalse where   x.active =false

先去除findBy,剩下UserDepUuid.假设查询实体为Doc.假如创建如下的查询:findByUserDepUuid().

1.POJO规范,首字母小写.userDepUuid是不是Doc的一个属性,是,则根据该属性查询.如果不是,第二步.

2.从右往左截取第一个大写字母(Uuid),剩下userDep.检查userDep是否为Doc的属性,是,则根据该属性查询,如果不是,重复第二步,最后假设user为Doc的一个属性.

3.接着处理剩下(UepUuid),先判断user对应的类型是否有depUuid属性,有,则根据Doc.user.depUuid的取值进行查询. 没有,继续进行第二步,截取掉了Uuid,剩下dep,假设user对于类型有dep属性,最终Doc.user.dep.uuid的值进行查询.

4.可能会有一种特殊情况.比如Doc包含一个user属性,也有一个userDep属性,此时会存在混淆.可以明确在属性之间加上"_"以显示表示.如:findByUser_DepUuid()  或者  findByUserDep_uuid()

 

特殊的参数:还可以直接在方法的参数上加入分页或排序的参数: 比如:

Page<UserModel> findByName(String name,Pageable pageable);
List<UserModeel>  findByName(String name,Sort sort);

 

 

也可以使用JPA的NameQueries,方法如下:

              1:在实体类上使用@NamedQuery,示例如下:
@NamedQuery(name = "UserModel.findByAge",query = "select o from UserModel o where o.age >= ?1")
2:在自己实现的DAO的Repository接口里面定义一个同名的方法,示例如下:
public List<UserModel> findByAge(int age);

 

3:然后就可以使用了,Spring会先找是否有同名的NamedQuery,如果有,那么就不会按照接口定义的方法来解析。

2.使用@Query

     @Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL 查询语句即可,如下所示: 

 @Query("select o from UserModel o where o.uuid=?1")
public List<UserModel> findByUuidOrAge(int uuid); 

 注意:

               1:方法的参数个数必须和@Query里面需要的参数个数一致

 

2:如果是like,后面的参数需要前面或者后面加“%”,比如下面都对:
 @Query("select o from UserModel o where o.name like ?1%")
public List<UserModel> findByUuidOrAge(String name);
 
@Query("select o from UserModel o where o.name like %?1")
public List<UserModel> findByUuidOrAge(String name);
 
@Query("select o from UserModel o where o.name like %?1%")
public List<UserModel> findByUuidOrAge(String name);
 
//当然,这样在传递参数值的时候就可以不加‘%’了,当然加了也不会错 
 还可以使用@Query来指定本地查询,只要设置nativeQuery为true,比如:
 @Query(value="select * from tbl_user where name like %?1" ,nativeQuery=true)
public List<UserModel> findByUuidOrAge(String name);
//注意:当前版本的本地查询不支持翻页和动态的排序 
使用命名化参数,使用@Param即可,比如:
 @Query(value="select o from UserModel o where o.name like %:nn")
public List<UserModel> findByUuidOrAge(@Param("nn") String name);
//同样支持更新类的Query语句,添加@Modifying即可,比如:
@Modifying
@Query(value="update UserModel o set o.name=:newName where o.name like %:nn")
public int findByUuidOrAge(@Param("nn") String name,@Param("newName") String newName);
/*
注意:
1:方法的返回值应该是int,表示更新语句所影响的行数
2:在调用的地方必须加事务,没有事务不能正常执行 .
*/

 3.JpaRepository的查询功能(创建查询的顺序)

               Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?
<jpa:repositories> 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:
  1. create-if-not-found:如果方法通过@Query指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查 询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。这是 query-lookup-strategy 属性的默认值
  2. create:通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query指定的查询语句,都将会被忽略
  3. use-declared-query:如果方法通过@Query指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常                                                     

        

你可能感兴趣的:(spring)