学习Spring Data JPA之路

1。核心概念

Spring Data存储库抽象中的中心接口是Repository它需要管理域类以及域类的ID类型作为类型参数。该接口主要作为标记接口来捕获要使用的类型,并帮助您发现扩展该接口的接口。CrudRepository规定对于正在管理的实体类复杂的CRUD功能

通常情况下,你的资料库接口扩展RepositoryCrudRepositoryPagingAndSortingRepository

要访问User页面大小为20 的第二页,您可以执行以下操作:

PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));

Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);


示例1.派生查询
interface UserRepository extends CrudRepository<User, Long> {

  long deleteByLastname(String lastname);
  List<User> removeByLastname(String lastname);Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
User findFirstByOrderByLastnameAsc();


User findTopByOrderByAgeDesc();


Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);


Slice<User> findTop3ByLastname(String lastname, Pageable pageable);


List<User> findFirst10ByLastname(String lastname, Sort sort);


List<User> findTop10ByLastname(String lastname, Pageable pageable);

}

4.2。查询方法

标准CRUD功能存储库通常会在底层数据存储上进行查询。使用Spring Data,声明这些查询变成了一个四步过程:

  1. 声明一个扩展Repository或其子接口的接口,如果您想公开该域类型的CRUD方法,请扩展CrudRepository而不是Repository并将其键入它应该处理的域类和ID类型,在接口上声明查询方法,如以下示例所示:

  2. interface PersonRepository extends Repository<Person, Long> {
      List<Person> findByLastname(String lastname);
    }
  3. 设置Spring以使用JavaConfigXML配置为这些接口创建代理实例

    1. 要使用Java配置,请创建类似于以下的类:

      import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
      
      @EnableJpaRepositories
      class Config {}
3.定义方法时的关键字说明

