死磕Spring Data JPA---第二篇

本文深入探讨JPA中的关键注解,包括@Id、@GeneratedValue、@Entity等,解析其在实体类映射中的作用及配置策略,适用于数据库操作的初学者和进阶开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JPA基础

它山之石可以攻玉,我们先学习下JPA的一些知识。

JPA @Id 和@GeneratedValue 注解的详解

JPA通用策略

通过annotation来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法。

可以观察下注解@GeneratedValue源码:

    @Target({METHOD,FIELD})    
    @Retention(RUNTIME)    
    public @interface GeneratedValue{    
        GenerationType strategy() default AUTO;    
        String generator() default "";    
    }  
    
    针对于GenerationType:
    
    public enum GenerationType{    
      TABLE,    
      SEQUENCE,    
      IDENTITY,    
      AUTO   
    }  

通过源码的学习:可以知道JPA的四种标准用法主要为: TABLE、SEQUENCE、IDENTITY、AUTO。

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

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

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

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

一般推荐使用上面2种方式。

@Id

*@Id 标注用于声明一个实体类的属性映射为数据库的主键列*。该属性通常置于属性声明语句之前,可与声明语句同行,也可写在单独行上。 @Id标注也可置于属性的getter方法之前。

@GeneratedValue:

*@GeneratedValue* *用于标注主键的生成策略,通过strategy 属性指定。默认情况下,**JPA 自动选择一个最适合底层数据库的主键生成策略:SqlServer对应identity,MySQL 对应 auto increment。*

@Table(name ="user")
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
​
​
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer id;
​
    private String name;
​
    private String password;
​
    private String account;
​
}

JPA 在实际开发过程中常用的注解

@Entity

被Entity标注的实体类将会被JPA管理控制,在程序运行时,JPA会识别并映射到指定的数据库表
唯一参数name:指定实体类名称,默认为当前实体类的非限定名称。
若给了name属性值即@Entity(name="XXX"),则jpa在仓储层(数据层)进行自定义查询时,所查的表名应是XXX。
如:select s from XXX s
​

@TABLE

当你想生成的数据库表名与实体类名称不同时,使用 @Table(name="数据库表名"),与@Entity标注并列使用,置于实体
类声明语句之前
@Table
@Entity(name ="user")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
}
​

@Column

通常置于实体的属性声明之前,可与 @Id 标注一起使用
@Column参数:
  1. name: 指定映射到数据库中的字段名
  2. unique: 是否唯一,默认为false
  3. nullable: 是否允许为null,默认为true
  4. insertable: 是否允许插入,默认为true
  5. updatetable: 是否允许更新,默认为true
  6. columnDefinition: 指定该属性映射到数据库中的实际类型,通常是自动判断。
​

@Transient

JPA会忽略该属性,不会映射到数据库中,即程序运行后数据库中将不会有该字段

@JoinColumn

定义表关联的外键字段名
常用参数有:
  1. name: 指定映射到数据库中的外键的字段名
  2. unique: 是否唯一,默认为false
  3. nullable: 是否允许为null,默认为true
  4. insertable: 是否允许插入,默认为true
  5. updatetable: 是否允许更新,默认为true
  6. columnDefinition: 指定该属性映射到数据库中的实际类型,通常是自动判断。
  7. foreignKey = @ForeignKey(name = "none",value = ConstraintMode.NO_CONSTRAINT):指定外键相关信息,这里用法是指定外联关系但是不建立数据库外键

@Enumerated

当实体类中有枚举类型的属性时,默认情况下自动生成的数据库表中对应的字段类型是枚举的索引值,是数字类型的,若希望数据库中存储的是枚举对应的String类型,在属性上加入@Enumerated(EnumType.STRING)注解即可。

@Enumerated(EnumType.STRING)
@Column(nullable = true)
private RoleEnum role;

@Temporal

