Spring Boot 整合——JPA 数据模型定义(数据实体基础注解使用)

2023年 3月 6日 43点热度 0人点赞

JPA

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

JPA介绍

JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易地掌握。JPA基于非侵入式原则设计,因此可以很容易地和其它框架或者容器集成。

JPA、hibernate、Spring Data JPA的关系

  • JPA是一套ORM规范,而hibernate是JPA的一个子集,也是JPA的实现。
  • Spring Data JPA也是JPA的实现,底层使用了hibernate的技术实现。

JPA基本功能

JPA提供了下面功能那个

  • ORM数据映射。
  • JPA的API
  • 查询语句JPQL

Spring Data JPA除了提供便捷的CRUD操作之外,它还提供了根据entity的内容去创建表结构的功能。

JPA和Mybatis优劣

这一段内容是我个人主观的想法,并不意味着是正确的

JPA
JPA是一个非常在意数据结构设置的ORM框架,所以使用起来会发现JPA提供了大量entity的注解,基本覆盖了各种使用场景。

但是和Mybatis相比JPA的复杂查询就显得比较繁琐。基础查询、以及设计时候就存在的多表关系的关联查询以外,
想去根据业务自己实现一些之前设计没有考虑的复杂关系查询时就显得非常麻烦。
所以这也要求在设计数据关系的时候尽可能的考虑更多的场景,以及保留足够的扩展空间。

Mybatis
和JPA相比,Mybatis显得就很简单的。因为所有查询都是基于SQL。并不存在将数据关系通过entity进行维护。
省去了繁琐的entity定义,显得整个数据定义轻松很多。但是这样要保证数据操作正确,查询效率就需要SQL编写者有一定SQL水平。

总结
所以个人的感觉JPA更倾向于在最开始就保证设计的可靠性,需要设计数据结构的开发者有较高的水平。
而Mybatis却比较轻设计,整个系统的可靠性被其中一个一个SQL语句共同来保证。这就要求参与SQL编写的开发者们有一定基础。

引入Spring Data JPA

在Spring Boot中引入Spring Data JPA非常简单,只需要添加其依赖starter-data-jpa即可

maven依赖

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
    </dependencies>

基本参数配置

spring:
  application:
    name: jpa
  datasource:
    url: jdbc:mysql://localhost:3306/test?charactorEncoding=utf-8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  jpa:
    hibernate:
      use-new-id-generator-mappings: true
      ddl-auto: none
      naming:
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
    properties: 
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
        hbm2ddl:
                # create 每次加载hibernate时都会删除上一次的生成的表,
                # 然后根据你的model类再重新来生成新表
                # create-drop 每次加载hibernate时根据model类生成表
                # 但是sessionFactory一关闭,表就自动删除
                # update 第一次加载hibernate时根据model类会自动建立起表的结构
                # 以后加载hibernate时根据model类自动更新表结构
                # validate 每次加载hibernate时,验证创建数据库表结构
                # 只会和数据库中的表进行比较,不会创建新表,但是会插入新值
          auto: update
          # 使用create-drop存在问题,jpa会执行一些外键SQL但是此时表不存在而报错
          # auto: create-drop
    show-sql: true
server:
  port: 8000

连接配置
JPA在和数据库进行连接的时候会根据spring.jpa.properties.hibernate.auto配置执行不同的连接策略。
其包含的策略

参数 作用
create 每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表
create-drop 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除
update 第一次加载hibernate时根据model类会自动建立起表的结构,以后加载hibernate时根据model类自动更新表结构
validate 每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值

使用JPA定义表结构

基础的操作

要想使用过JPA创建一个表只需要配置@entity和@Table注解,
当然单独使用@entity的时候系统也会创建表。但是一般我们习惯用@entity标注其为实体。
@Table来标志其对应表映射内容。

@Data
@Entity
@Table(name = "cascade_user_tag")
public class UserTag {

    @Id
    @GeneratedValue
    private Long id;
    
    private String tagName;
}

上面的内容我们会创建一个cascade_user_tag的表,其中id为主键,存在列tag_name

entity表的配置

JPA提供了对数据表的参数配置,除了普通的表参数定义,JPA使用@Inheritance提供了继承关系的表定义。

@Inheritance
@Inheritance内部参数为InheritanceType类型,其类型主要有下面几种

