我正在尝试编写一种方法,该方法将基于唯一但非主键返回 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 是否支持更好的方法来实现这一点?
让我也先发制人地解释为什么我要先插入,然后再选择是否以及何时失败。这需要在分布式环境中工作,因此我无法在检查中同步以查看记录是否已经存在和插入。最简单的方法是让数据库通过检查每次插入时的约束冲突来处理这种同步。