Java中没有定义 Date 类型的精度,而数据库中,表示时间类型的数据有 DATE,TIME,TIMESTAMP三种精度
  - @Temporal(TemporalType.DATE) 表示映射到数据库中的时间类型为 DATE,只有日期
  - @Temporal(TemporalType.TIME) 表示映射到数据库中的时间类型为 TIME,只有时间
  - @Temporal(TemporalType.TIMESTAMP) 表示映射到数据库中的时间类型为 TIMESTAMP,日期和时间都有

@OneToOne

参数:
    targetEntity: 指定关联实体类型,默认为被注解的属性或方法所属的类
    cascade: 级联操作策略
    CascadeType.ALL 级联所有操作
    CascadeType.PERSIST 级联新增
    CascadeType.MERGE 级联归并更新
    CascadeType.REMOVE 级联删除
    CascadeType.REFRESH 级联刷新
    CascadeType.DETACH 级联分离
    fetch: fetch 表示该属性的加载读取策略 (默认值为 EAGER)
        EAGER 主动抓取
        LAZY 延迟加载,只有用到该属性时才会去加载
    optional: 默认为true,关联字段是否为空
            如果为false,则常与@JoinColumn一起使用
    mappedBy: 指定关联关系,该参数只用于关联关系被拥有方
        只用于双向关联@OneToOne,@OneToMany,@ManyToMany。而@ManyToOne中没有
        @OneToOne(mappedBy = “xxx”)
            表示xxx所对应的类为关系被拥有方,而关联的另一方为关系拥有方
            关系拥有方:对应拥有外键的数据库表
        关系被拥有方:对应主键被子表引用为外键的数据库表
    orphanRemoval:默认值为false
        判断是否自动删除与关系拥有方不存在联系的关系被拥有方(关系被拥有方的一个主键在关系拥有方中未被引用,
        当jpa执行更新操作时,是否删除数据库中此主键所对应的一条记录,若为true则删除)

OneToOne分为单项和双向。通过以下2个例子说明

//单向 一对一
public class Emp{
  @Id
  @GeneratedValue
  privte Integer eId;
​
  @OneToOne(cascade = CascadeType.ALL)
  private Identity identity; 
}
​
@Entity
public class Identity{
​
}
​
//双向 一对一
public class Emp{
  @Id
  @GeneratedValue
  privte Integer eId;
​
  @OneToOne(cascade = CascadeType.ALL)
  private Identity identity;
}
​
public class Identity{
  @Id
  @GeneratedValue
  privte Integer iId;
​
  @OneToOne(cascade = CascadeType.ALL, mappedBy = "identity")
  private Emp emp;
}
​

@ManyToOne、@OneToMany

​ 多对一(也可叫一对多,只是前后表颠倒一下而已),只有双向多对一时才用得到@OneToMany。多对一中多的一方必定是对应数据库中拥有外键的表,即是关系拥有方,@ManyToOne只用在多对一中代表多的一类中,因为mappedBy只用于关系被拥有方,所以@ManyToOne参数中不包含mappedBy。

@ManyToOne参数说明

  • targetEntity: 指定关联实体类型,默认为被注解的属性或方法所属的类

  • cascade: 级联操作策略

    • CascadeType.ALL 级联所有操作

    • CascadeType.PERSIST 级联新增

    • CascadeType.MERGE 级联归并更新

    • CascadeType.REMOVE 级联删除

    • CascadeType.REFRESH 级联刷新

    • CascadeType.DETACH 级联分离

  • fetch: fetch 表示该属性的加载读取策略(@ManyToOne 的默认值是 EAGER,@OneToMany 的默认值是 LAZY)

  • EAGER 主动抓取

  • LAZY 延迟加载,只有用到该属性时才会去加载

  • optional: 默认为true,关联字段是否为空如果为false,则常与@JoinColumn一起使用

