在一对多的关系中,通常用@ManyToOne注释的字段是所有者 - 另一方具有'mappedBy'属性。但是,如果我跳过'mappedBy'并使用@JoinColumn(同一列)注释双方,我可以更新双方 - 更改被提交给db。
我没有两个单向关系而不是一个双向关系 - 只有一个连接列。
如果不选择一方作为关系所有者,我会遇到哪些问题?
我的实体看起来类似于以下内容:
@Entity
public class B {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
private B parent;
@OneToMany()
@JoinColumn(name = "parent_id")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
private List<B> children = new ArrayList<B>();
...
}
它似乎对性能没有任何影响(至少插入看起来不错) 这是一个简单的测试和日志输出:
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
B a = new B("a");
B b = new B("b");
B c = new B("c");
B d = new B("d");
B e = new B("e");
session.save(a);
session.save(b);
session.save(c);
session.save(d);
session.save(e);
session.getTransaction().commit();
System.out.println("all objects saved");
session.beginTransaction();
a.getChildren().add(b);
a.getChildren().add(c);
session.save(a);
session.getTransaction().commit();
System.out.println("b and c added as children");
session.beginTransaction();
a.getChildren().add(d);
a.getChildren().add(e);
session.getTransaction().commit();
System.out.println("e and f added as children");
session.close();
Hibernate: insert into B (fieldInB, parent_id) values (?, ?)
Hibernate: insert into B (fieldInB, parent_id) values (?, ?)
Hibernate: insert into B (fieldInB, parent_id) values (?, ?)
Hibernate: insert into B (fieldInB, parent_id) values (?, ?)
Hibernate: insert into B (fieldInB, parent_id) values (?, ?)
all objects saved
Hibernate: update B set parent_id=? where id=?
Hibernate: update B set parent_id=? where id=?
b and c added as children
Hibernate: update B set parent_id=? where id=?
Hibernate: update B set parent_id=? where id=?
e and f added as children
答案 0 :(得分:1)
您没有看到额外的SQL语句,因为您没有正确设置关联的两侧。例如,你说:
a.getChildren().add( b );
并假设现在a
和b
已关联。但是当你在这里拨打b.getParent()
时会发生什么?假设此代码“将b
作为孩子添加到a
”,然后返回此新子b
,并且调用者想要导航b.getParent()
?
而你应该做的是:
a.getChildren().add( b );
b.setParent( a );
现在您的惯用Java代码继续工作(b.getParent()
行为正常)。现在,在这里,您将看到多个SQL语句。这就是为什么你需要选择一方作为“所有者”。
另外,虽然完全是下铺,但请考虑:
a.getChildren().add( b );
b.setParent( c );
这里写入数据库的是什么?双方都有效地为b
命名了另一个父母。那我们相信哪个?
答案 1 :(得分:0)
我认为article可能有所帮助,特别是我在下面提到的内容:
使单向不等于双向的“东西”是什么?当我们有单向关系时,只有Customer类具有对User类的引用;你只能通过Customer.getUser()获得用户,你不能做其他方式。当我们编辑User类时,我们启用了User来获取客户User.getCustomer()。
您也可以在SO上查看question。
此question看起来也非常有用。
主要区别在于双向关系提供双向导航访问,因此您可以在没有显式查询的情况下访问另一方。此外,它允许您向两个方向应用级联选项。
在这个主题上有很多关于SO的问题。这些对你很有帮助:))
答案 2 :(得分:0)
这样效率很低,因为保存B会发出
而不是
答案 3 :(得分:0)
通常每侧(一个/多个)有不同的实体类型,但在这种情况下,单个实体类型在两侧。因此,当你说你正在更新双方时,你实际上只更新一方因为只有一个实体(这里是B)......因为这是一个与自身连接的实体,所以Hibernate知道它映射的是什么。 / p>
至于你没有选择一方担任关系所有者会遇到什么问题:我刚刚在我自己的代码中尝试了这个问题(删除了我某个实体的@OneToMany一侧的“mappedBy”),并且运行时获得“java.sql.BatchUpdateException:failed batch”。简而言之,您将从Hibernate获得奇怪的SQL异常,无法正确映射您的类。