前面第一篇介绍了常见 orm 框架,第二篇介绍了 spring-jpa 概念和配置,本文会介绍具体 jpa 的使用。
定义实体类
我们先定义实体类 Customer
Customer 类添加的 @Entity 标注表明 Customer 对象将会成为被 JPA 管理的实体;id 属性上的 @Id 和 @GeneratedValue(strategy=GenerationType.AUTO) 标注表明 id 是一个自增的 主键 。
通过定义的实体,jpa 就能够自动生成创建 Schema 的 DDL,它们将会在应用启动时初始化数据库,这个可以通过配置 spring.jpa.generate-ddl = false
来关闭,关于 spring.jpa
的配置,更过的可以看 JpaProperties
类:
然后在 JpaBaseConfiguration
中会去使用这些个配置:
我们可以看到 spring.jpa.generate-ddl
的配置是在 JpaVendorAdapter 中使用,那 JpaVendorAdapter 作用是什么呢?
jpaVendorAdapter:用于设置 JPA 实现厂商的特定属性,如设置 hibernate 的是否自动生成 DDL 的属性 generateDdl,这些属性是厂商特定的,因此最好在这里设置。目前 spring 提供 Hibernate JpaVendorAdapter,OpenJpaVendorAdapter,EclipseJpaVendorAdapter,TopLinkJpaVenderAdapter 四个实现。其中最主要的属性是 “database”,用来指定使用的数据库类型。从而根据数据库类型决定如何将数据库特定异常转换为 Spring 一致性异常。目前支持以下数据库:DB2,DERBY,H2,H sql , INFORMIX , MySQL ,ORACLE,POSTGRESQL,SQL_SERVER,SYBASE.
如果使用 Hibernate 作为 JPA 的实现,spring.jpa.hibernate.ddl-auto 给出了一种更加细粒度的配置方式,其中包括以下几种取值:
-
none:如果是非内嵌数据库,默认为 none,即做任何 DDL 操作
-
validate:验证数据库 Schema 是否和实体类一致
-
update:更新数据库 Schema
-
create:创建数据库 Schema
-
create-drop:如果是内嵌数据库(hsqldb、h2 和 derby),则会默认设置为这个选项;应用结束后会删除 Schema
在实际生产环境中,一般不会使用内嵌数据库,因此不用专门配置,即默认为 none 即可。数据库 DDL 通常是通过类似 Mybatis Migration 和 Flyway 等数据库版本管理工具来维护的。
下面介绍一些常用的注释。
@Entity 和 @Table
@Entity 标注用于实体类,表明该 Java 类为实体类,将映射到指定的数据库表。
当实体类与其映射的数据库表名不同名时,需要使用 @Table 标注说明。@Table 标注与 @Entity 标注一同使用,置于实体类声明语句之前。
@Table 标注的常用属性是 name,用于指明数据库的表名。
@Id 和 @GeneratedValue
@Id 标注应用于实体类的属性或者属性对应的 getter 方法,表示该属性映射为数据库表的主键。
@GeneratedValue 与 @Id 一同使用,表示主键的生成策略,通过 strategy 属性指定。
在 javax.persistence.GenerationType 中定义了以下几种可供选择的策略:
-
IDENTITY:采用数据库 ID 自增长的方式产生主键,Oracle 不支持这种方式。
-
AUTO: JPA 自动选择合适的策略,是默认选项。
-
SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 标注指定序列名,MySQL 不支持这种方式。
-
TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略更易于做数据库移植。
@Basic
@Basic 表示属性到数据库表的字段的映射。一个 @Entity 标注的实体类中的属性,如果没有任何标注,默认即为 @Basic,比如 Customer
类中的 firstName 和 lastName 属性。
@Basic 有两个常用属性:
-
fetch: 表示实体类对应属性的读取策略,有 EAGER 和 LAZY 两种取值,分别表示主动抓取和延迟加载,默认值为 EAGER。
-
optional: 表示该属性是否允许为 null,默认值为 true。
比如 customer 表的 lastName 字段不能为空,可以如下设置:
@Column
当实体类的属性与其映射的数据库表的列名称不同时,可以使用 @Colunm 标注。该标注描述了数据库表中该字段的详细定义,包含以下属性:
-
name:表示数据库表中该字段的名称,默认情形属性名称一致。
-
nullable:表示该字段是否允许为 null,默认为 true。
-
unique:表示该字段是否是唯一标识,默认为 false。
-
length:表示该字段的大小,仅对 String 类型的字段有效。
-
insertable:表示在 ORM 框架执行插入操作时,该字段是否应出现 INSETRT 语句中,默认为 true。
-
updateable:表示在 ORM 框架执行更新操作时,该字段是否应该出现在 UPDATE 语句中,默认为 true。对于一经创建就不能更改的字段,该属性非常有用,比如 email 属性。
-
columnDefinition:表示该字段在数据库中的实际类型。通常 ORM 框架可以根据属性类型自动判断数据库中字段的类型,但是依然有些例外:
-
Date 类型无法确定数据库中字段类型究竟是 DATE、TIME 还是 TIMESTAMP
-
String 的默认映射类型为 VARCHAR,如果希望将 String 类型映射到特定数据库的 Blob 或 TEXT 字段类型,则需要进行设置
@Transient
@Transient 表示该属性并非一个到数据库表的字段的映射,JPA 将忽略该属性。
如果一个属性并非数据库表的字段映射,就务必将其标注为 @Transient,否则 JPA 默认其注解为 @Basic。
@Temporal
在数据库中,表示时间类型的数据有 DATE,TIME,和 TIMESTAMP 三种精度 (即单纯的日期,时间,或者两者兼备)。 可使用 @Temporal 来设置 Date 类型的属性映射到对应精度的字段。
-
@Temporal(TemporalType.DATE) 映射为日期 // date (只有日期)
-
@Temporal(TemporalType.TIME) 映射为日期 // time (是有时间)
-
@Temporal(TemporalType.TIMESTAMP) 映射为日期 // date time (日期 + 时间)
@Lob
@Lob 标注表示将属性映射成数据库支持的大对象类型,Clob 或者 Blog。其中:
-
Clob(Character Large Ojects)类型是长字符串类型,java.sql.Clob、Character[]、char[] 和 String 将被映射为 Clob 类型。
-
Blob(Binary Large Objects)类型是字节类型,java.sql.Blob、Byte[]、byte[] 和 实现了 Serializable 接口的类型将被映射为 Blob 类型。
因为这两种类型的数据一般占用的内存空间比较大,所以通常使用延迟加载的方式,与 @Basic 标注同时使用,设置加载方式为 FetchType.LAZY。
一般情况下并不需要太多的设置,其默认行为足以处理大部分场景,这也是约定大于配置的原则。
可置于属性的标注都能置于对应的 getter,一般情况下应该在整个项目中使用其中一种方式,而不能混用。
更多 Entity 的标注,参考 Hibernate 文档
参考自 Spring Data JPA 实战入门训练。
这是 jpa 的第三篇,你的鼓励是我继续写下去的动力,期待我们共同进步。