@ OneToMany 参数说明

  • mappedBy: 指定关联关系,该参数只用于关联关系被拥有方只用于双向关联@OneToOne,@OneToMany,@ManyToMany。而@ManyToOne中没有@OneToMany(mappedBy = “xxx”)表示xxx所对应的类为关系被拥有方,而关联的另一方为关系拥有方

    • 关系拥有方:对应拥有外键的数据库表

    • 关系被拥有方:对应主键被子表引用为外键的数据库表

  • orphanRemoval:默认值为false判断是否自动删除与关系拥有方不存在联系的关系被拥有方(关系被拥有方的一个主键在关系拥有方中未被引用,当jpa执行更新操作时,是否删除数据库中此主键所对应的一条记录,若为true则删除)

//双向 多对一
public class Emp{
  @Id
  @GeneratedValue
  privte Integer eId;
​
  @ManyToOne(cascade = CascadeType.ALL)
  private Emp emp;
}
​
public class Dept{
  @Id
  @GeneratedValue
  privte Integer dId;
​
  @OneToMany(cascade = CascadeType.ALL, mappedBy = "emp")
  private List<Emp> emps;
}
​
无论是单向多对一还是双向,在多放都会产生一个外键。

@ManyToMany

  • targetEntity: 指定关联实体类型,默认为被注解的属性或方法所属的类

  • cascade: 级联操作策略

    • CascadeType.ALL 级联所有操作

    • CascadeType.PERSIST 级联新增

    • CascadeType.MERGE 级联归并更新

    • CascadeType.REMOVE 级联删除

    • CascadeType.REFRESH 级联刷新

    • CascadeType.DETACH 级联分离

  • fetch: fetch 表示该属性的加载读取策略 (默认值为 LAZY)

    • EAGER 主动抓取

    • LAZY 延迟加载,只有用到该属性时才会去加载

  • mappedBy: 指定关联关系,该参数只用于关联关系被拥有方只用于双向关联@OneToOne,@OneToMany,@ManyToMany。而@ManyToOne中没有。@ManyToMany(mappedBy = “xxx”)表示xxx所对应的类为关系被拥有方,而关联的另一方为关系拥有方:

    • 关系拥有方:对应拥有外键的数据库表

    • 关系被拥有方:对应主键被子表引用为外键的数据库表

public class Student{
  @ManyToMany(cascade = CascadeType.ALL)
  private List<Course> courses;
}
​
@Entity
public class Course{
  @ManyToMany(cascade = CascadeType.ALL, mappedBy = "courses")
  private List<Student> students;
}
所有双向关联使用时需谨慎,查询时容易引起栈内存溢出,尽量使用单向关联
​

还有一个注解@Embedded 和 @Embeddable,目前开发中基本上没有使用到(也许我做的jpa项目还不够吧,也可能是接触java才3年不到的原因吧)。开发中用到此注解的情况,个人感觉就是基础属性的提取吧。不过个人习惯用抽象类定义,然后继承从而实现(若是不对,勿喷。)。举个例子吧:

@Embeddable
public class BaseEntity implements Serializable{
    private static final long serialVersionUID = 8849870114128959929L;
    private String creator;
    private String updator;
}
​
@Entity
public class Person implements Serializable{
    private static final long serialVersionUID = 8849870114127659929L;
​
    @Id
    @GeneratedValue
    private Long id;
​
    @Column(nullable = false)
    private String name;
​
    @Embedded
    private BaseEntity baseEntity;
}

通过上面的代码,表person会产生的列有: id,name,creator,updator;

而我在实际开发过程中,习惯(喜欢)使用抽象类去处理.如下:

public abstract class BaseEntity implements Serializable{
    private static final long serialVersionUID = 8849870114128959929L;
    private String creator;
    private String updator;
}
​
@Entity
public class Person extends BaseEntity{
    @Id
    @GeneratedValue
    private Long id;
​
    @Column(nullable = false)
    private String name;
}

这里就不具体介绍这2个注解的解释,有兴趣的朋友可以看下这篇博客:

https://www.cnblogs.com/mr-wuxiansheng/p/7517943.html

[连接] https://www.cnblogs.com/mr-wuxiansheng/p/7517943.html连接

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值