表3.方法名称中支持的关键字
关键词 样品 JPQL片段

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstnamefindByFirstnameIsfindByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age 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.firstname like ?1(参数绑定%

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

  • @NonNullApi:在包级别上用于声明参数和返回值的默认行为是不接受或生成null值。

  • @NonNull:用于参数或返回值,不能是null (参数不需要,返回值@NonNullApi适用)。

  • @Nullable:用于可以是的参数或返回值null

常用注解

@MappedSuperclass:

@NoRepositoryBean:选择性的暴露库,确保将该注释添加到Spring Data不应在运行时为其创建实例的所有存储库接口。

@NamedQuery注解,大致意思就是让我们在Repository接口中定义的findByName方法不使用默认的查询实现,取而代之的是使用这条自定义的查询语句去查询,如果这里没有标注的话,会使用默认实现的。


总结性思考:

1.EntityManager

我们都知道,在使用持久化工具的时候,一般都有一个对象来操作数据库,在原生的Hibernate中叫做Session,在JPA中叫做EntityManager,在MyBatis中叫做SqlSession,通过这个对象来操作数据库。

我们一般按照三层结构来看的话,Service层做业务逻辑处理,Dao层和数据库打交道,在Dao中,就存在着上面的对象。那么ORM框架本身提供的功能有什么呢?答案是基本的CRUD,使用Spring-data-jpa进行开发的过程中,常用的功能,我们几乎不需要写一条sql语句。

2.Entity其实就是实体管理器,注意每一个实体类必须要有主见id,需要加入@Id的注解,否则就会报错说实体类没有定义。

3.dao

数据库访问对象,在jpa当中,有一个词语叫Repository,这里我们一般就用Repository结尾来表示这个dao,比如UserDao,这里我们使用UserRepository,同理,在mybatis中我们一般也不叫dao,mybatis由于使用xml映射文件,我们一般使用mapper结尾,比如我们也不叫UserDao,而叫UserMapper。

  首先base-package属性,代表你的Repository接口的位置,repository-impl-postfix属性代表接口的实现类的后缀结尾字符,比如我们的UserRepository,那么他的实现类就叫做UserRepositoryImpl,和我们平时的使用习惯完全一致。

  比如:我们的UserRepository和UserRepositoryImpl这两个类就像下面这样来写。

public interface UserRepository extends JpaRepository<User, Integer>{}
public class UserRepositoryImpl {}

  那么这里为什么要这么做呢?原因是:spring-data-jpa提供基础的CRUD工作,同时也提供业务逻辑的功能,所以我们的Repository接口要做两项工作,继承spring-data-jpa提供的基础CRUD功能的接口,比如JpaRepository接口,同时自己还需要在UserRepository这个接口中定义自己的方法,那么导致的结局就是UserRepository这个接口中有很多的方法,那么如果我们的UserRepositoryImpl实现了UserRepository接口,导致的后果就是我们势必需要重写里面的所有方法,这是Java语法的规定,如此一来,悲剧就产生了,UserRepositoryImpl里面我们有很多的@Override方法,这显然是不行的,结论就是,这里我们不用去写implements部分。原因是:这个过程中cglib发挥了杰出的作用,在spring-data-jpa内部,有一个类,叫做

public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>,
        JpaSpecificationExecutor<T>

我们可以看到这个类是实现了JpaRepository接口的,事实上如果我们按照上面的配置,在同一个包下面有UserRepository,但是没有UserRepositoryImpl这个类的话,在运行时期UserRepository这个接口的实现就是上面的SimpleJpaRepository这个接口。而如果有UserRepositoryImpl这个文件的话,那么UserRepository的实现类就是UserRepositoryImpl,而UserRepositoryImpl这个类又是SimpleJpaRepository的子类,如此一来就很好的解决了上面的这个不用写implements的问题。我们通过阅读这个类的源代码可以发现,里面包装了entityManager,底层的调用关系还是entityManager在进行CRUD。

4.相应的Repository的说明

JpaRepository实现了PagingAndSortingRepository接口,

PagingAndSortingRepository接口实现了CrudRepository接口,

CrudRepository接口实现了Repository接口;

Repository接口是一个标识接口,里面是空的;

Repository接口

这个接口是最基础的接口,只是一个标志性的接口,没有定义任何的方法,那这个接口有什么用了?既然Spring data JPA提供了这个接口,自然是有它的用处,例如,我们有一部分方法是不想对外提供的,比如我们只想提供增加和修改方法,不提供删除方法,那么前面的几个接口都是做不到的,这个时候,我们就可以继承这个接口,然后将CrudRepository接口里面相应的方法拷贝到Repository接口就可以了。

总结:上述五个接口,开发者到底该如何选择?其实依据很简单,根据具体的业务需求,选择其中之一。因为各个接口之间并不存在功能强弱的问题。

CrudRepository接口定义了增删改查方法;

  1. @NoRepositoryBean  
  2. public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {  
  3.     <S extends T> S save(S entity);//保存  
  4.     <S extends T> Iterable<S> save(Iterable<S> entities);//批量保存  
  5.     T findOne(ID id);//根据id查询一个对象  
  6.     boolean exists(ID id);//判断对象是否存在  
  7.     Iterable<T> findAll();//查询所有的对象  
  8.     Iterable<T> findAll(Iterable<ID> ids);//根据id列表查询所有的对象  
  9.     long count();//计算对象的总个数  
  10.     void delete(ID id);//根据id删除  
  11.     void delete(T entity);//删除对象  
  12.     void delete(Iterable<? extends T> entities);//批量删除  
  13.     void deleteAll();//删除所有  
  14. }  

PagingAndSortingRepository接口用于分页和排序;

  1. @NoRepositoryBean  
  2. public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {  
  3.     Iterable<T> findAll(Sort sort);// 不带分页的排序  
  4.     Page<T> findAll(Pageable pageable);// 带分页的排序  
  5. }  

编写测试类:
[java]  view plain  copy
  1. public interface UserRepositoryWithOrder extends  PagingAndSortingRepository<User, Integer> {  
  2.   
  3. }  


[java]  view plain  copy
  1. @RunWith(SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })  
  3. @TransactionConfiguration(defaultRollback = false)  
  4. @Transactional  
  5. public class UserRepositoryWithOrderTest {  
  6.     @Autowired  
  7.     private UserRepositoryWithOrder dao;  
  8.       
  9.     @Test  
  10.     public void testOrder(){  
  11. //分页时,需要传入排序字段,可以多个,可以单个,第一个参数是正序,第二个参数是需要排序的字段。
  12.         Sort sort = new Sort(Direction.DESC, "id");  
  13. //第几页(默认从0开始),每页数量大小,排序字段(上面创建的)
  14.         Pageable pageable = new PageRequest(05, sort);  
  15.         Page<User> page = dao.findAll(pageable);  
  16.         System.out.println(JSON.toJSONString(page));  
  17.         System.out.println(page.getSize());  
  18.     }  
  19. }  