名称 作用
SINGLE_TABLE 表示存在继承关系的时候,子类和父类部分保存在一张表中
TABLE_PER_CLASS 表示存在继承关系的时候,子类和父类不在一张表中且全字段
JOINED 表示存在继承关系的时候,子类部分和父类部分保存在不同的表中

SINGLE_TABLE

TABLE_PER_CLASS、JOINED这两种有兴趣可以看下我项目中的例子,这里主要讲下SINGLE_TABLE。
使用SINGLE_TABLE的时候可以指定DiscriminatorColumn可以指定一个列用来标识不同实体的类别。
比如下面的例子,使用table_type作为标记不同实体的字段,其中父类使用base标记子类使用son进行标记。

@Data
@Entity
@Table(name = "ext_base_single_bean")
/**继承策略,
 * SINGLE_TABLE表示继承关系的实体保存在一个表;
 * JOINED表示每个实体子类部分保存在各自的表;
 * TABLE_PER_CLASS表示每个实体类保存在各自的表*/
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
/**标识字段定义*/
@DiscriminatorColumn(name="table_type",
        discriminatorType = DiscriminatorType.STRING)
@DiscriminatorOptions(force=true)
/**该类的标识*/
@DiscriminatorValue("base")
public class BaseSingleBean {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Basic
    private BigDecimal balance;

}

@Data
@Entity
@DiscriminatorValue("son")
public class SonSingleBean extends BaseSingleBean {

    @Basic
    private String sonValue;
}

entity列的配置

主键列

定义主键使用下面注解

@Id
@GeneratedValue(strategy = GenerationType.AUTO)

其中@GeneratedValue用来标识使用何种主键创建器。主要提供了下面几种

名称 作用
TABLE 使用数据库中一张表作为主键表
SEQUENCE 使用数据库序列作为主键,某些数据库中,不支持主键自增长,比如Oracle,其提供了一种叫做"序列(sequence)"的机制生成主键
IDENTITY 此种主键生成策略就是通常所说的主键自增长,数据库在插入数据时,会自动给主键赋值
AUTO 使用默认方法,系统会根据以上三种主键生成策略中选择其中一种

TABLE

IDENTITY和SEQUENCE都是使用自增或者类似自增的方式进行主键创建这个这里不谈,介绍下TABLE

@Data
@Entity
@Table(name = "base_key_table_bean")
public class KeyTableBean {

    /**
     * 使用生产方式。GenerationType
     * GenerationType
     * TABLE:容器指定用底层的数据表确保唯一,主键的值每次都是从指定的表中查询来获得
     * SEQUENCE:使用数据库德SEQUENCE列保证唯一(Oracle数据库通过序列来生成唯一ID)
     * IDENTITY:使用数据库的IDENTITY列保证唯一
     * AUTO:由容器挑选一个合适的方式来保证唯一
     * NONE:容器不负责主键的生成,由程序来完成
     */
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,generator = "table_key")
    @TableGenerator(name = "table_key",// 主键生成策略的名称
            table = "key_my_gen",// 生成策略所持久化的表名
            pkColumnName = "key_name",// 在持久化表中,该主键生成策略所对应键值的名称
            valueColumnName = "key_value",// 在持久化表中,该主键当前所生成的值
            pkColumnValue = "TABLE_KEY",// 生产策略所对应的主键的值
            initialValue = 10,// 初始值
            allocationSize = 1)// 每次主键值增加的大小
    private Long id;

    private String value;
}

使用GenerationType.TABLE我们需要配合@TableGenerator。
上面的例子中,我们设置了主键生成器名称为table_key,主键表为key_my_gen,
指定了主键名称对应列key_name且名称被指定为TABLE_KEY,指定了主键值对应列key_value。

根据上面配置,我们执行以下测试,系统会在key_my_gen表中生产key_name值为TABLE_KEY,key_value为数据主键的内容。

    @Test
    public void add() {
        KeyTableBean bean = new KeyTableBean();
        bean.setValue("value");
        repository.save(bean);
    }
联合主键

有些时候我们所使用的主键不是一个单一的参数,可能是多个字段组成的一个主键。
面对这样的场景我们可以使用@IdClass这样的注解。它允许我们将一个类设置为主键

@Data
@Entity
@Table(name = "key_test_keys")
@IdClass(value = KeysBean.class)
public class TestKeys implements Serializable {
    @Id
    private long id;
    @Id
    private String keyId;
    
