Spring data jpa 系列指南笔记 (二) 实体继承

实体继承

在面向对象的编程,继承是一个重要的概念,子类可以继承父类属性,方法,字段。常规的关系型数据库没有继承的概念,但JPA提供了几种方式,用来实现继承。

@MappedSuperclass继承

这种继承应该是最常见的继承方式。

需求场景:

继续之前的示例,我们现在有两个实体了,一个用户,一个角色。这两个实体都有几个共同的属性,id,cratedTime,lastModifiedTime,以及后面的实体都有相同的属性。我们就可以声明一个抽象类,来声明这些属性,并在类上面添加@MappedSuperclass注解。其它的子类只需要继承自该类。就可以自动获得这些属性,不仅仅是代码上获得该属性,在数据库表上也会出现该字段。

  • 声明 AbstractModel
kotlin 复制代码
@MappedSuperclass
abstract class AbstractModel {
    @Id
    @Column(name = "id_")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null

    @Column(name = "created_time_")
    val cratedTime: ZonedDateTime = ZonedDateTime.now()

    @Column(name = "last_modified_time_")
    var lastModifiedTime: ZonedDateTime = ZonedDateTime.now()
}

// 让User和Role继承自该实体
@Entity
@Table(name = "t_user_")
class User : AbstractModel() {
    @Column(name = "username_", length = 50)
    var username: String = ""

    @Column(name = "password_", length = 64)
    var password: String = ""
}

@Entity
@Table(name = "t_role_")
class Role : AbstractModel() {    
    @Column(name = "name_", length = 50)
    var name: String = ""
}

生成的表结构如下

sql 复制代码
create table t_role_ (
    id_ bigint generated by default as identity, 
    created_time_ timestamp(6) with time zone, 
    last_modified_time_ timestamp(6) with time zone, 
    name_ varchar(50), 
    primary key (id_)
);
create table t_user_ (
    id_ bigint generated by default as identity, 
    created_time_ timestamp(6) with time zone, 
    last_modified_time_ timestamp(6) with time zone, 
    password_ varchar(64), 
    username_ varchar(50), 
    primary key (id_)
)

继承效果:

  • AbstractModel不会生成表,只会生成t_user_表和t_role_表,
  • 每个表都会生成id,cratedTime,lastModifiedTime属性相应的字段,
  • 每个表的ID生成策略是独立的。User和Role之间没有任何关系。

单表继承

单表继承,子类和父类的的数据都保存在一张数据库表中。在数据库表中通过dtype区分子类。只需要在父类上声明@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

kotlin 复制代码
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
open class AbstractModel {
    @Id
    @Column(name = "id_")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    open val id: Long? = null

    @Column(name = "created_time_")
    open val cratedTime: ZonedDateTime = ZonedDateTime.now()

    @Column(name = "last_modified_time_")
    open var lastModifiedTime: ZonedDateTime = ZonedDateTime.now()
}

@Entity
class Role : AbstractModel() {

    @Column(name = "name_", length = 50)
    var name: String = ""
}

@Entity
class User : AbstractModel() {

    @Column(name = "username_", length = 50)
    var username: String = ""

    @Column(name = "password_", length = 64)
    var password: String = ""

}

生成的建表语句如下

sql 复制代码
create table abstract_model (
    dtype varchar(31) not null check ((dtype in ('AbstractModel','Role','User'))), 
    id_ bigint generated by default as identity, 
    created_time_ timestamp(6) with time zone, 
    last_modified_time_ timestamp(6) with time zone, 
    name_ varchar(50), 
    password_ varchar(64), 
    username_ varchar(50), 
    primary key (id_)
)

继承效果:

  • 子类不能有@Table注解,因为子类不会生成表
  • 只有AbstractModel会生成表,但在表中会有一列dtype用于标识这一行数据属于哪个子类
  • 所有的子类的字段都会被添加到父类表中

表Join继承

表Join继承,则是子类和父类的表通过主键Join实现继承。只需要在父类上声明@Inheritance(strategy = InheritanceType.JOINED)

kotlin 复制代码
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
open class AbstractModel {
    @Id
    @Column(name = "id_")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null

    @Column(name = "created_time_")
    val cratedTime: ZonedDateTime = ZonedDateTime.now()

    @Column(name = "last_modified_time_")
    var lastModifiedTime: ZonedDateTime = ZonedDateTime.now()
}

@Entity
@Table(name = "t_role_")
class Role : AbstractModel() {
    
    @Column(name = "name_", length = 50)
    var name: String = ""
}

@Entity
@Table(name = "t_user_")
class User : AbstractModel() {

    @Column(name = "username_", length = 50)
    var username: String = ""

    @Column(name = "password_", length = 64)
    var password: String = ""
}

生成的表结构如下

