使用带有 Hibernate 的 JPA 注释来描述外键仅在子表中的 @OneToMany 关系
Posted
技术标签:
【中文标题】使用带有 Hibernate 的 JPA 注释来描述外键仅在子表中的 @OneToMany 关系【英文标题】:Using JPA annotations with Hibernate to describe @OneToMany relationship where foreign key is only in child table 【发布时间】:2016-04-25 18:59:27 【问题描述】:我有一个非常满意的现有数据模型:
public class Garden
private String name; // "Oak Grove"
private List<Plant> plants;
public class Plant
private String name; // "Cherry Tomato"
我想在 Hibernate 中使用以下条件进行映射:
Java 中的Plant
类不维护对其父级Garden
的引用。这使得 Java 层 IMO 中的事情变得更加困难。
PLANT
表应该有一个GARDEN_ID
列,它是GARDEN(ID)
列的外键。
我在添加@OneToMany
之前的初始设置:
@Entity(name = "GARDEN")
public class Garden
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "NAME", nullable = false)
private String name; // "Oak Grove"
// Not yet mapped
private List<Plant> plants;
@Entity(name = "PLANT")
public class Plant
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "NAME", nullable = false)
private String name; // "Cherry Tomato"
如何在List<Plant> plants;
上定义@OneToMany
注释,以便在Plant
中维护外键引用?
如果我只是添加:
@OneToMany(cascade = CascadeType.ALL )
@JoinColumn(name = "GARDEN_ID")
private List<Plant> plants;
然后用植物拯救花园失败:
Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "GARDEN_ID"; SQL statement:
insert into PLANT (NAME, ID) values (?, ?) [23502-191]
因此,Hibernate 似乎并没有尝试保留外键。有没有办法在不完全破坏我的对象模型的情况下做到这一点?
编辑:我测试的方式是:
Garden garden = new Garden("Oak Grove");
garden.addPlant(new Plant("Cherry Tomato"));
gardenManager.save(garden);
其中save()
方法看起来非常Hibernate-ey:
public void save(T item)
try (Session session = factory.openSession())
Transaction transaction = session.beginTransaction();
try
session.saveOrUpdate(item);
transaction.commit();
catch (Exception ex)
System.out.println("Error occurred saving item: " + ex.getMessage());
ex.printStackTrace();
transaction.rollback();
【问题讨论】:
您是否尝试过删除 @JoinColumn 并将 mappedBy 添加到 @OneToMany? @OneToMany(cascade = CascadeType.ALL, mappedBy = "garden") @Nicholas 代码已添加。 @RubioRic 我的理解是,当子 (Plant
) 类上有相应的 Java 属性 (private Garden garden
) 时使用 mappedBy
- 这不是我想要的完成。
@CraigOtis 你是对的。我的错。
无法重现(5.0.9.Final)我得到两个插入和更新:休眠:插入成员(id)值(?)休眠:插入课程(id)值(? ) Hibernate:更新课程集 member_id=?哪里 id=?。除了类名之外没有其他区别。
【参考方案1】:
Yogesh Sakurikar 很接近,但双向@JoinColumn
有点偏离。下面你将看到如何双向或单向加入
@Entity(name = "GARDEN")
public class Garden
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "NAME", nullable = false)
private String name; // "Oak Grove"
// use this if you don't want a bi-directional relationship
// @OneToMany
// @JoinColumn(name = "ID", referencedColumnName="GARDEN_ID")
// private List<Plant> plants;
// use this if you want it bi-directional
@OneToMany(fetch = FetchType.LAZY, mappedBy = "garden")
private Set<Plant> plants;
@Entity(name = "PLANT")
public class Plant
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "NAME", nullable = false)
private String name; // "Cherry Tomato"
// use this if you don't want a bi-directional relationship
// @Column(name="GARDEN_ID")
// private long gardenId;
// use this if you want a bi-directional relationship
@ManyToOne
@JoinColumn(name = "GARDEN_ID", referencedColumnName="ID", nullable = false)
private Garden garden;
下面的代码假定双向关系。否则你需要知道你的Garden.id
,然后才能完全描述任何孩子Plant
Garden garden = new Garden("Oak Grove");
Plant plant = new Plant("Cherry Tomato")
plant.setGarden(garden); //don't forget to set the parent on the child
garden.addPlant(plant);
gardenManager.save(garden);
【讨论】:
问题是我宁愿Plant
不知道Garden
。否则,我需要一些我觉得有点麻烦的“不要忘记”代码。我不认为对象模型层应该做出牺牲来满足数据库 - 相反,它应该围绕数据工作。
那么你需要一个单独的链接表。
将父级设置为子级实际上是“更干净”的方法,因为您不需要知道两者的 ID。您的要求将添加第三个表(您可能应该有第三个实体),这将是 @OneToOne
和 Plant
和 @ManyToOne
和 Gadren
... 除非你想要相同的 Plant
在多个Garden
s,这将使他们成为@ManyToMany
,然后肯定需要一个连接表。【参考方案2】:
对于一对多关系,如果植物要持有该关系,则需要使用双向定义它。 这是我认为你应该能够实现它:
@Entity(name = "GARDEN")
public class Garden
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "NAME", nullable = false)
private String name; // "Oak Grove"
@OneToMany(fetch = FetchType.LAZY, mappedBy = "garden")
private List<Plant> plants;
@Entity(name = "PLANT")
public class Plant
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "NAME", nullable = false)
private String name; // "Cherry Tomato"
@ManyToOne
@JoinColumn(name = "GARDEN_ID", nullable = false)
Garden garden;
这种双向方法会让实体管理器知道两者之间存在关系,因为一方面是一对多,另一方面是多对一。希望这会有所帮助。
【讨论】:
感谢您的回答,但我正在寻找一种方法来映射当前对象模型,而无需将Garden
添加到 Plant
。
不确定这是否会有所帮助,但我也会尝试一下:@OneToMany(cascade=CascadeType.ALL) @JoinTable(name="PLANT", joinColumns=@JoinColumn(name=" GARDEN_ID", referencedColumnName="ID") , inverseJoinColumns=@JoinColumn(name="ID", referencedColumnName="GARDEN_ID")) private Set以上是关于使用带有 Hibernate 的 JPA 注释来描述外键仅在子表中的 @OneToMany 关系的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate @LazyToOne 注释的 JPA 等价物是啥?
Hibernate/JPA - 使用@Future 更新还包含日期字段的实体字段
如何将继承策略与 JPA 注释和 Hibernate 混合使用?