推荐使用 @MappedSuperclass 定义抽象父类复用通用字段(如 id、createdAt),子类用 @Entity 继承,避免 @Entity 父类或 @Inheritance 导致的表映射错误。

在 Java 中,通过继承共享通用数据库实体字段,最常用且推荐的方式是使用 抽象父类 + JPA 注解(如 @MappedSuperclass),而不是直接用 @Entity 继承。这样既能复用字段和逻辑,又避免了父类被当成独立表映射的错误。
用 @MappedSuperclass 复用基础字段
这是 JPA 提供的专用机制,用于定义可被继承但不对应数据库表的公共字段。
- 父类加上
@MappedSuperclass,不加@Entity - 通用字段(如
id、createdAt、updatedAt、version)写在父类中,并标注 JPA 注解(如@Id、@GeneratedValue、@Column、@Version) - 子类用
@Entity声明,并自动继承所有映射字段,无需重复声明
示例:
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "created_at", updatable = false, nullable = false)
private LocalDateTime createdAt = LocalDateTime.now();
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt = LocalDateTime.now();
@Version
private Integer version;
// getter/setter 省略
}
@Entity
@Table(name = "users")
public class User extends BaseEntity {
@Column(name = "username", unique = true)
private String username;
@Column(name = "email")
private String email;
// 自动拥有 id、createdAt、updatedAt、version 字段
}
避免踩坑:不用 @Inheritance 或普通 @Entity 继承
直接让多个 @Entity 类继承同一个 @Entity 父类,会导致 JPA 尝试做继承映射(如单表/联合表策略),通常不是你想要的效果——你只是想“复用字段”,不是建继承关系模型。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
-
@Inheritance适合真正有业务层级的实体关系(如Animal → Dog/Cat),会引入额外表结构或判别列,增加复杂度 - 若父类误加了
@Entity,JPA 会尝试为它创建表,报错或产生冗余表 -
@MappedSuperclass是轻量、无侵入、语义明确的解决方案
进阶:配合 Lombok 和构造逻辑简化代码
可以结合 Lombok 减少模板代码,并在父类中统一处理时间字段更新逻辑。
- 用
@Data或@Getter/@Setter自动生成 getter/setter - 重写
setUpdatedAt(),确保每次更新都设为当前时间;也可用 JPA 的@PreUpdate回调 - 若需强制初始化
createdAt,可用@PrePersist(注意:字段默认值已生效时,该回调非必需)
例如:
@MappedSuperclass
public abstract class BaseEntity {
// ... id、version 字段
@Column(name = "created_at", updatable = false, nullable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
LocalDateTime now = LocalDateTime.now();
this.createdAt = now;
this.updatedAt = now;
}
@PreUpdate
protected void onUpdate() {
this.updatedAt = LocalDateTime.now();
}
}
其他场景补充说明
如果项目用的是 MyBatis 或 MyBatis-Plus,方式略有不同:
- MyBatis:无注解继承支持,靠手动继承 POJO 类 + XML 中复用
<sql>片段或<include> - MyBatis-Plus:支持
@TableName和继承,但通用字段仍需在子类中显式声明(除非用MetaObjectHandler全局填充) - Spring Data JDBC:类似 JPA,推荐用普通 Java 继承 + 构造器/初始化逻辑,无内置
@MappedSuperclass等价物

