JpaRepository接口继承了以上所有接口,所以拥有它们声明的所有方法;

  1. public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {  
  2.     List<T> findAll();//查询所有对象,不排序  
  3.     List<T> findAll(Sort sort);//查询所有对象,并排序  
  4.     <S extends T> List<S> save(Iterable<S> entities);//批量保存  
  5.     void flush();//强制缓存与数据库同步  
  6.     T saveAndFlush(T entity);//保存并强制同步  
  7.     void deleteInBatch(Iterable<T> entities);//批量删除  
  8.     void deleteAllInBatch();//删除所有  
  9. }  

JpaSpecificationExecutor接口,不属于Repository体系,实现一组JPA Criteria()查询相关的方法。

[java]  view plain  copy
  1. public interface JpaSpecificationExecutor<T> {
        Optional<T> findOne(@Nullable Specification<T> var1);
        List<T> findAll(@Nullable Specification<T> var1);
        Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
        List<T> findAll(@Nullable Specification<T> var1, Sort var2);
        long count(@Nullable Specification<T> var1);
    }

该接口提供了对JPA Criteria查询的支持。Spring data JPA不会自动扫描识别,所以会报找不到对应的Bean,我们只需要继承任意一个继承了Repository的子接口或直接继承Repository接口,Spring data JPA就会自动扫描识别,进行统一的管理。


编写接口如下:

[java]  view plain  copy
  1. public interface SpecificationExecutorRepository extends CrudRepository<User, Integer>,  JpaSpecificationExecutor<User> {  
  2.   
  3. }  

