1、@Query

  对于少量的查询,使用@NamedQuery在实体上声明查询是一种有效的办法,并且可以很好的工作。由于查询本身绑定到执行它们的java方法,实际上可以通过Spring-Data-Jpa提供的@Query注解来直接绑定它们,而不是将它们注释到domain类。这将domain类从持久化特定信息中解放出来,并将查询共同定位到存储库接口。

  1.1、@Query源码

/**
* 直接注解在repository方法上声明一个查找器
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@QueryAnnotation
@Documented
public @interface Query { /**
* 定义在执行带有@Query注解方法时,要执行的JPA查询。
*/
String value() default ""; /**
* 定义一个特殊的count查询,用于分页查询时,查找页面元素的总个数。如果没有配置,将根据方法名派生一个count查询。
*/
String countQuery() default ""; /**
*定义为countQuery查询生成的投影部分,如果没有配置countQuery和countProjection,将根据方法名派生count查询。
*/
String countProjection() default ""; /**
* 配置给定的查询是否是原生SQL查询,默认是false,query中为JPQL语句,为true时,query中应写原声SQL语句。
*/
boolean nativeQuery() default false; /**
* named query使用,如果没有定义,@NamedQuery的名字使用{$ domainClass}.${queryMethodName}
*/
String name() default ""; /**
* 返回在执行count查询时@NamedQuery使用的name,默认是named query name 添加后缀.count。
*/
String countName() default "";
}

  1.2、使用时,在Repository接口的方法上添加即可。

    /**
* 通过用户名密码进行查询
* @param username username
* @param password password
* @return User
*/
@Query(value = "select u from User u where u.username = ?1 and u.password = ?2 ")
User findByUsernameAndPassword(String username,String password);

  1.3、可以识别LIKE的分隔符字符(%),并将查询转换为有效的JPQL查询(删除%)。在执行查询时,传递给方法调用的参数将使用之前识别的LIKE模式进行扩充。

    /**
* 查询以...开头的phone用户
* @param phone phone
* @return list
*/
@Query(value = "select u from User u where u.phone like ?1% ")
List<User> findByPhoneIsStartingWith(String phone);

  1.4、Spring-Data-Jpa目前不支持对nativeQuery=true时的Sort动态排序,对于原生SQL来说,它不能可靠的执行这种操作。但是可以通过指定count查询来使用分页。

    /**
* 根据性别查询并分页,原生SQL,不能使用SEX枚举,要使用String
* @param sex sex
* @param pageable pageable
* @return page
*/
@Query(value = "SELECT * FROM cfq_jpa_user WHERE sex = ?1 ",
countQuery = "SELECT count(*) FROM cfq_jpa_user WHERE sex = ?1 ",
nativeQuery = true)
Page<User> findBySexString(String sex, Pageable pageable);

  1.5、默认情况下,Spring-Data-Jpa拒绝任何包含函数调用的Order实例,可以通过起别名或JpaSort来替代。

  接口方法;

    /**
* 测试Order中不支持函数
* @param sex sex
* @param sort sort
* @return list
*/
@Query(value = "select u.id,length(u.email) as em_len from User u where u.sex = ?1 ")
List<Object[]> findByAsArrayAndSort(Sex sex,Sort sort);

  测试用例:

    /**
* Sort中不支持使用函数,可以使用别名或JpaSort替代
*/
@Test
void findByAsArrayAndSort(){ //sort指向domain模型中有效的属性,jpa会自动为我们加上表的别名,不需要自己添加,添加就会报错哦。
List<Object[]> result1 = userRepository.findByAsArrayAndSort(Sex.MAN, Sort.by("email")); //Sort不支持使用函数
//Sort expression 'length(email): ASC' must only contain property references or aliases used in the select clause. If you really want to use something other than that for sorting, please use JpaSort.unsafe(…)!
assertThrows(InvalidDataAccessApiUsageException.class,() -> userRepository.findByAsArrayAndSort(Sex.MAN, Sort.by("length(email)"))); //使用JpaSort的unsafe来使用函数
List<Object[]> result2 = userRepository.findByAsArrayAndSort(Sex.MAN, JpaSort.unsafe("length(email)")); //可以使用别名来进行排序
List<Object[]> result3 = userRepository.findByAsArrayAndSort(Sex.MAN, Sort.by("em_len"));
}

  1.6、默认情况下,Spring-Data-Jpa是基于位置的参数绑定,当重构关于参数位置的查询方法时容易出错,可以使用@Param指定名称,进行名称绑定。如果参数名称和定义的名称一致,可以省略@Param。

    /**
* 使用@Param进行参数名称绑定后,参数位置无所谓
* @param ppp ppp
* @param uuu uuu
* @return list
*/
@Query(value = "select u from User u where u.username = :username or u.phone = :phone ")
List<User> findByPhoneOrUsername(@Param("phone")String ppp,@Param("username")String uuu); /**
* 参数名称一致时,可以省略@Param
* @param username
* @param phone
* @return
*/
@Query(value = "select u from User u where u.phone = :phone or u.username = :username ")
List<User> findByUsernameOrPhone(String username,String phone);

  1.7、支持一个entityName的spel变量,用法是select x from #{#entityName} x,插入entityName与给定Repository的关联的域类型。如果域类设置了@Entity的name属性,使用name属性的名称,如果没设置,使用域类型的简单类名。也可以在参数上使用spel表达式。

    /**
* 为了避免在查询的字符串声明中使用实体类名,可以使用#{#entityName}变量.
* 根据用户名查询
* @param username username
* @return User
*/
@Query(value = "select u from #{#entityName} u where u.username = ?1 ")
User findUserByUsernameWithSpelEntityName(String username); /**
* 使用spel表达式
*
* @param user user
* @return user
*/
@Query(value = "select u from User u where u.username = :#{#user.username} ")
User findUserByUsernameWithSpel(User user);

  1.8、对于like条件,通常在开头或结尾添加%,可以在参数绑定或spel上附加%。

    /**
* like查询可以在参数绑定或spel上追加%。
* @param email email
* @return list
*/
@Query(value = "select u from User u where u.email like %:#{[0]}% and u.email like %:email%")
List<User> findByEmailLikeWithSpel(String email); /**
* 原生sql也支持参数绑定和spel表达式
* @param email email
* @return list
*/
@Query(value = "SELECT * FROM cfq_jpa_user WHERE email LIKE %:#{[0]}% AND email LIKE %:email% " ,nativeQuery = true)
List<User> findByEmailLikeWithNativeQuery(String email);

  1.9、对于like条件中的_,%进行转义,在spel中可以使用escape(String) 进行替换,将第二个参数中的字符作为第一个参数中_和%的前缀。escape(String)方法只能将_或%转义,如果数据库有其他的通配符,这无法转义。

  1.10、转义的字符可以通过@EnableJpaRepositories注解的escapeCharacter属性进行设置。

    /**
* like查询 escape() 定义转义字符
* @param email email
* @return list
*/
@Query(value = "select u from User u where u.email like %?#{escape([0])}% escape ?#{escapeCharacter()} ")
List<User> findByEmailLikeWithEscaped(String email);

  1.11、当 #{entityName} 与参数SPEL表达式一起使用时,参数要使用?#{进行开头。例如:

    /**
* 使用spel表达式
* 当 #{#entityName} 与SPEL 一起使用时,参数要使用 ?#{
*
* @param user user
* @return user
*/
// @Query(value = "select u from User u where u.username = :#{#user.username} ")
@Query(value = "select u from #{#entityName} u where u.username = ?#{#user.username} ")
User findUserByUsernameWithSpel(User user);

