JPA 注解及主键生成策略使用指南

简介: JPA 注解及主键生成策略使用指南

JPA 注解


Entity 常用注解


参考:JPA & Spring Data JPA学习与使用小记


指定对象与数据库字段映射时注解的位置:如@Id、@Column等注解指定Entity的字段与数据库字段对应关系时,注解的位置可以在Field(属性)或Property(属性的get方法上),两者统一用其中一种,不能两者均有。推荐用前者。


@Entity、@Table

@Entity(必需)



标注在实体类上。


映射实体类。指出该 Java 类为实体类,将映射到指定的关系数据库表。


应用了此注解后,将会自动将类名映射作为数据库表名、将类内的字段名映射为数据库表的列名。映射策略默认是按驼峰命名法拆分将类名或字段名拆分成多部分,然后以下划线连接,如StudentEntity -> student_entity、studentName -> student_name。若不按默认映射,则可通过@Table、@Column 指定,见下面。



@Table(可选)


标注在实体类上。


映射数据库表名。当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用


  • schema属性:指定数据库名
  • name属性:指定表名,不指定时默认按驼峰命名法拆分将类名,并以下划线连接


@DynamicInsert、@DynamicUpdate


@DynamicInsert(可选)


标注在实体类上。



设置为 true,表示 insert 对象的时候,生成动态的 insert 语句,如果这个字段的值是 null 就不会加入到 insert 语句中,默认 false

@DynamicUpdate(可选)

标注在实体类上。



设置为 true,表示执行 update 对象时,在生成动态的 update 语句前,会先查询该表在数据库中的字段值,并对比更新使用的对象中的字段值与数据库中的字段值是否相同,若相同(即该值没有修改),则该字段就不会被加入到 update 语句中。


默认 false,表示无论更新使用实体类中的字段值与数据库中的字段值是否一致,都加入到 update 语句中,即都使用对象中所有字段的值覆盖数据库中的字段值。


比如只想更新某个属性,但是却把整个属性都更改了,这并不是希望的结果,希望的结果是:更改了哪写字段,只要更新修改的字段就够了。



   所以 jpa 更新数据库字段值,无论是否有 @DynamicUpdate 注解,均需要手动先 select 对象,然后通过 set 更新对象的属性值,然后再 save 对象,实现更新操作


注意:

   @DynamicUpdate 的动态更新的含义是,比较更新要使用的实体类中的所有字段值与从数据库中查询出来的所有字段值,判断其是否有修改,不同则加入到update语句中更新字段值。


   例如:数据库中 id=1 的记录所有字段都是非空的,但是实体类中只有 name 有值,也就是所有字段都变了,只是其他字段被更新为了新的空值。


@Id、@GeneratedValue


@Id(必需)


标注在实体类成员变量或 getter 方法之上。


映射生成主键。用于声明一个实体类的属性映射为数据库的一个主键列。


若同时指定了下面的 @GeneratedValue 则存储时会自动生成主键值,否则在存入前用户需要手动为实体赋一个主键值。


主键值类型可以是:


   Primitive types:

   boolean, byte, short, char, int, long, float, double

   Equivalent wrapper classes from package java.lang:

   Byte,Short, Character, Integer, Long, Float, Double

   java.math.BigInteger, java.math.BigDecimal

   java.lang.String

   java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp

   Any enum type

   Reference to an entity object

   composite of several keys above


指定联合主键,有 @IdClass、@EmbeddedId 两种方法。


@GeneratedValue


@GeneratedValue 用于标注主键的生成策略,通过 strategy 属性指定。


默认情况下,JPA 自动选择一个最适合底层数据库的主键生成策略:SqlServer 对应 identity,MySQL 对应 auto increment


   AUTO: JPA自动选择合适的策略,是默认选项


   IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式


   TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。


   SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式



@Column、@Basic、@Transient


@Column(可选)


标注在实体类成员变量或 getter 方法之上。

映射表格列。当实体的属性与其映射的数据库表的列不同名时需要使用 @Column 标注说明。