    private String value;
}

主键类。

@Data
public class KeysBean implements Serializable {

    private long id;

    private String keyId;
}

此时数据库中创建的key_test_keys表中就会出现两个字段都是主键

普通列

当然除了主键、JPA对列的更多的配置在@Column中。JPA为@Column提供了众多参数

注解 参数 描述
@Column Column元数据定义了映射到数据库的列的所有属性
name 列的名称
unique 指明此列为值唯一的列,此约束适用除了主键映射和到表级别指定的约束
nullable 此列是否为空
insertable 此列是否包含在新增SQL中
updatable 此列是否包含在更新SQL中
columnDefinition 为列生成DDL时使用的SQL片段
table 包含列的表的名称
length 字段的长度,只有在String类型中使用
precision 字段的精度,只有在decimal类型中使用
scale 字段的小数点位数,只有在decimal类型中使用
@Basic 实体类的属性声明语句之前,或属性的getter()方法之前,标注将实体类的当前属性映射到数据库表的字段,对于没有任何标注的getter()方法,默认即为@Basic
fetch 定义字段或属性的值是应延迟加载还是必须立即获取
optional 定义字段或属性的值是否可以为空。这是一个提示。如果未指定,则默认为true。
@Transient @Transient表示该属性并不是一个到数据库表的字段的映射,指定的这些属性不会被持久化,ORM框架将忽略该属性
@Temporal 格式化时间日期,页面直接得到格式化类型的值
value 日期生成格式使用,java.util.Date 或者 java.util.Calendar
@UniqueConstraint UniqueConstraint定义在Table或SecondaryTable元数据里,用来指定建表时需要建唯一约束的列
name 约束的名称
columnNames 列名称的集合
@DiscriminatorColumn DiscriminatorColumn定义在使用SINGLE_TABLE或JOINED继承策略的表中区别不继承层次的列
columnDefinition 为鉴别器列生成DDL时使用的SQL片段。默认为提供程序生成的SQL以创建指定的鉴别器类型的列
discriminatorType 用作类鉴别器的对象/列的类型
length 基于字符串的鉴别器类型的列长度。不适用其他类型
name 要用于鉴别器的列的名称
@Lob Lob指定一个属性作为数据库支持的大对象类型在数据库中存储。使用LobType这个枚举来定义Lob是二进制类型还是字符类型

配置列参数
下面的demo中是对列名称和参数长度进行配置的内容。

    @Column(name = "value",// 映射的列名
            unique = true,// 是否唯一
            nullable = false,// 是否允许为空
            length = 150,// 对于字符型列,length属性指定列的最大字符长度
            insertable = true,// 是否允许插入
            updatable = false)// 是否允许更新
    private String value;

    @Column(name = "null_value",// 映射的列名
            unique = false,// 是否唯一
            nullable = true,// 是否允许为空
            length = 150,// 对于字符型列,length属性指定列的最大字符长度
            insertable = false,// 是否允许插入
            updatable = false)// 是否允许更新
    private String nullValue;

指定属性非数据库列

JPA提供了@Transient基本操作来标识此字段在数据表中不存在。

    /**
     * 用来测试Transient
     */
    @Transient
    private String notInTable;

时间格式化

通过@Temporal我们可以实现Date的格式化设置。

@Temporal内部参数TemporalType提供了三种类型,分别对应三种数据结构

类型 对应数据结构
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp
    @Temporal(TemporalType.TIMESTAMP)
    public Date getCreateTime() {
        return createTime;
    }

当然实际上JPA为了让数据设计的时候尽可呢的满足各种使用场景提供了各种注解。下面是目前常用的注解。
这里只介绍了一些基础实体定义的注解,关于多表关联的内容,会在后续找时间继续补充完成。

本篇文章涉及的源码下载地址:https://gitee.com/daifyutils/springboot-samples

jpa常用注解

都在 : javax.persistence.* 包下面

类数据注解

