业务密钥相等的级联更新:Hibernate最佳实践? [英] Cascading updates with business key equality: Hibernate best practices?

查看:83
本文介绍了业务密钥相等的级联更新:Hibernate最佳实践?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Hibernate的新手,尽管看上去有很多例子,但在这里似乎有很大的灵活性,有时候很难将所有选项都缩小到最好的方式。我一直在为一个项目工作了一段时间,尽管读了很多书籍,文章和论坛,但我还是留下了一些头脑。任何经验丰富的建议将非常感激。

因此,我有一个模型,涉及两个父类之间的一对多关系的类。每个类都有一个代理主键和一个唯一约束的组合业务键。

 < class name =Container> 
< id name =idtype =java.lang.Long>
< generator class =identity/>
< / id>
< properties name =containerBusinessKeyunique =trueupdate =false>
< property name =namenot-null =true/>
< property name =ownernot-null =true/>
< / properties>
< set name =itemsinverse =truecascade =all-delete-orphan>
< key column =containernot-null =true/>
< / set>
< / class>

< class name =Item>
< id name =idtype =java.lang.Long>
< generator class =identity/>
< / id>
< properties name =itemBusinessKeyunique =trueupdate =false>
< property name =typenot-null =true/>
< property name =colornot-null =true/>
< / properties>
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 s是持久的。但是,如果再次执行上述代码块,Hibernate将抛出 ConstraintViolationException - name和owner列的重复值。因为新的 Container 实例有一个空的标识符,所以Hibernate假定它是一个未保存的瞬态实例。这是预期的但不是所期望的。由于持久和暂时的 Container 对象具有相同的业务键值,我们真正想要发布更新。



很容易让Hibernate说服我们新的 Container 实例与我们的旧实例相同。通过快速查询,我们可以获得我们想要更新的 Container 的标识符,并将我们的临时对象的标识符设置为匹配。

 容器c =新的容器(物品,我); 
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,但是由于中的一对多关系,对于子项> Item 对象,也会抛出相同的 ConstraintViolationException



我的问题是:这种情况下的最佳做法是什么?强烈建议使用代理主键,并且还建议使用业务键相等。然而,当你把这两个建议放在一起练习时,Hibernate的两个最大的便利 - saveOrUpdate和级联操作 - 似乎几乎变得毫无用处。正如我所看到的,我只有两个选择:


  • 手动获取并设置映射中每个对象的标识符。这很明显,但对于一个中等大小的模式来说,这是很多额外的工作,看起来Hibernate很容易做到。

  • 编写一个自定义拦截器来获取和设置每个对象标识符操作。这看起来比第一个选项更干净,但是相当笨拙,我认为你应该编写一个插件来覆盖Hibernate的默认行为,以符合推荐设计的映射,这似乎是错误的。
  • b $ b


有更好的方法吗?我是否完全错误的假设?我希望我只是缺少一些东西。



谢谢。

解决方案

在添加子项之前加载容器(无论如何都是个好主意),这样你就可以利用'set'行为。当您将Item添加到容器时,它不会被添加。



哦,您还需要确保在Item中覆盖equals和hashcode以确保工作正常

 查询查询= session.createSQLQuery(FROM Container+ 
where name =?AND owner =?);
query.setString(0,Things);
query.setString(1,Me);
容器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();


I'm new to Hibernate, and while there are literally tons of examples to look at, there seems to be so much flexibility here that it's sometimes very hard to narrow all the options down the best way of doing things. I've been working on a project for a little while now, and despite reading through a lot of books, articles, and forums, I'm still left with a bit of a head scratcher. Any veteran advice would be very appreciated.

So, I have a model involving two classes with a one-to-many relationship from parent to child. Each class has a surrogate primary key and a uniquely constrained composite business key.

<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>

The beans behind these mappings are as boring as you can possibly imagine--nothing fancy going on. With that in mind, consider the following code:

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();

Everything works fine the first time, and both the Container and its Items are persisted. If the above code block is executed again, however, Hibernate throws a ConstraintViolationException--duplicate values for the "name" and "owner" columns. Because the new Container instance has a null identifier, Hibernate assumes it is an unsaved transient instance. This is expected but not desired. Since the persistent and transient Container objects have the same business key values, what we really want is to issue an update.

It is easy enough to convince Hibernate that our new Container instance is the same as our old one. With a quick query we can get the identifier of the Container we'd like to update, and set our transient object's identifier to match.

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();

This almost satisfies Hibernate, but because the one-to-many relationship from Container to Item cascades, the same ConstraintViolationException is also thrown for the child Item objects.

My question is: what is the best practice in this situation? It is highly recommended to use surrogate primary keys, and it is also recommended to use business key equality. When you put these two recommendations in to practice together, however, two of the greatest conveniences of Hibernate--saveOrUpdate and cascading operations--seem to be rendered almost completely useless. As I see it, I have only two options:

  • Manually fetch and set the identifier for each object in the mapping. This clearly works, but for even a moderately sized schema this is a lot of extra work which it seems Hibernate could easily be doing.
  • Write a custom interceptor to fetch and set object identifiers on each operation. This looks cleaner than the first option but is rather heavy-handed, and it seems wrong to me that you should be expected to write a plug-in which overrides Hibernate's default behavior for a mapping which follows the recommended design.

Is there a better way? Am I making completely the wrong assumptions? I'm hoping that I'm just missing something.

Thanks.

解决方案

Load the container before you add the children (tis a good idea anyway), that way you will be able to take advantage of the 'set' behaviour. When you add the Item to container it will not be added.

Oh, you'll also need to make sure you override equals and hashcode in Item to ensure things work the way you expect.

So...

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();

这篇关于业务密钥相等的级联更新:Hibernate最佳实践?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