1 @Query注解详解及其用法
说明:本文的写作构建在笔者的SpringBoot之路(二)使用用Spring-Data-JPA访问数据库进行基本的CRUD操作这篇文章的基础之上。
@Query注解在spring-data-jpa中可用来定制自定义sql语句的数据库增删改查操作,使用起来也是非常方便
1.1 源码分析
@Retention(RetentionPolicy.RUNTIME)
1.2 @Query的用法
使用命名查询为实体声明查询是一种有效的方法,对于少量查询 很有效;一般只需要关心@Query里面的value和nativeQuery的值;使 用声明式JPQL查询有一个好处,就是启动的时候就知道语法正确与否。
示例:声名一个@Query注解装饰的方法在UserRepository上
(1)@Query注解中自定义select语句
@Query(value=
(2)@Query注解中自定义update语句
@Modifying
注意:update操作必须在@Query注解上方加上@Modifying注解,否则执行sql语句时会报无法提取结果集异常
1.3 like查询,注意不会自动加上%关键字
@Query(value=
注意: nativeQuery不支持直接Sort的参数查询
nativeQuery的错误写法,下面这个写法会使项目启动时报错:
@Query(value=
1.4 本节测试
以1.2中的在@Query注解中自定义update语句为例,完成Service层和Controller层代码
Service层代码
//UserService 接口中添加抽象方法
controller层代码
@PutMapping(
这时就可以通过postman测试效果了
// 请求方法与URL
从返回结果的email字段可发现用户的邮箱已经发生了改变
nativeQuery排序的正确写法
@Query(value=
使用@Query注解查询时的不足就是不支持动态条件查询
2 javax.persistence概况介绍
虽然Spring Data JPA已经帮我们对数据的操作封装得很好了, 约定大于配置思想,帮我们默认了很多东西。JPA(Java持久性API) 是存储业务实体关联的实体来源。它显示了如何定义一个面向普通 Java对象(POJO)作为一个实体,以及如何与管理关系实体提供一套 标准。因此,javax.persistence下面的有些注解还是必须要去了解的,以便于更好地提高工作效率.
(1)javax.persistence位于org.hibernate:hibernate-core.jar包里面,可 以通过Intellij Idea的maven插件直接分析一下maven的依赖,也可以用 $ mvn dependency:tree分析,例如:
[INFO] | +- org.hibernate:hibernate-core:jar:5.3.9.Final:compile
[INFO] | | +- javax.persistence:javax.persistence-api:jar:2.2:compile
[INFO] | | +- org.javassist:javassist:jar:3.23.1-GA:compile
[INFO] | | +- antlr:antlr:jar:2.7.7:compile
[INFO] | | +- org.jboss:jandex:jar:2.0.5.Final:compile
[INFO] | | +- org.dom4j:dom4j:jar:2.1.1:compile
[INFO] | | \- org.hibernate.common:hibernate-commons-annotations:jar:5.0.4.Final:compile
[INFO] | +- org.springframework.data:spring-data-jpa:jar:2.1.6.RELEASE:compile
[INFO] | | +- org.springframework.data:spring-data-commons:jar:2.1.6.RELEASE:compile
[INFO] | | +- org.springframework:spring-orm:jar:5.1.6.RELEASE:compile
[INFO] | | \- org.springframework:spring-tx:jar:5.1.6.RELEASE:compile
[INFO] | \- org.springframework:spring-aspects:jar:5.1.6.RELEASE:compile
(2)通过Intellij Idea的Diagram来看一下此模块类的关键关 系,如图所示:
(3)下图显示了JPA的类的层次结构,包括核心类和JPA接口。
(4)JPA类层次结构的显示单元
单元 | 描述 |
---|---|
EntityManagerFactory | 一个EntityManager的工厂类,创建并管理多个EntityManager实例 |
EntityManager | 一个接口,管理持久化操作对象,工作原理类似工厂的查询实例 |
Entity | 实体是持久性对象,是存储在数据库中的记录 |
EntityTransaction | 与EntityManager是一对一的关系,对于每个EntityManager,操作是由EntityTransaction来维护的 |
Persistence | 这个类包含使用静态方法获取EntityManagerFactory的实例 |
Query | 该接口由每个JPA供应商实现,能够获得符合标准的关系对象 |
上述的类和接口用于存储实体到数据库的一个记录,帮助程序员 通过减少自己编写的代码将数据存储到数据库中,使他们能够专注于 更重要的业务活动代码,如数据库表映射的类编写代码。
3 javax.persistence包下的基本注解
javax.persistence包下基本注解包括@Entity、@Table、@Id、@IdClass、 @GeneratedValue、@Basic、@Transient、@Column、@Temporal、 @Enumerated、@Lob。
3.1 @Entity
先看一个Blog的示例,其中实体的配置如下:
@Entity
在数据库中新建一张与UserBlogEntity实体类对应的表:
use mysql;
@Entity定义对象将会成为被JPA管理的实体,将映射到指定的数据库表。
200),
@Entity定义对象将会成为被JPA管理的实体,将映射到指定的数 据库表。
public
3.2 @Table
@Table指定数据库的表名
public @interface Table {
//表名,可选,不填写时默认为实体类名
String name() default "";
//表的catalog
String catalog() default "";
//表所在的schema
String schema() default "";
//唯一约束,只有在创建表时有用,默认不需要
UniqueConstraint[] uniqueConstraints() default {};
//索引,只在创建表时有用,默认不需要
Index[] indexes() default {};
}
3.3 @Id
@Id定义属性为数据库的主键,一个实体里面必须有一个
3.4 @IdClass
@IdClass利用外部类的联合主键
public
作为符合主键类,要满足以下几点要求。
必须实现Serializable接口。
必须有默认的public无参数的构造方法。
必须覆盖equals和hashCode方法。equals方法用于判断两个对 象是否相同,EntityManger通过find方法来查找Entity时是根 据equals的返回值来判断的。在本例中,只有对象的name和 email值完全相同或同一个对象时才返回true,否则返回 false。hashCode方法返回当前对象的哈希码,生成的hashCode相同的概率越小越好,算法可以进行优化。
用法:假设UserBlog的联合主键是createUserId和title,新增一个UserBlogKey的类,UserBlogkey.class代码如下:
@NoArgsConstructor
UserBlogRepository中的改动如下:
public
@IdClass的用法实战
(1)先写两个往User_Blog表中插入数据的接口和一个根据Id查询UserBlogEntity的接口
service层代码 UserBlogService.java
public
UserBlogServiceImpl.java
@Service
controller层代码 UserBlogController.java
@RestController
postman测试
(1) 插入一条数据
//请求方法与URL
(2)批量插入多条数据
//请求方法与URL
(3) 使用联合主键查询单条记录
//请求方法与URL
3.5 @GeneratedValue
@GeneratedValue为主键生成策略,例如:
public
GenerationType一共有以下4个值:
public
3.6 @Basic@Basic表示属性是到数据库表的字段的映射。如果实体的字段上 没有任何注解,默认即为@Basic。
public
3.7 @Transient
@Transient表示该属性并非一个到数据库表的字段的映射,表示 非持久化属性;
与@Basic作用相反。JPA映射数据库的时候忽略它。
3.8 @Column
@Column定义该属性对应数据库中的列名
public
3.9 @Temporal
@Temporal用来设置Date类型的属性映射到对应精度的字段。
(1)@Temporal(TemporalType.DATE)映射为日期∥date(只有 日期)
(2)@Temporal(TemporalType.TIME)映射为日期∥time(只有 时间)
(3)@Temporal(TemporalType.TIMESTAMP)映射为日期∥date time(日期+时间)
3.10 @Enumerated
@Enumerated很好用,直接映射enum枚举类型的字段。
(1) 看源码:
public
(2) 用法:
public
这时插入两条数据,数据库里面的值是MAIL/FMAIL,而不是“男 性”/“女性”。如果我们用@Enumerated(EnumType.ORDINAL),那么 这时数据库里面的值是0,1。但是实际工作中,不建议用数字下标,
因为枚举里面的属性值是会不断新增的,如果新增一个,位置变化了 就惨了。
3.11 @Lob
@Lob 将属性映射成数据库支持的大对象类型,支持以下两种数 据库类型的字段。
(1)Clob(Character Large Ojects)类型是长字符串类型, java.sql.Clob、Character[]、char[]和String将被映射为Clob类 型
(2)Blob(Binary Large Objects)类型是字节类型, java.sql.Blob、Byte[]、byte[]和实现了Serializable接口的类型 将被映射为Blob类型。
(3)Clob、Blob占用内存空间较大,一般配合 @Basic(fetch=FetchType.LAZY)将其设置为延迟加载。
小结
本文主要结合源码和测试用例系统地讲解了spring-data-jpa中@Query
注解及javax.persistence包下的基本注解及其用法和需要注意的坑,限于文章篇幅,关于联表查询的注解及其使用和JpoRepository扩展详解及高级查询用法将放在下一遍文章中完成。
推荐阅读
SpringBoot之路(二)使用用Spring-Data-JPA访问数据库进行基本的CRUD操作
参考文档
:张振华著《Spring Data Jpa从入门到精通》第4章和第5章部分
欢迎新读者扫描下方二维码关注笔者的微信公众号
需要源码的读者在公众号中发送消息“bootDemo项目源码”即可获取该项目源码地址