注解 参数 描述
@Entity 说明这个class是实体类
name 实体名称,默认使用实体的类名,不能使用java保留名
@Table Table用来定义entity主表的name,catalog,schema等属性
name 表的名称。默认为实体名称。
catalog 表的目录
schema 表空间
uniqueConstraints 表上的唯一约束。这些仅在表生成生效时使用。这些约束除了适用于由主键映射所需的列和joincolumn
indexes 表的索引。这些仅在表生成生效时使用。默认为无其他索引
@Column Column元数据定义了映射到数据库的列的所有属性
name 列的名称
unique 指明此列为值唯一的列,此约束适用除了主键映射和到表级别指定的约束
nullable 此列是否为空
insertable 此列是否包含在新增SQL中
updatable 此列是否包含在更新SQL中
columnDefinition 为列生成DDL时使用的SQL片段
table 包含列的表的名称
length 字段的长度,只有在String类型中使用
precision 字段的精度,只有在decimal类型中使用
scale 字段的小数点位数,只有在decimal类型中使用
@Basic 实体类的属性声明语句之前,或属性的getter()方法之前,标注将实体类的当前属性映射到数据库表的字段,对于没有任何标注的getter()方法,默认即为@Basic
fetch 定义字段或属性的值是应延迟加载还是必须立即获取
optional 定义字段或属性的值是否可以为空。这是一个提示。如果未指定,则默认为true。
@Transient @Transient表示该属性并不是一个到数据库表的字段的映射,指定的这些属性不会被持久化,ORM框架将忽略该属性
@Temporal 格式化时间日期,页面直接得到格式化类型的值
value 日期生成格式使用,java.util.Date 或者 java.util.Calendar
@UniqueConstraint UniqueConstraint定义在Table或SecondaryTable元数据里,用来指定建表时需要建唯一约束的列
name 约束的名称
columnNames 列名称的集合
@DiscriminatorColumn DiscriminatorColumn定义在使用SINGLE_TABLE或JOINED继承策略的表中区别不继承层次的列
columnDefinition 为鉴别器列生成DDL时使用的SQL片段。默认为提供程序生成的SQL以创建指定的鉴别器类型的列
discriminatorType 用作类鉴别器的对象/列的类型
length 基于字符串的鉴别器类型的列长度。不适用其他类型
name 要用于鉴别器的列的名称
@Lob Lob指定一个属性作为数据库支持的大对象类型在数据库中存储。使用LobType这个枚举来定义Lob是二进制类型还是字符类型

主键

注解 参数 描述
@Id 声明当前field为映射表中的主键列
@GeneratedValue 为一个实体生成一个唯一标识的主键
strategy 主键生产策略:GenerationType
generator 主键生成器的名称
@Table Table用来定义entity主表的name,catalog,schema等属性
name 表的名称。默认为实体名称。
catalog 表的目录
schema 表空间
uniqueConstraints 表上的唯一约束。这些仅在表生成生效时使用。这些约束除了适用于由主键映射所需的列和joincolumn
indexes 表的索引。这些仅在表生成生效时使用。默认为无其他索引
@TableGenerator TableGenerator定义一个主键值生成器,在Id这个元数据的generate=TABLE时,generator属性中可以使用生成器的名字
name name属性表示该表主键生成策略的名称
table 表示表生成策略所持久化的表名
catalog 表所在的目录名或是数据库名
schema 表空间
pkColumnName 该主键生成策略所对应键值的名称
valueColumnName 该主键当前所生成的值,它的值将会随着每次创建累加
pkColumnValue 在持久化表中,该生成策略所对应的主键
initialValue 初始值
allocationSize 每次主键值增加的大小
uniqueConstraints 表上的唯一约束。这些仅在表生成生效时使用。这些约束除了适用于由主键映射所需的列和joincolumn
indexes 表的索引。这些仅在表生成生效时使用。默认为无其他索引
@IdClass 当entity class使用复合主键时,需要定义一个类作为id class。
value 联合组件对应的类
@SequenceGenerator SequenceGenerator定义一个主键值生成器,在Id这个元数据的generator属性中可以使用生成器的名字。生成器可以在类、方法或者属性上定义。生成器是数据库支持的sequence对象。
allocationSize 从序列分配序列号时要增加的量
catalog 主键生成器的目录
initialValue 主键开始的值
name 主键生成器的名称
schema 主键生成器的空间
sequenceName 要从中获取主键值的数据库序列对象的名称
@Version Version指定实体类在乐观事务中的version属性。在实体类重新由EntityManager管理并且加入到乐观事务中时,保证完整性。每一个类只能有一个属性被指定为version,version属性应该映射到实体类的主表上。

