使用InheritanceType.JOINED实现分层表时如何让Hibernate在父子表都写入tenant_id
以下三种都是符合JPA/Hibernate规范的实现,均不需要在子类重复定义租户字段,比你当前的临时方案更简洁易维护:
方案1:使用@AttributeOverride注解(全Hibernate版本兼容)
直接在子类上添加注解,显式要求Hibernate将继承自父类的tenantId字段同时映射到子类对应的数据库表中:
@Entity @AttributeOverride(name = "tenantId", column = @Column(name = "tenant_id", nullable = false)) class Car( override var tenantId: Int, override var title: String, ) : Vehicle(tenantId, title) { var numberOfDoors: Int = 0 }
Plane等其他继承自Vehicle的子类,添加相同的@AttributeOverride注解即可。
该注解的作用是重写父类属性的映射规则,通知Hibernate该属性不仅要写入父表,也要在子类对应的表中保存冗余值。
方案2:使用Hibernate原生@TenantId注解(Hibernate 6+版本适用)
如果你的Hibernate版本在6.0及以上,官方提供了专门的多租户字段注解,自带多表同步维护能力,仅需修改父类的tenantId字段定义即可,子类不需要做任何额外配置:
@Entity @Inheritance(strategy = InheritanceType.JOINED) abstract class Vehicle( @TenantId @Column(name = "tenant_id", nullable = false) var tenantId: Int, var title: String )
添加@TenantId注解后,Hibernate会自动识别租户字段,在所有继承层级的表插入、更新操作时自动同步该字段的值。
方案3:全局事件监听(适合实体数量多的场景)
如果你的项目中存在大量继承自Vehicle的实体,逐个添加@AttributeOverride效率较低,可以实现Hibernate的PreInsertEventListener事件监听器,全局拦截所有实体的插入操作,自动为子类表填充tenant_id值,一次配置全局生效。
原理解释
InheritanceType.JOINED策略默认只会将父类定义的公共字段存储在父表,子类表仅保存子类特有的字段,你遇到的是该策略的默认表现,只需要通过上述任意一种方式显式通知Hibernate需要冗余存储租户字段即可。配置完成后,Hibernate生成的插入SQL会自动包含子类表的tenant_id字段写入逻辑,满足合规要求。
内容的提问来源于stack exchange,提问作者Tom Naessens