类的字段名在数据库中对应的字段名可以通过此注解的name属性指定,不指定则默认为将属性名按驼峰命名法拆分并以下划线连接,如 createTime 对应 create_time。


**注意:**即使name的值中包含大写字母,对应到db后也会转成小写,如@ Column(name="create_Time") 在数据库中字段名仍为create_time。可通过SpringBoot配置参数【spring.jpa.hibernate.naming.physical-strategy】配置对应策略,如指定name值是什么,数据库中就对应什么名字的列名。默认值为:【org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy】



@Column注解一共有10个属性,这10个属性均为可选属性,各属性含义分别如下:


   name:定义了被标注字段在数据库表中所对应字段的名称。


   unique:该字段是否为唯一标识,默认为false。也可以使用@Table标记中的@UniqueConstraint。


   nullable:该字段是否可以为null值,默认为true。


   length:字段的长度,当字段的类型为varchar时,该属性才有效,默认为255个字符。


   insertable:在使用“INSERT”脚本插入数据时,是否插入该字段的值。默认为true。


   updatable:在使用“UPDATE”脚本插入数据时,是否更新该字段的值。默认为true。


   insertable = false 和updatable = false 一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。


   columnDefinition:建表时创建该字段的DDL语句,一般用于通过Entity生成表定义时使用。

   (如果DB中表已经建好,该属性没有必要使用)


   precision和scale:表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数。


   table:定义了包含当前字段的表名。


@Basic(可选)


表示一个简单的属性到数据表的字段的映射,对于没有任何标注的属性或getter方法,默认标注为 @Basic


   fetch 表示属性的读取策略,有 EAGER 和 LAZY 两种,分别为立即加载和延迟加载


   optional 表示该属性是否允许为 null,默认为 true



@Transient:忽略属性


定义暂态属性。表示该属性并非一个到数据库表的字段的映射,ORM 框架将忽略该属性。


如果一个属性并非数据库表的字段映射,就必须将其标识为 @Transient,否则ORM 框架默认为其注解 @Basic,例如工具方法不需要映射。



@Temporal:时间日期精度


标注在实体类成员变量或getter方法之上。可选。


在 JavaAPI 中没有定义 Date 类型的精度,而在数据库中表示 Date 类型的数据类型有 Date(年月日),Time(时分秒),TimeStamp(年月日时分秒) 三种精度,进行属性映射的时候可以使用 @Temporal 注解调整精度。


目前此注解只能用于修饰JavaAPI中的【java.util.Date】、【java.util.Calendar】类型的变量,TemporalType 取 DATE、TIME、TIMESTAMP 时在MySQL中分别对应的 DATE、TIME、DATETIME 类型。


示例:


  @Temporal(TemporalType.TIMESTAMP)
    @CreationTimestamp //org.hibernate.annotations.CreationTimestamp,用于在JPA执行insert操作时自动更新该字段值
    @Column(name = "create_time", updatable=false )   //为防止手动set,可设false以免该字段被更新
    private Date createTime;
  @Temporal(TemporalType.TIMESTAMP)
    @UpdateTimestamp //org.hibernate.annotations.UpdateTimestamp,用于在JPA执行update操作时自动更新该字段值
    private Date updateTime;



时间日期自动更新:


1、Hibernate 的注解:

@CreationTimestamp(创建时间)、@UpdateTimestamp(更新时间)


用法:在时间日期类型属性上加上注解即可


2、SpringDataJPA 的注解:(可参阅

https://blog.csdn.net/tianyaleixiaowu/article/details/77931903

@CreatedDate(创建时间)、@LastModifiedDate(更新时间)、@CreatedBy、@LastModifiedBy

用法:


   在实体类加上注解 @EntityListeners(AuditingEntityListener.class)

   在启动类上加上注解 @EnableJpaAuditing

   在实体类中属性中加上面四种注解


示例:


@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Integer id;
    // 创建时间
    @CreatedDate
    @Column(name = "create_time", updatable=false )   //为防止手动set,可设false以免该字段被更新
    private Long createTime;
    // 更新时间
    @LastModifiedDate
    @Column(name = "update_time")
    private Long updateTime;
}