Service 类:
[java]  view plain  copy
  1. @Service  
  2. public class SpecificationExecutorRepositoryManager {  
  3.     @Autowired  
  4.     private SpecificationExecutorRepository dao;  
  5.     /** 
  6.      * 描述:根据name来查询用户 
  7.      */  
  8.     public Optional<User> findUserByName(final String name){  
  9.         return dao.findOne(new Specification<User>() {  
  10.               
  11.             @Override  
  12.             public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query,  CriteriaBuilder cb) {  
  13.                 Predicate predicate = cb.equal(root.get("name"), name);  
  14.                 return predicate;  
  15.             }  
  16.         });  
  17.     }  
  18.       
  19.     /** 
  20.      * 描述:根据name和email来查询用户 
  21.      */  
  22.     public Optional<User> findUserByNameAndEmail(final String name, final String email){  
  23.         return dao.findOne(new Specification<User>() {  
  24.               
  25.             @Override  
  26.             public Predicate toPredicate(Root<User> root,  
  27.                     CriteriaQuery<?> query, CriteriaBuilder cb) {  
  28.                 List<Predicate> list = new ArrayList<Predicate>();  
  29.                 Predicate predicate1 = cb.equal(root.get("name"), name);  
  30.                 Predicate predicate2 = cb.equal(root.get("email"), email);  
  31.                 list.add(predicate1);  
  32.                 list.add(predicate2);  
  33.                 // 注意此处的处理  
  34.                 Predicate[] p = new Predicate[list.size()];  
  35.                 return cb.and(list.toArray(p));  
  36.             }  
  37.         });  
  38.     }  
  39.       
  40.     /** 
  41.      * 描述:组合查询 
  42.      */  
  43.     public User findUserByUser(final User userVo){  
  44.         return dao.findOne(new Specification<User>() {  
  45.               
  46.             @Override  
  47.             public Predicate toPredicate(Root<User> root,  
  48.                     CriteriaQuery<?> query, CriteriaBuilder cb) {  
  49.                 Predicate predicate = cb.equal(root.get("name"), userVo.getName());  
  50.                 cb.and(predicate, cb.equal(root.get("email"), userVo.getEmail()));  
  51.                 cb.and(predicate, cb.equal(root.get("password"), userVo.getPassword()));  
  52.                 return predicate;  
  53.             }  
  54.         });  
  55.     }  
  56.       
  57.     /** 
  58.      * 描述:范围查询in方法,例如查询用户id在[2,10]中的用户 
  59.      */  
  60.     public List<User> findUserByIds(final List<Integer> ids){  
  61.         return dao.findAll(new Specification<User>() {  
  62.   
  63.             @Override  
  64.             public Predicate toPredicate(Root<User> root,  
  65.                     CriteriaQuery<?> query, CriteriaBuilder cb) {  
  66.                 return root.in(ids);  
  67.             }  
  68.         });  
  69.     }  
  70.       
  71.     /** 
  72.      * 描述:范围查询gt方法,例如查询用户id大于9的所有用户 
  73.      */  
  74.     public List<User> findUserByGtId(final int id){  
  75.         return dao.findAll(new Specification<User>() {  
  76.   
  77.             @Override  
  78.             public Predicate toPredicate(Root<User> root,  
  79.                     CriteriaQuery<?> query, CriteriaBuilder cb) {  
  80.                 return cb.gt(root.get("id").as(Integer.class), id);  
  81.             }  
  82.         });  
  83.     }  
  84.       
  85.     /** 
  86.      * 描述:范围查询lt方法,例如查询用户id小于10的用户 
  87.      */  
  88.     public List<User> findUserByLtId(final int id){  
  89.         return dao.findAll(new Specification<User>() {  
  90.   
  91.             @Override  
  92.             public Predicate toPredicate(Root<User> root,  
  93.                     CriteriaQuery<?> query, CriteriaBuilder cb) {  
  94.                 return cb.lt(root.get("id").as(Integer.class), id);  
  95.             }  
  96.         });  
  97.     }  
  98.       
  99.     /** 
  100.      * 描述:范围查询between方法,例如查询id在3和10之间的用户 
  101.      */  
  102.     public List<User> findUserBetweenId(final int start, final int end){  
  103.         return dao.findAll(new Specification<User>() {  
  104.   
  105.             @Override  
  106.             public Predicate toPredicate(Root<User> root,  
  107.                     CriteriaQuery<?> query, CriteriaBuilder cb) {  
  108.                 return cb.between(root.get("id").as(Integer.class), start, end);  
  109.             }  
  110.         });  
  111.     }  
  112.       
  113.     /** 
  114.      * 描述:排序和分页操作 
  115.      */  
  116.     public Page<User> findUserAndOrder(final int id){  
  117.         Sort sort = new Sort(Direction.DESC, "id");  
  118.         return dao.findAll(new Specification<User>() {  
  119.   
  120.             @Override  
  121.             public Predicate toPredicate(Root<User> root,  
  122.                     CriteriaQuery<?> query, CriteriaBuilder cb) {  
  123.                 return cb.gt(root.get("id").as(Integer.class), id);  
  124.             }  
  125.         }, new PageRequest(05, sort));  
  126.     }  
  127.       
  128.     /** 
  129.      * 描述:只有排序操作 
  130.      */  
  131.     public List<User> findUserAndOrderSecondMethod(final int id){  
  132.         return dao.findAll(new Specification<User>() {  
  133.   
  134.             @Override  
  135.             public Predicate toPredicate(Root<User> root,  CriteriaQuery<?> query, CriteriaBuilder cb) {  
  136.                 cb.gt(root.get("id").as(Integer.class), id);  
  137.                 query.orderBy(cb.desc(root.get("id").as(Integer.class)));  
  138.                 return query.getRestriction();  
  139.             }  
  140.         });  
  141.     }  
  142. Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。
  143. Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似
    1:Root实例是类型化的,且定义了查询的FROM子句中能够出现的类型。
    2:查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。
    3:Criteria查询,可以有多个查询根。
    4:AbstractQuery是CriteriaQuery 接口的父类,它提供得到查询根的方法。
    CriteriaQuery接口:代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by等注意:CriteriaQuery对象只对实体类型或嵌入式类型的Criteria查询起作用
  144. CriteriaBuilder接口:用来构建CritiaQuery的构建器对象Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合。
  145. }  

3、通过解析方法名创建查询

