【发布时间】:2011-06-28 17:05:49
【问题描述】:
我正在尝试编写一个方法,该方法将基于唯一但非主键返回一个 Hibernate 对象。如果实体已经存在于数据库中,我想返回它,但如果它不存在,我想创建一个新实例并在返回之前保存它。
更新:让我澄清一下,我正在为其编写的应用程序基本上是输入文件的批处理器。系统需要逐行读取文件并将记录插入到数据库中。文件格式基本上是我们模式中几个表的非规范化视图,所以我要做的是解析父记录,或者将其插入数据库,以便我可以获得一个新的合成键,或者如果它已经存在,则选择它。然后我可以在具有外键的其他表中添加额外的关联记录回到该记录。
这变得棘手的原因是每个文件都需要完全导入或根本不导入,即对给定文件所做的所有插入和更新都应该是一个事务的一部分。如果只有一个进程在执行所有导入,这很容易,但如果可能的话,我想将其拆分到多台服务器上。由于这些限制,我需要能够留在一个事务中,但要处理已经存在记录的异常。
父记录的映射类如下所示:
@Entity
public class Foo {
@Id
@GeneratedValue(strategy = IDENTITY)
private int id;
@Column(unique = true)
private String name;
...
}
我最初编写此方法的尝试如下:
public Foo findOrCreate(String name) {
Foo foo = new Foo();
foo.setName(name);
try {
session.save(foo)
} catch(ConstraintViolationException e) {
foo = session.createCriteria(Foo.class).add(eq("name", name)).uniqueResult();
}
return foo;
}
问题是当我要查找的名称存在时,调用 uniqueResult() 会引发 org.hibernate.AssertionFailure 异常。完整的堆栈跟踪如下:
org.hibernate.AssertionFailure: null id in com.searchdex.linktracer.domain.LinkingPage entry (don't flush the Session after an exception occurs)
at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:82) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:190) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:147) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1185) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1709) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.uniqueResult(CriteriaImpl.java:369) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
有谁知道是什么导致这个异常被抛出? hibernate 是否支持更好的方法来实现这一点?
让我先解释一下为什么我要先插入,然后再选择是否以及何时失败。这需要在分布式环境中工作,因此我无法在检查中同步以查看记录是否已经存在和插入。最简单的方法是让数据库通过检查每次插入时是否违反约束来处理这种同步。
【问题讨论】:
-
“分布式环境”是什么意思?您是否使用基于网格的 RDBMS?
-
@vbence 分布式 我的意思是我有多个客户端在不同的机器上针对单个集中式数据库运行此代码。两个不同的客户端可以尝试同时插入相同的记录,在这种情况下,“第一个”客户端应该获胜并被持久化,而另一个客户端应该只返回已经持久化的内容。
-
您真的没有看到不正确分解数据的后果吗?
-
@Thomas,我不确定您所说的“正确分解数据”是什么意思。
-
您是否考虑过对批处理数据进行预处理,以免发生冲突?还是简单地先在一个线程中处理冲突的部分?
标签: java hibernate unique-constraint upsert unique-key