其他注解


@MappedSuperClass:共有字段超类



标注在实体类上。


共有字段超类中声明了各 Entity 共有的字段,即数据库中多表中共有的字段,如 create_time、update_time、id 等。


标注为 @MappedSuperclass 的类将不是一个完整的实体类,将不会映射到数据库表,但是其属性都将映射到其子类的数据库字段中。


标注为 @MappedSuperclass 的类不能再标注 @Entity 或 @Table 注解,也无需实现序列化接口。



允许多级继承

注解的类继承另一个实体类或标注 @MappedSuperclass 类,可以使用 @AttributeOverride 或 @AttributeOverrides 注解重定义其父类属性映射到数据库表中字段。



@IdClass:指定联合主键类

标注在实体类上。



指定联合主键类。如:@IdClass(StudentExperimentEntityPK.class)


主键类 StudentExperimentEntityPK 需要满足:


   实现 Serializable 接口

   有默认的 public 无参数的构造方法

   重写 equals 和 hashCode 方法。equal s方法用于判断两个对象是否相同,EntityManger 通过 find 方法来查找 Entity 时,是根据 equals 的返回值来判断的。hashCode 方法返回当前对象的哈希码

   它的类型和名称必须与使用 @Id 进行标注的实体主键字段的类型和名称一致。


示例:

/** 
 * 实体类
 */
@Data
@Entity
@Table(name = "customer_course")
@IdClass(CustomerCourseEntityPK.class)  // 指定联合主键类
public class CustomerCourseEntity {
    @Id
    @Column(name = "customer_id", length = ColumnLengthConstrain.LEN_ID_MAX)
    private String customerId;
    @Id
    @Column(name = "course_id", length = ColumnLengthConstrain.LEN_ID_MAX)
    private String courseId;
    @Column(name = "max_number")
    private Integer maxNumber;
    @ManyToOne
    @JoinColumn(name = "course_id", referencedColumnName = "id", nullable = false, insertable = false, updatable = false)
    private CourseEntity courseByCourseId;
}


/** 
 * 联合主键类
 */
@Data
public class CustomerCourseEntityPK implements Serializable {
    private static final long serialVersionUID = 1L;
    private String customerId;
    private String courseId;
}




@EmbeddedId:联合主键


标注在实体类成员变量或 getter 方法上。


功能与 @IdClass 一样用于指定联合主键。不同的是其标注在实体内的主键类变量上,且主键类应该标注 @Embeddable 注解。


此外在主键类内指定的字段在实体类内可以不再指定,若再指定则需为 @Column 加上 insertable = false, updatable = false 属性

示例:


@Data
@Entity
@Table(name = "customer_course")
@IdClass(CustomerCourseEntityPK.class)  // 指定联合主键类
public class CustomerCourseEntity {
    @EmbeddedId
    private CustomerCourseEntityPK id;
    @Column(name = "max_number")
    private Integer maxNumber;
}



/** 
 * 联合主键类
 */
@Data
@Embeddable
public class CustomerCourseEntityPK implements Serializable {
    private static final long serialVersionUID = 1L;
    @Column(name = "customer_id", length = ColumnLengthConstrain.LEN_ID_MAX)
    private String customerId;
    @Column(name = "course_id", length = ColumnLengthConstrain.LEN_ID_MAX)
    private String courseId;
}


@Inheritance:表结构复用

标注在实体类上。


用于表结构复用。指定被该注解修饰的类被子类继承后子类和父类的表结构的关系。


通过 strategy 属性指定关系,有三种策略:


   SINGLE_TABLE:适用于共同字段多独有字段少的关联关系定义。


   子类和父类对应同一个表且所有字段在一个表中,还会自动生成(也可通过 @DiscriminatorColumn 指定)一个字段 varchar 'dtype' 用来表示一条数据是属于哪个实体的。为默认值。


   未使用 @Inheritance 或使用了但没指定 strategy 属性时默认采用此策略。


   JOINED:子类和父类对应不同表,父类属性对应的列(除了主键)不会且无法再出现在子表中。子表自动产生与父表主键对应的外键与父表关联。同样地也可通过 @DiscriminatorColumn 为父类指定一个字段用于标识一条记录属于哪个子类。


   TABLE_PER_CLASS:子类和父类对应不同表且各类自己的所有字段(包括继承的)分别都出现在各自的表中;表间没有任何外键关联。此策略最终效果与 @MappedSuperClass 等同。