sql 复制代码
create table abstract_model (
    id_ bigint generated by default as identity, 
    created_time_ timestamp(6) with time zone, 
    last_modified_time_ timestamp(6) with time zone, 
    primary key (id_)
);
create table t_role_ (
    name_ varchar(50), 
    id_ bigint not null, 
    primary key (id_)
);
create table t_user_ (
    password_ varchar(64), 
    username_ varchar(50), 
    id_ bigint not null, 
    primary key (id_)
);
alter table if exists t_role_ add constraint FKs5f8aqfjxmnt60k5w5ecfatpn foreign key (id_) references abstract_model;
alter table if exists t_user_ add constraint FK1m8egvri3dc7pswqyhis6jmpg foreign key (id_) references abstract_model

继承效果:

  • AbstractModel会生成表,
  • 每个子类都会生成表,但不会包含cratedTime,lastModifiedTime属性相应的字段,
  • 子类表和父类表之间通过一对一的关系实现关联。
  • 子表的ID序列由父表决定。
  • 会同时创建子表和父表之间的外键约束

每个类一个表继承

在这种模式下,每个类都会生成自己的表。只需要在父类上添加注解@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 实体定义如下:

kotlin 复制代码
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
open class AbstractModel {

    @Id
    @Column(name = "id_")
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    open val id: Long? = null

    @Column(name = "created_time_")
    open val cratedTime: ZonedDateTime = ZonedDateTime.now()

    @Column(name = "last_modified_time_")
    open var lastModifiedTime: ZonedDateTime = ZonedDateTime.now()
}

@Entity
@Table(name = "t_role_")
class Role : AbstractModel() {

    @Column(name = "name_", length = 50)
    var name: String = ""
}

@Entity
@Table(name = "t_user_")
class User : AbstractModel() {

    @Column(name = "username_", length = 50)
    var username: String = ""

    @Column(name = "password_", length = 64)
    var password: String = ""

}

生成的建表语句如下

sql 复制代码
create table abstract_model (
    id_ bigint not null, 
    created_time_ timestamp(6) with time zone, 
    last_modified_time_ timestamp(6) with time zone, 
    primary key (id_)
);
create table t_role_ (
    id_ bigint not null, 
    created_time_ timestamp(6) with time zone, 
    last_modified_time_ timestamp(6) with time zone, 
    name_ varchar(50), 
    primary key (id_)
);
create table t_user_ (
    id_ bigint not null, 
    created_time_ timestamp(6) with time zone, 
    last_modified_time_ timestamp(6) with time zone, 
    password_ varchar(64), 
    username_ varchar(50), 
    primary key (id_)
);

继承效果:

  • AbstractModel会生成表,
  • 每个子类都会生成表,都会生成父相关字段,
  • 子类表和父类表之间没有外键关联。
  • 所有的相关类共享一个ID空间,即父类,子类,之间的ID不能重复。

小结

各种继承方式的总结表格如下:

继承方式 创建父表 创建子表 子表包含所有字段 共享ID空间
@MappedSuperclass
SINGLE_TABLE
TABLE_PER_CLASS
JOINED

我们需要根据场景来选择不同的继承方式。

后续会持续的更新其它内容。直到jpa的进阶应用,欢迎关注。

项目地址:github.com/ldwqh0/jpa-...

相关推荐
SimonKing34 分钟前
全网爆火的OpenClaw保姆级教程Linux版,它来了。
java·后端·程序员
青柠代码录1 小时前
【Linux】常用命令:sort
后端
小江的记录本1 小时前
【MyBatis-Plus】MyBatis-Plus的核心特性、条件构造器、分页插件、乐观锁插件
java·前端·spring boot·后端·sql·tomcat·mybatis
驕傲的兎孒2 小时前
基于 SpringBoot + Vue3 + AI 打造企业级售后服务支持平台 | 实战方案分享
人工智能·spring boot·后端
大傻^2 小时前
Spring AI Alibaba 可观测性实践:AI应用监控与链路追踪
java·人工智能·后端·spring·springaialibaba
诗人不写诗2 小时前
spring是如何组织切面的
java·后端·spring
小杨同学492 小时前
STM32 进阶封神之路(二十二):DMA 实战全攻略 ——ADC 采集 + 串口收发 + 内存复制(库函数 + 代码落地)
后端·单片机·嵌入式
天下无贼!2 小时前
【Python】2026版——FastAPI 框架快速搭建后端服务
开发语言·前端·后端·python·aigc·fastapi
大傻^2 小时前
Spring AI Alibaba Agent开发:基于ChatClient的智能体构建模式
java·数据库·人工智能·后端·spring·springaialibaba
大傻^3 小时前
Spring AI Alibaba ChatClient实战:流式输出与多轮对话管理
java·人工智能·后端·spring·springai·springaialibaba