2、@Modifying与派生delete

  2.1、@Modifying源码

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 指示应将查询方法视为修改查询,因为这会更改执行查询的方式。
* 只有在使用@Query注解定义的查询方法上才考虑使用此注释。
* 它不应该应用与自定义实现方法或从方法名派生的查询,因为它们已经控制了底层数据库访问API,或者指定是否按名称进行修改。
*
* 需要添加@Modifying注解的包括 INSERT、UPDATE、DELETE、DDL语句。
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
public @interface Modifying { /**
* 定义是否应在执行修改查询之前刷新基础持久性上下文。
*/
boolean flushAutomatically() default false; /**
* 定义是否应在执行修改查询后清除基础持久性上下文。
*/
boolean clearAutomatically() default false;
}

  2.2、对于@Query执行INSERT、UPDATE、DELETE、DDL语句时,需要添加@Modifying注解,来改变查询方式。

  2.3、@Modifying只有在使用@Query注解时,才考虑使用,对于自定义实现查询和方法名派生的查询则不需要。

  2.4、由于EntityManager在执行修改查询后可能包含过时的实体,因此我们不会自动清除它,因为这实际上会删除EntityManager中仍挂起的所有为刷新的修改。如果希望自动清除EntityManager,将clearAutomatically设置为true。

  2.5、Spring-Data-JPA还支持派生的delete查询,这样可以避免显式声明JPQL查询。

  2.6、派生delete查询会先执行select在执行delete,使用@Query和@Modifying直接执行delete。

  2.7、派生delete会触发@PreRemove,而@Query和@Modifying则不会。