顾名思义,就是根据方法的名字,就能创建查询,也许初听起来,感觉很不可思议,等测试后才发现,原来一切皆有可能。

编写接口:

[java]  view plain  copy
  1. public interface SimpleConditionQueryRepository extends JpaRepository<User, Integer> {  
  2.     /** 
  3.      * 说明:按照Spring data 定义的规则,查询方法以find|read|get开头 
  4.      * 涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写 
  5.      */  
  6.       
  7.       
  8.       
  9.     /** 
  10.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = :name and u.email = :email 
  11.      * 参数名大写,条件名首字母大写,并且接口名中参数出现的顺序必须和参数列表中的参数顺序一致 
  12.      */  
  13.     User findByNameAndEmail(String name, String email);  
  14.       
  15.     /** 
  16.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = ?1 or u.password = ?2 
  17.      */  
  18.     List<User> findByNameOrPassword(String name, String password);  
  19.       
  20.     /** 
  21.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id between ?1 and ?2 
  22.      */  
  23.     List<User> findByIdBetween(Integer start, Integer end);  
  24.       
  25.     /** 
  26.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id < ?1 
  27.      */  
  28.     List<User> findByIdLessThan(Integer end);  
  29.       
  30.     /** 
  31.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id > ?1 
  32.      */  
  33.     List<User> findByIdGreaterThan(Integer start);  
  34.       
  35.     /** 
  36.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is null 
  37.      */  
  38.     List<User> findByNameIsNull();  
  39.       
  40.     /** 
  41.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is not null 
  42.      */  
  43.     List<User> findByNameIsNotNull();  
  44.       
  45.     /** 
  46.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name like ?1 
  47.      */  
  48.     List<User> findByNameLike(String name);  
  49.       
  50.     /** 
  51.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name not like ?1 
  52.      */  
  53.     List<User> findByNameNotLike(String name);  
  54.       
  55.     /** 
  56.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.password = ?1 order by u.id desc 
  57.      */  
  58.     List<User> findByPasswordOrderByIdDesc(String password);  
  59.       
  60.     /** 
  61.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name <> ?1 
  62.      */  
  63.     List<User> findByNameNot(String name);  
  64.       
  65.     /** 
  66.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id in ?1 
  67.      */  
  68.     List<User> findByIdIn(List<Integer> ids);  
  69.       
  70.     /** 
  71.      * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id not in ?1 
  72.      */  
  73.     List<User> findByIdNotIn(List<Integer> ids);  
  74. }  
