当前位置: 首页 > 面试题库 >

使用Hibernate根据唯一键查找或插入

澹台权
2023-03-14
问题内容

我正在尝试编写一个将基于唯一但非主键返回Hibernate对象的方法。如果实体已经存在于数据库中,我想返回它,但是如果不存在,我想创建一个新实例并在返回之前保存它。

更新:
让我澄清一下,我为此编写的应用程序基本上是输入文件的批处理程序。系统需要逐行读取文件并将记录插入数据库。文件格式基本上是我们架构中几个表的非规范化视图,因此我要做的是解析父记录,或者将其插入db中,以便获得新的合成键,或者如果它已经存在,则选择它。然后,我可以在具有外键的其他表中添加其他关联的记录。

之所以变得棘手,是因为每个文件都需要完全导入或根本不导入,即,为给定文件完成的所有插入和更新都应该是一个事务的一部分。如果只有一个进程完成所有导入,则这很容易,但是我想尽可能在​​多个服务器之间进行分解。由于这些限制,我需要能够留在一个事务中,但是要处理已经存在记录的异常。

父记录的映射类如下所示:

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

有谁知道导致此异常的原因是什么?休眠是否支持实现此目的的更好方法?

让我也先解释一下为什么我先插入然后选择是否以及何时失败。这需要在分布式环境中工作,因此我无法在检查之间进行同步以查看记录是否已存在以及插入是否存在。最简单的方法是让数据库通过检查每个插入上的约束冲突来处理此同步。


问题答案:

我有类似的批处理要求,进程运行在多个JVM上。我为此采取的方法如下。这很像jtahlborn的建议。但是,正如vbence所指出的,如果您使用NESTED事务,则当您遇到约束违例异常时,您的会话将无效。相反,我使用REQUIRES_NEW,它会挂起当前事务并创建一个新的独立事务。如果新事务回滚,则不会影响原始事务。

我正在使用Spring的TransactionTemplate,但如果您不希望依赖Spring,可以肯定可以轻松地对其进行翻译。

public T findOrCreate(final T t) throws InvalidRecordException {
   // 1) look for the record
   T found = findUnique(t);
   if (found != null)
     return found;
   // 2) if not found, start a new, independent transaction
   TransactionTemplate tt = new TransactionTemplate((PlatformTransactionManager)
                                            transactionManager);
   tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
   try {
     found = (T)tt.execute(new TransactionCallback<T>() {
        try {
            // 3) store the record in this new transaction
            return store(t);
        } catch (ConstraintViolationException e) {
            // another thread or process created this already, possibly
            // between 1) and 2)
            status.setRollbackOnly();
            return null;
        }
     });
     // 4) if we failed to create the record in the second transaction, found will
     // still be null; however, this would happy only if another process
     // created the record. let's see what they made for us!
     if (found == null)
        found = findUnique(t);
   } catch (...) {
     // handle exceptions
   }
   return found;
}


 类似资料:
  • 我正在尝试我们非主键作为外键在我的应用程序。场景如下:我有EMPLOYEE和EMPLOYEE_PROPERTIES表。Employee和Employee属性之间存在一对多的关系。下面是我的架构: 下面是我的hibernate映射XML:------------------------------- -------------员工属性------------------- 是否可以引用非主键作为外键

  • 我有以下问题,我有一个已经存在的表,有三个字段field1,field2,field3。Field1实际上是另一个表的外键(@OneToOne)。所有字段2和字段3都可以为空,所以我不能为所有三个字段设置主键。在数据库中,field1 field2 field3有一个唯一约束。 我用JPA/Hibernate尝试了几种解决方案,但没有找到一个好的。如果不定义@Id,JPA当然无法工作。在@Embe

  • 我是hibernate新手,我遇到了以下问题。“唯一索引或主键冲突”。问题的出现是由于错误的映射,但我花了几个小时来找出为什么会发生这种情况。 我有一个超级类叫做数据结构 然后是关联两个元素的类关联。这里省略了类的某些部分,只是为了简化它。 这个类作为两个数据结构类型类之间的中间类。像这样。 TP-协会-TP TP等级: 或者激活类 总的来说,我增加了以下内容: 当我尝试添加相同的对象时,问题就出

  • 我正在两个应用程序之间进行基准测试,一个是用构建的,另一个是用构建的。总体而言,差异不太大(小于一个数量级)。唯一的例外是当我通过主键获取记录时 使用JDBC的时间不超过10毫秒,使用Hibernate的时间从不低于60毫秒。我已经安装了ehcache,但是对于这个特定的查询,差异仍然是一样的<有没有什么我不知道的技巧? ---编辑 JPA映射和类是使用Hibernate工具构建的,它们没有什么特

  • 我正在尝试实现一个用户身份验证表。我正在进行用户注册,我的控制台打印出来: org.h2.jdbc.唯一索引或主键冲突:"PUBLIC.USERS_USERNAME_UINDEXPUBLIC.USERS(USERNAME)VALUES 1";SQL语句: INSERT INTO USERS(FIRST_NAME,LAST_NAME, USERNAME, PASSWORD)VALUES(?,?,?,

  • 假设我有一个名为“student_course”的Dynamo DB表。我想存储每个学生在大学学习的课程。一个学生可以一次学习多个课程,一个课程可以一次有多个学生。所以基本上它是一个多映射。 我的数据访问模式只有一个用例- 一次获取一个学生和一门课程的记录,即获取每个学生ID和CourseId组合的数据。它保证对于学生ID和课程ID组合,只有一条记录可用。 为了实现这一点,我可以通过以下两种方式存