源码地址:https://github.com/caofanqi/study-spring-data-jpa

学习Spring-Data-Jpa(十)---注解式方法查询之@Query、@Modifying与派生delete的更多相关文章

  1. Spring Data Jpa (四)注解式查询方法

    详细讲解声明式的查询方法 1 @Query详解 使用命名查询为实体声明查询是一种有效的方法,对于少量查询很有效.一般只需要关心@Query里面的value和nativeQuery的值.使用声明式JPQ ...

  2. SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法

    软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...

  3. 学习Spring Data JPA

    简介 Spring Data 是spring的一个子项目,在官网上是这样解释的: Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型,同时仍然保留底层数据存储的特​​殊 ...

  4. 学习-spring data jpa

    spring data jpa对照表 Keyword Sample JPQL snippet And findByLastnameAndFirstname - where x.lastname = ? ...

  5. 使用Spring Data JPA的Specification构建数据库查询

    Spring Data JPA最为优秀的特性就是可以通过自定义方法名称生成查询来轻松创建查询SQL.Spring Data JPA提供了一个Repository编程模型,最简单的方式就是通过扩展Jpa ...

  6. spring data jpa 使用JPQL的方式查询

    用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询 @Que ...

  7. Spring Data JPA 复杂/多条件组合查询

    1: 编写DAO类或接口  dao类/接口 需继承 public interface JpaSpecificationExecutor<T> 接口: 如果需要分页,还可继承 public ...

  8. Spring Data JPA 实现多表关联查询

    本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...

  9. Spring Data JPA 常用注解 @Query、@NamedQuery

    1.@Transient @Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性:如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则ORM框架 ...

随机推荐

  1. C++ 工程师养成 每日一题fourth (reverse的使用)

    题目: 将一句话的单词进行倒置,标点不倒置. 这道题最简单的解法是使用algorithm提供的reverse()函数 具体步骤我写在代码注释里面: #include <string> #i ...

  2. 关于elasticsearch使用G1垃圾回收替换CMS

    最近ES集群数据节点经常出现jvm占用过高,频繁GC导致ES集群卡死,很长时间才恢复.在网上看到用G1垃圾回收可以改善这一情况,但都是老版本的ES,我们现在使用的版本是5.5.2,所以想问问各位5.5 ...

  3. cas sso 单点登录

    一些介绍: https://www.jianshu.com/p/b7de8e4cf217 https://blog.csdn.net/javaloveiphone/article/details/52 ...

  4. 阿里巴巴 Java 开发手册 (九) 异常日志

    (一) 异常处理 1. [强制]Java 类库中定义的一类 RuntimeException 可以通过预先检查进行规避,而不应该 通过 catch 来处理,比如:IndexOutOfBoundsExc ...

  5. 2019 讯飞java面试笔试题 (含面试题解析)

      本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.讯飞等公司offer,岗位是Java后端开发,因为发展原因最终选择去了讯飞,入职一年时间了,也成为了面试官,之 ...

  6. 【转载】C#中使用double.TryParse方法将字符串转换为double类型

    在C#编程过程中,将字符串string转换为double类型过程中,时常使用double.Parse方法,但double.Parse在无法转换的时候,会抛出程序异常,其实还有个double.TryPa ...

  7. Django:内置组件Content-Type

    12.Django组件之Content_Type 1.帮助我们生成了一张表,里面有所有表名.这样不再自建表在表中填表名,用Foreignkey获取 2.为了让我们快速进入插入数据,填写一个字段Gene ...

  8. Django:基于调试组插件go-debug-toolbar

    1.django-debug-toolbar 介绍 django-debug-toolbar 是一组可配置的面板,可显示有关当前请求/响应的各种调试信息,并在单击时显示有关面板内容的更多详细信息.返回 ...

  9. 木马防杀 花指令 OllyDbg

    打开木马 入口地址 添加花指令 全0的地方,可以插入花指令 保存为可执行文件 随便选择几行,右击 保存文件

  10. IDEA 阿里巴巴代码规范检查插件

    1.问题概要 大家都想写出规范的代码,可规范的标准是什么勒,估计每个人心中的标准都不是完全一致的 在分工合作越来越精细化的时代,我们需要一个最大程度接近公认的规范,这里我们以阿里巴巴的代码规范作为参考 ...