测试类 ( 注释部分为实际发送的sql语句 )
[java]  view plain  copy
  1. @RunWith(SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })  
  3. @TransactionConfiguration(defaultRollback = false)  
  4. @Transactional  
  5. public class SimpleConditionQueryRepositoryTest {  
  6.     @Autowired  
  7.     private SimpleConditionQueryRepository dao;  
  8.       
  9.     /** 
  10.      * select 
  11.         user0_.id as id0_, 
  12.         user0_.account as account0_, 
  13.         user0_.email as email0_, 
  14.         user0_.name as name0_, 
  15.         user0_.password as password0_  
  16.     from 
  17.         USER user0_  
  18.     where 
  19.         user0_.name=?  
  20.         and user0_.email=? limit ? 
  21.      */  
  22.     @Test  
  23.     public void testFindUserByNameAndEmail(){  
  24.         User user = dao.findByNameAndEmail("chhliu""[email protected]");  
  25.         System.out.println(JSON.toJSONString(user));  
  26.     }  
  27.       
  28.     /** 
  29.      * select 
  30.         user0_.id as id1_, 
  31.         user0_.account as account1_, 
  32.         user0_.email as email1_, 
  33.         user0_.name as name1_, 
  34.         user0_.password as password1_  
  35.     from 
  36.         USER user0_  
  37.     where 
  38.         user0_.name=?  
  39.         or user0_.password=? 
  40.      */  
  41.     @Test  
  42.     public void testFindUserByNameOrPassword(){  
  43.         List<User> users = dao.findByNameOrPassword("chhliu""123456");  
  44.         System.out.println(JSON.toJSONString(users));  
  45.     }  
  46.       
  47.     /** 
  48.      * select 
  49.         user0_.id as id1_, 
  50.         user0_.account as account1_, 
  51.         user0_.email as email1_, 
  52.         user0_.name as name1_, 
  53.         user0_.password as password1_  
  54.     from 
  55.         USER user0_  
  56.     where 
  57.         user0_.id between ? and ? 
  58.      */  
  59.     @Test  
  60.     public void testFindByIdBetween(){  
  61.         List<User> users = dao.findByIdBetween(58);  
  62.         System.out.println(JSON.toJSONString(users));  
  63.     }  
  64.       
  65.     /** 
  66.      * select 
  67.         user0_.id as id1_, 
  68.         user0_.account as account1_, 
  69.         user0_.email as email1_, 
  70.         user0_.name as name1_, 
  71.         user0_.password as password1_  
  72.     from 
  73.         USER user0_  
  74.     where 
  75.         user0_.id<? 
  76.      */  
  77.     @Test  
  78.     public void testFindByIdLessThan(){  
  79.         List<User> users = dao.findByIdLessThan(4);  
  80.         System.out.println(JSON.toJSONString(users));  
  81.     }  
  82.       
  83.     /** 
  84.      * select 
  85.         user0_.id as id0_, 
  86.         user0_.account as account0_, 
  87.         user0_.email as email0_, 
  88.         user0_.name as name0_, 
  89.         user0_.password as password0_  
  90.     from 
  91.         USER user0_  
  92.     where 
  93.         user0_.id>? 
  94.      */  
  95.     @Test  
  96.     public void testFindByIdGreaterThan(){  
  97.         List<User> users = dao.findByIdGreaterThan(6);  
  98.         System.out.println(JSON.toJSONString(users));  
  99.     }  
  100.       
  101.     /** 
  102.      * select 
  103.         user0_.id as id0_, 
  104.         user0_.account as account0_, 
  105.         user0_.email as email0_, 
  106.         user0_.name as name0_, 
  107.         user0_.password as password0_  
  108.     from 
  109.         USER user0_  
  110.     where 
  111.         user0_.name is null 
  112.      */  
  113.     @Test  
  114.     public void testFindByNameIsNull(){  
  115.         List<User> users = dao.findByNameIsNull();  
  116.         System.out.println(JSON.toJSONString(users));  
  117.     }  
  118.       
  119.     /** 
  120.      * select 
  121.         user0_.id as id1_, 
  122.         user0_.account as account1_, 
  123.         user0_.email as email1_, 
  124.         user0_.name as name1_, 
  125.         user0_.password as password1_  
  126.     from 
  127.         USER user0_  
  128.     where 
  129.         user0_.name is not null 
  130.      */  
  131.     @Test  
  132.     public void testFindByNameIsNotNull(){  
  133.         List<User> users = dao.findByNameIsNotNull();  
  134.         System.out.println(JSON.toJSONString(users));  
  135.     }  
  136.       
  137.     /** 
  138.      * select 
  139.         user0_.id as id1_, 
  140.         user0_.account as account1_, 
  141.         user0_.email as email1_, 
  142.         user0_.name as name1_, 
  143.         user0_.password as password1_  
  144.     from 
  145.         USER user0_  
  146.     where 
  147.         user0_.name like ? 
  148.      */  
  149.     @Test  
  150.     public void testFindByNameLike(){  
  151.         List<User> users = dao.findByNameLike("chhliu");  
  152.         System.out.println(JSON.toJSONString(users));  
  153.     }  
  154.       
  155.     /** 
  156.      * select 
  157.         user0_.id as id0_, 
  158.         user0_.account as account0_, 
  159.         user0_.email as email0_, 
  160.         user0_.name as name0_, 
  161.         user0_.password as password0_  
  162.     from 
  163.         USER user0_  
  164.     where 
  165.         user0_.name not like ? 
  166.      */  
  167.     @Test  
  168.     public void testFindByNameNotLike(){  
  169.         List<User> users = dao.findByNameNotLike("chhliu");  
  170.         System.out.println(JSON.toJSONString(users));  
  171.     }  
  172.       
  173.     /** 
  174.      * select 
  175.         user0_.id as id0_, 
  176.         user0_.account as account0_, 
  177.         user0_.email as email0_, 
  178.         user0_.name as name0_, 
  179.         user0_.password as password0_  
  180.     from 
  181.         USER user0_  
  182.     where 
  183.         user0_.password=?  
  184.     order by 
  185.         user0_.id desc 
  186.      */  
  187.     @Test  
  188.     public void testFindByPasswordOrderByIdDesc(){  
  189.         List<User> users = dao.findByPasswordOrderByIdDesc("123456");  
  190.         System.out.println(JSON.toJSONString(users));  
  191.     }  
  192.       
  193.     /** 
  194.      * select 
  195.         user0_.id as id1_, 
  196.         user0_.account as account1_, 
  197.         user0_.email as email1_, 
  198.         user0_.name as name1_, 
  199.         user0_.password as password1_  
  200.     from 
  201.         USER user0_  
  202.     where 
  203.         user0_.name<>? 
  204.      */  
  205.     @Test  
  206.     public void testFindByNameNot(){  
  207.         List<User> users = dao.findByNameNot("chhliu");  
  208.         System.out.println(JSON.toJSONString(users));  
  209.     }  
  210.       
  211.     /** 
  212.      * select 
  213.         user0_.id as id1_, 
  214.         user0_.account as account1_, 
  215.         user0_.email as email1_, 
  216.         user0_.name as name1_, 
  217.         user0_.password as password1_  
  218.     from 
  219.         USER user0_  
  220.     where 
  221.         user0_.id in ( 
  222.             ? , ? , ? , ? 
  223.         ) 
  224.      */  
  225.     @Test  
  226.     public void testFindByIdIn(){  
  227.         List<User> users = dao.findByIdIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));  
  228.         System.out.println(JSON.toJSONString(users));  
  229.     }  
  230.       
  231.     /** 
  232.      * select 
  233.         user0_.id as id0_, 
  234.         user0_.account as account0_, 
  235.         user0_.email as email0_, 
  236.         user0_.name as name0_, 
  237.         user0_.password as password0_  
  238.     from 
  239.         USER user0_  
  240.     where 
  241.         user0_.id not in  ( 
  242.             ? , ? , ? , ? 
  243.         ) 
  244.      */  
  245.     @Test  
  246.     public void testFindByIdNotIn(){  
  247.         List<User> users = dao.findByIdNotIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));  
  248.         System.out.println(JSON.toJSONString(users));  
  249.     }  
  250. }  