多数据关联

注解 参数 描述
@OneToOne 描述一个 一对一的关联
targetEntity 目标类的实体
cascade 关联到目标的操作
fetch 是否使用延迟加载
mappedBy 反向关联
optional 关联是否可选。如果设置为false,则非空关系必须始终存在
orphanRemoval 将移除操作级联到关联的实体中
@ManyToOne 一个多对一的映射,该注解标注的属性通常是数据库表的外键.
targetEntity 目标类的实体
cascade 关联到目标的操作
fetch 是否使用延迟加载
optional 关联是否可选。如果设置为false,则非空关系必须始终存在
@OneToMany 一个 一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段
targetEntity 目标类的实体
cascade 关联到目标的操作
fetch 是否使用延迟加载
mappedBy 反向关联
orphanRemoval 讲移除操作级联到关联的实体中
@ManyToMany 描述一个多对多的关联.多对多关联上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理
targetEntity 目标类的实体
cascade 关联到目标的操作
fetch 是否使用延迟加载
mappedBy 反向关联
@JoinColumn 我们通过JoinColumn来定义关系的属性。JoinColumn的大部分属性和Column类似。
name 列的外键名称
referencedColumnName 外键列的引用名称
unique 此列值唯一
nullable 是否非空
columnDefinition 为列生成DDL时使用的SQL片段
foreignKey 联接列的外键约束
table 包含此列的表的名字
insertable 此列是否包含在新增SQL中
updatable 此列是否包含在更新SQL中
@JoinColumns 关系存在多个JoinColumn,用JoinColumns定义多个JoinColumn的属性。
foreignKey 此列的外键约束
value JoinColumn的集合
@JoinTable JoinTable在many-to-many关系的所有者一边定义。如果没有定义JoinTable,使用JoinTable的默认值。
catalog 表的目录
foreignKey 外键约束,创建表的时候使用
indexes 表的索引,在生成表的时候使用
inverseForeignKey 创建表时候用于指定InverseJoinColumns元素对应的列的外键约束
inverseJoinColumns 联接表的外键列,该列引用不拥有关联的实体的主表
joinColumns 联接表的外键列,该列引用拥有关联的实体的主表
name 进行连接的表名称
schema 表的命名空间
uniqueConstraints 放置在表上的约束,创建表的时候使用
@OrderBy 在一对多,多对多关系中,有时我们希望从数据库加载出来的集合对象是按一定方式排序的,这可以通过OrderBy来实现,默认是按对象的主键升序排列。
value 排序字段或排序规则

多表映射(实际中并不常用)

注解 参数 描述
@SecondaryTable 一个entity class可以映射到多表,SecondaryTable用来定义单个从表的名字,主键名字等属性。
name 表的名称
catalog 表的目录
schema 表的命名空间
pkJoinColumns 和主表关联的列
uniqueConstraints 放置在表上的约束,创建表的时候使用
indexes 表的索引,在生成表的时候使用
foreignKey 用于指定或控制列的外键约束的生成
@SecondaryTables 当一个entity class映射到一个主表和多个从表时,用SecondaryTables来定义各个从表的属性
value SecondaryTable的集合
@PrimaryKeyJoinColumn entity class映射到一个或多个从表。从表根据主表的主键列(列名为referencedColumnName值的列),建立一个类型一样的主键列,列名由name属性定义
columnDefinition 生产DDL时使用的SQL语句,一对一关联的时候不能使用
foreignKey 外键约束,创建表的时候使用
name 当前表的主键列的名称
referencedColumnName 要连接表的主键列的名称
@PrimaryKeyJoinColumns 如果entity class使用了复合主键,指定单个PrimaryKeyJoinColumn不能满足要求时,可以用PrimaryKeyJoinColumns来定义多个PrimaryKeyJoinColumn
foreignKey 外键约束,创建表的时候使用
value PrimaryKeyJoinColumn的集合

其他注解

注解 参数 描述
@MapKey 在一对多,多对多关系中,我们可以用Map来保存集合对象。默认用主键值做key,如果使用复合主键,则用id class的实例做key,如果指定了name属性,就用指定的field的值做key。
name 用作映射键的关联实体的字段或属性的名称。

原文地址:Spring Boot 整合——JPA 数据模型定义(数据实体基础注解使用)

rainbow

这个人很懒,什么都没留下

文章评论