我刚开始使用Hibernate,虽然有大量的例子要看,但这里似乎有太多的灵活性,有时很难缩小所有的选项--最好的方法。我做一个项目已经有一段时间了,尽管我读了很多书、文章和论坛,但我还是有点头脑发热。任何经验丰富的建议都将不胜感激。
因此,我有一个模型,涉及两个类,一对多的关系从父母到孩子。每个类都有一个代理主键和一个受唯一约束的复合业务密钥。
<class name="Container">
<id name="id" type="java.lang.Long">
<generator class="identity"/>
</id>
<properties name="containerBusinessKey" unique="true" update="false">
<property name="name" not-null="true"/>
<property name="owner" not-null="true"/>
</properties>
<set name="items" inverse="true" cascade="all-delete-orphan">
<key column="container" not-null="true"/>
<one-to-many class="Item"/>
</set>
</class>
<class name="Item">
<id name="id" type="java.lang.Long">
<generator class="identity"/>
</id>
<properties name="itemBusinessKey" unique="true" update="false">
<property name="type" not-null="true"/>
<property name="color" not-null="true"/>
</properties>
<many-to-one name="container" not-null="true" update="false"
class="Container"/>
</class>这些映射背后的bean是你所能想象到的最无聊的东西--没有什么好想象的。考虑到这一点,请考虑以下代码:
Container c = new Container("Things", "Me");
c.addItem(new Item("String", "Blue"));
c.addItem(new Item("Wax", "Red"));
Transaction t = session.beginTransaction();
session.saveOrUpdate(c);
t.commit();第一次一切都很好,Container和Item都是持久化的。但是,如果再次执行上述代码块,Hibernate将抛出"name“和"owner”列的重复值ConstraintViolationException。因为新的Container实例有一个空标识符,Hibernate假定它是一个未保存的瞬态实例。这是预期的,但不是期望的。由于持久和瞬态Container对象具有相同的业务键值,所以我们真正想要的是发出更新。
让Hibernate相信我们的新Container实例和旧的实例是一样的,这是非常容易的。通过快速查询,我们可以获得要更新的Container的标识符,并将瞬态对象的标识符设置为匹配。
Container c = new Container("Things", "Me");
c.addItem(new Item("String", "Blue"));
c.addItem(new Item("Wax", "Red"));
Query query = session.createSQLQuery("SELECT id FROM Container" +
"WHERE name = ? AND owner = ?");
query.setString(0, c.getName());
query.setString(1, c.getOwner());
BigInteger id = (BigInteger)query.uniqueResult();
if (id != null) {
c.setId(id.longValue());
}
Transaction t = session.beginTransaction();
session.saveOrUpdate(c);
t.commit();这几乎满足Hibernate的要求,但是由于从Container到项目的一对多关系级联,所以对于子Item对象也会抛出相同的ConstraintViolationException。
我的问题是:在这种情况下,最佳做法是什么?强烈建议使用代理项主键,还建议使用业务密钥相等。但是,当您将这两项建议结合在一起时,Hibernate的两个最大便利--saveOrUpdate和级联操作--似乎几乎完全没有用处。在我看来,我只有两种选择:
有更好的办法吗?我是不是做错了假设?我希望我只是错过了什么。
谢谢。
发布于 2009-06-17 17:47:40
在添加子容器之前加载容器(不管怎样,这是一个好主意),这样您就可以利用“set”行为。当您将项目添加到容器中时,它将不会被添加。
哦,您还需要确保在Item中覆盖等于和hashcode,以确保事情按照预期的方式工作。
所以..。
Query query = session.createSQLQuery("FROM Container " +
"WHERE name = ? AND owner = ?");
query.setString(0, "Things");
query.setString(1, "Me");
Container c = (BigInteger)query.uniqueResult();
c.addItem(new Item("String", "Blue"));
c.addItem(new Item("Wax", "Red"));
Transaction t = session.beginTransaction();
session.saveOrUpdate(c);
t.commit();发布于 2009-06-17 17:59:18
这两项建议(代理主键、业务主键)相互冲突,因为它们被推荐用于不同的上下文。面向对象的编程似乎在代理主键方面做得更好。Hibernate期望您进行面向对象的编程,因为Hibernate的目的是允许您在透明地将对象持久化到数据库的同时进行面向对象的编程。
https://stackoverflow.com/questions/1008519
复制相似问题