@Inheritance 与 @MappedSuperclass 的区别:


   @MappedSuperclass 子类与父类没有外键关系、不会对应同一个表


   @Inheritance 适用于表关联后者适用于定义公共字段


   两者是可以混合使用


@Inheritance、@MappedSuperClass 可用于定义 Inheritance 关系。这些方式的一个缺点是子类中无法覆盖从父类继承的字段的定义(如父类中 name 是 not null,但子类中允许为 null)。


除了 @Inheritance、@MappedSuperClass 外,还有一种 Inheritance 方法(此法可解决上述不足):先定义一个 Java POJO(干净的 POJO,没有任何对该类使用任何的 ORM 注解),然后不同子类继承该父类,并分别在不同子类中进行 ORM 定义即可。此法下不同子类拥有父类的公共字段且该字段在不同子类中对应的数据库列定义可不同。




@Embedded、@Embeddable


当一个实体类要在多个不同的实体类中进行使用,而其不需要生成数据库表


  • @Embeddable:标注在类上,表示此类是可以被其他类嵌套
  • @Embedded:标注在属性上,表示嵌套被@Embeddable注解的同类型类



@Enumerated:映射枚举


使用此注解映射枚举字段,以String类型存入数据库

注入数据库的类型有两种:EnumType.ORDINAL(Interger)、EnumType.STRING(String)


@TableGenerator:主键值生成器

TableGenerator定义一个主键值生成器,在 @GeneratedValue的属性strategy = GenerationType.TABLE时,generator属性中可以使用生成器的名字。生成器可以在类、方法或者属性上定义。


生成器是为多个实体类提供连续的ID值的表,每一行为一个类提供ID值,ID值通常是整数。

属性说明:


   name:生成器的唯一名字,可以被Id元数据使用。

   table:生成器用来存储id值的Table定义。

   pkColumnName:生成器表里用来保存主键名字的字段

   valueColumnName:生成器表里用来保存主键值的字段

   pkColumnValue:生成器表里用来保存主键名字的字段的值

   initialValue:id值的初始值。

   allocationSize:id值的增量


示例:

@Entity
public class Employee {
    @Id
    @Column(name = "id")
    @TableGenerator(name = "hf_opert_id_gen",   // 此处的名字要和下面generator属性值一致
      table = "mcs_hibernate_seq",      // 主键保存到数据库的表名
      pkColumnName = "sequence_name",     // 表里用来保存主键名字的字段
      valueColumnName = "sequence_next_hi_value",   // 表里用来保存主键值的字段
      pkColumnValue = "user_id",  // 表里名字字段对应的值
      allocationSize = 1)     // 自动增长,设置为1
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "hf_opert_id_gen")
    private Integer id;
}


@JoinColumn、@JoinColumns

@JoinColumn:指定外键


如果在实体类的某个属性上定义了联表关系(OneToOne或OneTOMany等),则使用@JoinColumn注解来定义关系的属性。JoinColumn的大部分属性和Column类似。


属性说明:


   name:主表的列名。若不指定,默认为 关联表的名称 + “_” + 关联表主键的字段名,例如 address_id

   referencedColumnName:关联表作为外键的列名。若不指定,默认为关联表的主键作为外键。

   unique:是否唯一 ,默认false

   nullable:是否允许为空,默认true

   insertable:是否允许插入,默认true

   updatable:是否允许更新,默认true

   columnDefinition:定义建表时创建此列的DDL

   secondaryTable:从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字。

   foreignKey():外键。默认@ForeignKey(ConstraintMode.PROVIDER_DEFAULT);


