如何用Hibernate JPA注解实现同一列关联两张不同表?
问题背景
现有Travel表数据会定时删除,TravelHistory表保存历史数据,两者记录ID一致,且可能同时存在。需要在TravelAudit实体中通过同一列travel_id关联这两个实体,实现一次查询获取Travel和/或TravelHistory数据,避免额外手动查询。
报错信息
org.springframework.beans.factory.BeanCreationException: 创建类路径资源[org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]中定义的名为'entityManagerFactory'的Bean失败;初始化方法调用失败;嵌套异常为javax.persistence.PersistenceException: [持久化单元: default] 无法构建Hibernate SessionFactory
解决方案
核心是通过@JoinColumn的insertable和updatable属性控制字段的读写权限,避免Hibernate识别到重复字段映射冲突,同时保留同一关联列。
方案1:指定单个关联维护travel_id
如果需要通过TravelAudit维护travel_id字段,只需让其中一个关联拥有读写权限,另一个仅做查询映射:
@Entity @Table(name = "travel_audit") public class TravelAudit { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 仅做查询映射,不参与字段写入更新 @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "travel_id", insertable = false, updatable = false) private Travel travel; // 负责travel_id字段的写入和更新 @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "travel_id") private TravelHistory travelHistory; // Getter、Setter方法 }
方案2:单独维护travel_id字段
如果不需要通过关联维护travel_id,可以单独定义字段负责读写,两个关联仅做查询映射:
@Entity @Table(name = "travel_audit") public class TravelAudit { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 负责travel_id字段的读写 @Column(name = "travel_id", nullable = false) private Long travelId; // 仅做查询映射 @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "travel_id", insertable = false, updatable = false) private Travel travel; // 仅做查询映射 @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "travel_id", insertable = false, updatable = false) private TravelHistory travelHistory; // Getter、Setter方法 }
效果验证
配置完成后,调用Optional<TravelAudit> findByTravelId(Long travelId);时,Hibernate会自动关联查询Travel和TravelHistory表,无需手动分两次查询。若对应ID在某张表中不存在,对应的实体属性会为null,符合业务需求。
内容的提问来源于stack exchange,提问作者Tupac