这里,我们只定义了一个接口,接口里面只有方法,但是没有任何的实现,却完成了各种操作。

看到这里,估计很多人都会问,Spring data JPA是怎么做到的了?原来,框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 findfindByreadreadBygetgetBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByIdIn()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析

在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:

And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd)

Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr)

Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min)

LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max)

GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min)

IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull()

IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull()

NotNull --- 与 IsNotNull 等价

Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user)

NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user)

OrderBy ---等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user)

Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user)

In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数

NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数

五、创建查询的顺序

Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?为此,<jpa:repositories> 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:

create --- 通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query 指定的查询语句,都将会被忽略。

create-if-not-found --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。这是 query-lookup-strategy 属性的默认值。

use-declared-query --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常。

六、Spring Data JPA 对事务的支持

细心的读者也许从上面的代码中看出了一些端倪,我们在使用Spring data JPA的时候,只是定义了接口,在使用的时候,直接注入就可以了,并没有做与事物相关的任何处理,但实际上,事物已经起到效果了,这又是为什么了?

默认情况下,Spring Data JPA 实现的方法都是使用事务的。针对查询类型的方法,其等价于 @Transactional(readOnly=true);增删改类型的方法,等价于 @Transactional。可以看出,除了将查询的方法设为只读事务外,其他事务属性均采用默认值。

如果用户觉得有必要,可以在接口方法上使用 @Transactional 显式指定事务属性,该值覆盖 Spring Data JPA 提供的默认值。同时,开发者也可以在业务层方法上使用 @Transactional 指定事务属性,这主要针对一个业务层方法多次调用持久层方法的情况。持久层的事务会根据设置的事务传播行为来决定是挂起业务层事务还是加入业务层的事务。



发布了93 篇原创文章 · 获赞 26 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/lizhen1114/article/details/80700288