@Data
@Entity
public class Person {
    ...
    // Person和Address是一对一关系。Address表中名为id_address的列作为外键指向Person表中名为address_id的列
    @OneToOne
    @JoinColumn(name="address_id", referencedColumnName="id_address", unique=true)
    private Address address;
}
@Data
@Entity
public class Address {
    @Id
    @column(name ="id_address")
    private Integer idAddress;
}



@JoinColumns


如果在实体类的某个属性上定义了联表关系(OneToOne或OneTOMany等),并且关系存在多个JoinColumn,则使用@JoinColumns注解定义多个JoinColumn的属性。


属性说明:

   value:定义JoinColumn数组,指定每个JoinColumn的属性


@Data
@Entity
public class Custom {
    // Custom和Order是一对一关系。Order表中一个名为CUST_ID的列作为外键指向Custom对应表中名为ID_CUST的列,另一名为CUST_NAME的列作为外键指向Custom对应表中名为NAME_CUST的列
    @OneToOne
    @JoinColumns({
        @JoinColumn(name="CUST_ID", referencedColumnName="ID_CUST"),
        @JoinColumn(name="CUST_NAME", referencedColumnName="NAME_CUST")
    })
    private Order order;
}


@OneToOne、@OneToMany


@OneToOne


描述一个 一对一的关联


属性说明:

  • fetch:表示抓取策略,默认为FetchType.LAZY
  • cascade:表示级联操作策略。CascadeType.ALL,当前类增删改查改变之后,关联类跟着增删改查。


@Data
@Entity
public class Person {
    ...
    // Person和Address是一对一关系。Address表中名为id_address的列作为外键指向Person表中名为address_id的列
    @OneToOne
    @JoinColumn(name="address_id", referencedColumnName="id_address", unique=true)
    private Address address;
}


相关文章
|
8月前
|
存储 Java 数据库连接
JPA 之 Hibernate EntityManager 使用指南
JPA 之 Hibernate EntityManager 使用指南
200 0
|
8月前
|
存储 Java 关系型数据库
JPA 注解及主键生成策略使用指南2
JPA 注解及主键生成策略使用指南2
|
SQL 存储 Oracle
JPA 概述及常用注解详解、SpringDataJpa 使用指南
JPA 概述及常注解详解、SpringDataJpa 使用指南
4040 2
JPA 概述及常用注解详解、SpringDataJpa 使用指南
|
Java 容器 Spring
Springboot2中新增的注解属性proxyBeanMethods
Springboot2中新增的注解属性proxyBeanMethods:代理bean的方法
328 0
|
XML Java 数据格式
《SpringBoot系列八》:Spring注解别名@AliasFor和覆盖(含原理)
《SpringBoot系列八》:Spring注解别名@AliasFor和覆盖(含原理)
514 0
|
Oracle Java 关系型数据库
《SpringBoot篇》11.JPA常用注解只需一个表
《SpringBoot篇》11.JPA常用注解只需一个表
106 0
《SpringBoot篇》11.JPA常用注解只需一个表
|
SQL Java 数据库连接
《SpringBoot篇》10.JPQL超详细介绍与JPA命名规则(一)
《SpringBoot篇》10.JPQL超详细介绍与JPA命名规则(一)
232 0
|
SQL Java 数据库
《SpringBoot篇》10.JPQL超详细介绍与JPA命名规则(二)
《SpringBoot篇》10.JPQL超详细介绍与JPA命名规则(二)
209 0
《SpringBoot篇》10.JPQL超详细介绍与JPA命名规则(二)
|
存储 Java Spring
自定义spring注解使用
自定义spring注解使用+ThreadLocal使用
自定义spring注解使用
|
SQL XML Java
SpringBoot + Mybatis系列之传参作为字段/表名时的注意事项
今天遇到一个非常有意思的事情,一个小伙伴反馈,同样的 sql,为啥直接通过 mysql 终端执行的结果,和 mybatis 的执行结果不一致,感觉有点鬼畜;然后看了一下,发现这是个比较典型的问题,#{}与${}的使用区别 接下来我们看一下这个问题,顺带也重新学习一下它们两的区别
332 0
SpringBoot + Mybatis系列之传参作为字段/表名时的注意事项