1。核心概念
Spring Data存储库抽象中的中心接口是Repository
。它需要管理域类以及域类的ID类型作为类型参数。该接口主要作为标记接口来捕获要使用的类型,并帮助您发现扩展该接口的接口。该CrudRepository
规定对于正在管理的实体类复杂的CRUD功能。
通常情况下,你的资料库接口扩展Repository
,CrudRepository
或PagingAndSortingRepository
。
要访问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);
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,声明这些查询变成了一个四步过程:
声明一个扩展Repository或其子接口的接口,如果您想公开该域类型的CRUD方法,请扩展
CrudRepository
而不是Repository
。并将其键入它应该处理的域类和ID类型,在接口上声明查询方法,如以下示例所示:-
interface PersonRepository extends Repository<Person, Long> { List<Person> findByLastname(String lastname); }
设置Spring以使用JavaConfig或XML配置为这些接口创建代理实例。
要使用Java配置,请创建类似于以下的类:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @EnableJpaRepositories class Config {}
关键词 | 样品 | JPQL片段 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@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接口定义了增删改查方法;
- @NoRepositoryBean
- 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);//根据id查询一个对象
- boolean exists(ID id);//判断对象是否存在
- Iterable<T> findAll();//查询所有的对象
- Iterable<T> findAll(Iterable<ID> ids);//根据id列表查询所有的对象
- long count();//计算对象的总个数
- void delete(ID id);//根据id删除
- void delete(T entity);//删除对象
- void delete(Iterable<? extends T> entities);//批量删除
- void deleteAll();//删除所有
- }
PagingAndSortingRepository接口用于分页和排序;
- @NoRepositoryBean
- public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
- Iterable<T> findAll(Sort sort);// 不带分页的排序
- Page<T> findAll(Pageable pageable);// 带分页的排序
- }
- public interface UserRepositoryWithOrder extends PagingAndSortingRepository<User, Integer> {
- }
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
- @TransactionConfiguration(defaultRollback = false)
- @Transactional
- public class UserRepositoryWithOrderTest {
- @Autowired
- private UserRepositoryWithOrder dao;
- @Test
- public void testOrder(){
- //分页时,需要传入排序字段,可以多个,可以单个,第一个参数是正序,第二个参数是需要排序的字段。
- Sort sort = new Sort(Direction.DESC, "id");
- //第几页(默认从0开始),每页数量大小,排序字段(上面创建的)
- Pageable pageable = new PageRequest(0, 5, sort);
- Page<User> page = dao.findAll(pageable);
- System.out.println(JSON.toJSONString(page));
- System.out.println(page.getSize());
- }
- }
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();//删除所有
- }
JpaSpecificationExecutor接口,不属于Repository体系,实现一组JPA Criteria()查询相关的方法。
- 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就会自动扫描识别,进行统一的管理。
编写接口如下:
- public interface SpecificationExecutorRepository extends CrudRepository<User, Integer>, JpaSpecificationExecutor<User> {
- }
Service 类:
- @Service
- public class SpecificationExecutorRepositoryManager {
- @Autowired
- private SpecificationExecutorRepository dao;
- /**
- * 描述:根据name来查询用户
- */
- public Optional<User> findUserByName(final String name){
- return dao.findOne(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
- Predicate predicate = cb.equal(root.get("name"), name);
- return predicate;
- }
- });
- }
- /**
- * 描述:根据name和email来查询用户
- */
- public Optional<User> findUserByNameAndEmail(final String name, final String email){
- return dao.findOne(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- List<Predicate> list = new ArrayList<Predicate>();
- Predicate predicate1 = cb.equal(root.get("name"), name);
- Predicate predicate2 = cb.equal(root.get("email"), email);
- list.add(predicate1);
- list.add(predicate2);
- // 注意此处的处理
- Predicate[] p = new Predicate[list.size()];
- return cb.and(list.toArray(p));
- }
- });
- }
- /**
- * 描述:组合查询
- */
- public User findUserByUser(final User userVo){
- return dao.findOne(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- Predicate predicate = cb.equal(root.get("name"), userVo.getName());
- cb.and(predicate, cb.equal(root.get("email"), userVo.getEmail()));
- cb.and(predicate, cb.equal(root.get("password"), userVo.getPassword()));
- return predicate;
- }
- });
- }
- /**
- * 描述:范围查询in方法,例如查询用户id在[2,10]中的用户
- */
- public List<User> findUserByIds(final List<Integer> ids){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return root.in(ids);
- }
- });
- }
- /**
- * 描述:范围查询gt方法,例如查询用户id大于9的所有用户
- */
- public List<User> findUserByGtId(final int id){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.gt(root.get("id").as(Integer.class), id);
- }
- });
- }
- /**
- * 描述:范围查询lt方法,例如查询用户id小于10的用户
- */
- public List<User> findUserByLtId(final int id){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.lt(root.get("id").as(Integer.class), id);
- }
- });
- }
- /**
- * 描述:范围查询between方法,例如查询id在3和10之间的用户
- */
- public List<User> findUserBetweenId(final int start, final int end){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.between(root.get("id").as(Integer.class), start, end);
- }
- });
- }
- /**
- * 描述:排序和分页操作
- */
- public Page<User> findUserAndOrder(final int id){
- Sort sort = new Sort(Direction.DESC, "id");
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root,
- CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.gt(root.get("id").as(Integer.class), id);
- }
- }, new PageRequest(0, 5, sort));
- }
- /**
- * 描述:只有排序操作
- */
- public List<User> findUserAndOrderSecondMethod(final int id){
- return dao.findAll(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
- cb.gt(root.get("id").as(Integer.class), id);
- query.orderBy(cb.desc(root.get("id").as(Integer.class)));
- return query.getRestriction();
- }
- });
- }
- Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。
- 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查询起作用 - CriteriaBuilder接口:用来构建CritiaQuery的构建器对象Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合。
- }
3、通过解析方法名创建查询
顾名思义,就是根据方法的名字,就能创建查询,也许初听起来,感觉很不可思议,等测试后才发现,原来一切皆有可能。
编写接口:
- public interface SimpleConditionQueryRepository extends JpaRepository<User, Integer> {
- /**
- * 说明:按照Spring data 定义的规则,查询方法以find|read|get开头
- * 涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写
- */
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = :name and u.email = :email
- * 参数名大写,条件名首字母大写,并且接口名中参数出现的顺序必须和参数列表中的参数顺序一致
- */
- User findByNameAndEmail(String name, String email);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = ?1 or u.password = ?2
- */
- List<User> findByNameOrPassword(String name, String password);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id between ?1 and ?2
- */
- List<User> findByIdBetween(Integer start, Integer end);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id < ?1
- */
- List<User> findByIdLessThan(Integer end);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id > ?1
- */
- List<User> findByIdGreaterThan(Integer start);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is null
- */
- List<User> findByNameIsNull();
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is not null
- */
- List<User> findByNameIsNotNull();
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name like ?1
- */
- List<User> findByNameLike(String name);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name not like ?1
- */
- List<User> findByNameNotLike(String name);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.password = ?1 order by u.id desc
- */
- List<User> findByPasswordOrderByIdDesc(String password);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name <> ?1
- */
- List<User> findByNameNot(String name);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id in ?1
- */
- List<User> findByIdIn(List<Integer> ids);
- /**
- * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id not in ?1
- */
- List<User> findByIdNotIn(List<Integer> ids);
- }
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
- @TransactionConfiguration(defaultRollback = false)
- @Transactional
- public class SimpleConditionQueryRepositoryTest {
- @Autowired
- private SimpleConditionQueryRepository dao;
- /**
- * select
- user0_.id as id0_,
- user0_.account as account0_,
- user0_.email as email0_,
- user0_.name as name0_,
- user0_.password as password0_
- from
- USER user0_
- where
- user0_.name=?
- and user0_.email=? limit ?
- */
- @Test
- public void testFindUserByNameAndEmail(){
- User user = dao.findByNameAndEmail("chhliu", "[email protected]");
- System.out.println(JSON.toJSONString(user));
- }
- /**
- * select
- user0_.id as id1_,
- user0_.account as account1_,
- user0_.email as email1_,
- user0_.name as name1_,
- user0_.password as password1_
- from
- USER user0_
- where
- user0_.name=?
- or user0_.password=?
- */
- @Test
- public void testFindUserByNameOrPassword(){
- List<User> users = dao.findByNameOrPassword("chhliu", "123456");
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id1_,
- user0_.account as account1_,
- user0_.email as email1_,
- user0_.name as name1_,
- user0_.password as password1_
- from
- USER user0_
- where
- user0_.id between ? and ?
- */
- @Test
- public void testFindByIdBetween(){
- List<User> users = dao.findByIdBetween(5, 8);
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id1_,
- user0_.account as account1_,
- user0_.email as email1_,
- user0_.name as name1_,
- user0_.password as password1_
- from
- USER user0_
- where
- user0_.id<?
- */
- @Test
- public void testFindByIdLessThan(){
- List<User> users = dao.findByIdLessThan(4);
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id0_,
- user0_.account as account0_,
- user0_.email as email0_,
- user0_.name as name0_,
- user0_.password as password0_
- from
- USER user0_
- where
- user0_.id>?
- */
- @Test
- public void testFindByIdGreaterThan(){
- List<User> users = dao.findByIdGreaterThan(6);
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id0_,
- user0_.account as account0_,
- user0_.email as email0_,
- user0_.name as name0_,
- user0_.password as password0_
- from
- USER user0_
- where
- user0_.name is null
- */
- @Test
- public void testFindByNameIsNull(){
- List<User> users = dao.findByNameIsNull();
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id1_,
- user0_.account as account1_,
- user0_.email as email1_,
- user0_.name as name1_,
- user0_.password as password1_
- from
- USER user0_
- where
- user0_.name is not null
- */
- @Test
- public void testFindByNameIsNotNull(){
- List<User> users = dao.findByNameIsNotNull();
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id1_,
- user0_.account as account1_,
- user0_.email as email1_,
- user0_.name as name1_,
- user0_.password as password1_
- from
- USER user0_
- where
- user0_.name like ?
- */
- @Test
- public void testFindByNameLike(){
- List<User> users = dao.findByNameLike("chhliu");
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id0_,
- user0_.account as account0_,
- user0_.email as email0_,
- user0_.name as name0_,
- user0_.password as password0_
- from
- USER user0_
- where
- user0_.name not like ?
- */
- @Test
- public void testFindByNameNotLike(){
- List<User> users = dao.findByNameNotLike("chhliu");
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id0_,
- user0_.account as account0_,
- user0_.email as email0_,
- user0_.name as name0_,
- user0_.password as password0_
- from
- USER user0_
- where
- user0_.password=?
- order by
- user0_.id desc
- */
- @Test
- public void testFindByPasswordOrderByIdDesc(){
- List<User> users = dao.findByPasswordOrderByIdDesc("123456");
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id1_,
- user0_.account as account1_,
- user0_.email as email1_,
- user0_.name as name1_,
- user0_.password as password1_
- from
- USER user0_
- where
- user0_.name<>?
- */
- @Test
- public void testFindByNameNot(){
- List<User> users = dao.findByNameNot("chhliu");
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id1_,
- user0_.account as account1_,
- user0_.email as email1_,
- user0_.name as name1_,
- user0_.password as password1_
- from
- USER user0_
- where
- user0_.id in (
- ? , ? , ? , ?
- )
- */
- @Test
- public void testFindByIdIn(){
- List<User> users = dao.findByIdIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
- System.out.println(JSON.toJSONString(users));
- }
- /**
- * select
- user0_.id as id0_,
- user0_.account as account0_,
- user0_.email as email0_,
- user0_.name as name0_,
- user0_.password as password0_
- from
- USER user0_
- where
- user0_.id not in (
- ? , ? , ? , ?
- )
- */
- @Test
- public void testFindByIdNotIn(){
- List<User> users = dao.findByIdNotIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
- System.out.println(JSON.toJSONString(users));
- }
- }
看到这里,估计很多人都会问,Spring data JPA是怎么做到的了?原来,框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 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 指定事务属性,这主要针对一个业务层方法多次调用持久层方法的情况。持久层的事务会根据设置的事务传播行为来决定是挂起业务层事务还是加入业务层的事务。