笔记来源:尚硅谷SpringData教程(springdata经典,spring data快速上手)
@Query 与 @Modifying
1、@Query 注解
1.1、自定义查询
这种查询可以声明在 Repository 方法中,摆脱像命名查询那样的约束,将查询直接在相应的接口方法中声明,结构更为清晰,这是 Spring data 的特 有实现
@Query("SELECT C FROM Customer c WHERE c.customerId=?1")Customer testGetByCustomerId2(Integer id);
测试方法
PersonRepository 类
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();
Test 类
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}
日志信息
Hibernate: select person0_.id as id1_1_, person0_.add_id as add_id6_1_, person0_.address_id as address_2_1_, person0_.birth_day as birth_da3_1_, person0_.email as email4_1_, person0_.last_name as last_nam5_1_ from jpa_persons person0_ where person0_.id=( select max(person1_.id) from jpa_persons person1_ )Person{id=27, lastName='XY', email='XY@qq.com', birthDay=2022-05-21 22:48:34.0}
结论:使用 @Query
注解可以自定义 JPQL 语句以实现更灵活的查询
1.2、索引参数与命名参数
索引参数
索引参数如下所示,索引值从 1 开始,查询中 “?X” 个数需要与方法定义的参数个数相一致,并且顺序也要一致
@Modifying@Query("update User u set u.firstname=?1 where u.lastname=?2")int setFixedFirstnameFor(String firstname,String lastname);
命名参数
命名参数(推荐使用这种方式):可以定义好参数名,赋值时采用 @Param("参数名")
,而不用管顺序
@Query("select cirom customer u where c.firstname=:firstname or c.lastname=:lastname")Customer findByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname);
测试方法 1
PersonRepository 类
@Query("select p from Person p where p.lastName=?1 and p.email=?2")Person getByLastNameAndEmail(String lastName, String email);
Test 类
@Testpublic void testGetByLastNameAndEmail() { Person person = repository.getByLastNameAndEmail("AA", "AA@qq.com"); System.out.println(person);}
日志信息
Hibernate: select person0_.id as id1_1_, person0_.add_id as add_id6_1_, person0_.address_id as address_2_1_, person0_.birth_day as birth_da3_1_, person0_.email as email4_1_, person0_.last_name as last_nam5_1_ from jpa_persons person0_ where person0_.last_name=? and person0_.email=?Person{id=1, lastName='AA', email='AA@qq.com', birthDay=2022-05-21 22:48:34.0}
测试方法 2
PersonRepository 类
@Query("select p from Person p where p.lastName=:lastName and p.email=:email")Person getByEmailAndLastName(@Param("email") String email, @Param("lastName") String lastName);
Test 类
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();0
日志信息
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();1
1.3、模糊查询
如果是 @Query
中有 LIKE
关键字,后面的参数需要前面或者后面加 %
,这样在传递参数值的时候就可以不加 %
:
右模糊
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();2
左模糊
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();3
全模糊
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();4
测试方法 1
PersonRepository 类
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();5
Test 类
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();6
日志信息
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();7
测试方法 2
PersonRepository 类
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();8
Test 类
@Query("select p from Person p where p.id=(select max(id) from Person p)")Person getMaxIdPerson();9
日志信息
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}0
1.4、本地查询
还可以使用 @Query
来指定本地查询,只要设置 nativeQuery
为 true
,比如:
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}1
测试方法
PersonRepository 类
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}2
Test 类
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}3
日志信息
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}4
2、@Modifying 注解
2.1、执行更新操作
@Query
与 @Modifying
这两个 annotation
一起声明,可定义个性化更新操作,例如只涉及某些字段更新时最为常用,示例如下:
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}5
注意:
方法的返回值应该是 int
,表示更新语句所影响的行数
在调用的地方必须加事务,没有事务不能正常执行
测试方法 1
不加 @Modifying
注解会怎么样?
PersonRepository 类
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}6
Test 类
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}7
日志信息
@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}8
报错抛出了 QueryExecutionRequestException
异常,提示 Query 不支持 DML 更新操作
测试方法 2
仅仅加 @Modifying
注解会怎么样?
PersonRepository 类:唯一区别就是加上 @Modifying
注解
@Modifying@Testpublic void testGetMaxIdPerson() { Person person = repository.getMaxIdPerson(); System.out.println(person);}6
日志信息
Hibernate: select person0_.id as id1_1_, person0_.add_id as add_id6_1_, person0_.address_id as address_2_1_, person0_.birth_day as birth_da3_1_, person0_.email as email4_1_, person0_.last_name as last_nam5_1_ from jpa_persons person0_ where person0_.id=( select max(person1_.id) from jpa_persons person1_ )Person{id=27, lastName='XY', email='XY@qq.com', birthDay=2022-05-21 22:48:34.0}0
报错抛出了 TransactionRequiredException
异常,表明更新或删除操作必须要有一个事务
2.2、事务
Spring Data 提供了默认的事务处理方式,即所有的查询均声明为 只读事务
对于自定义的方法,如需改变 Spring Data 提供的事务默认方式,可以在方法上注解 @Transactional
声明
进行多个 Repository 操作时,也应该使它们在同一个事务中处理,按照分层架构的思想,这部分属于业务逻辑层。因此,需要在 Service 层实现对多个 Repository 的调用,并在相应的方法上声明事务
测试方法 3
@Modifying
和 @Transactional
注解都加上
PersonService 类
Hibernate: select person0_.id as id1_1_, person0_.add_id as add_id6_1_, person0_.address_id as address_2_1_, person0_.birth_day as birth_da3_1_, person0_.email as email4_1_, person0_.last_name as last_nam5_1_ from jpa_persons person0_ where person0_.id=( select max(person1_.id) from jpa_persons person1_ )Person{id=27, lastName='XY', email='XY@qq.com', birthDay=2022-05-21 22:48:34.0}1
Test 类
Hibernate: select person0_.id as id1_1_, person0_.add_id as add_id6_1_, person0_.address_id as address_2_1_, person0_.birth_day as birth_da3_1_, person0_.email as email4_1_, person0_.last_name as last_nam5_1_ from jpa_persons person0_ where person0_.id=( select max(person1_.id) from jpa_persons person1_ )Person{id=27, lastName='XY', email='XY@qq.com', birthDay=2022-05-21 22:48:34.0}2
日志信息
Hibernate: select person0_.id as id1_1_, person0_.add_id as add_id6_1_, person0_.address_id as address_2_1_, person0_.birth_day as birth_da3_1_, person0_.email as email4_1_, person0_.last_name as last_nam5_1_ from jpa_persons person0_ where person0_.id=( select max(person1_.id) from jpa_persons person1_ )Person{id=27, lastName='XY', email='XY@qq.com', birthDay=2022-05-21 22:48:34.0}3
数据表 JPA_PERSONS 数据
结论:
1、可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作。注意:JPQL 不支持使用 INSERT
2、在 @Query
注解中编写 JPQL 语句,但必须使用 @Modifying
进行修饰。以通知 SpringData 这是一个 UPDATE 或 DELETE 操作
3、UPDATE 或 DELETE 操作需要使用事务,此时需要定义 Service 层。在 Service 层的方法上添加事务操作
4、默认情况下,SpringData 的每个方法上有事务,但都是一个只读事务。他们不能完成修改操作!
总结
本节主要掌握 @Query
和 @Modifying
两个注解:
@Query
注解支持索引参数、命名参数、模糊查询和本地查询
@Modifying
注解支持更新操作,如 update 和 delete,但不支持 insert(准确来说,JPQL 不支持 insert);需要结合 @Query
和 @Transactional
注解使用
附上导图,仅供参考
原文:https://juejin.cn/post/7101675736704450568