2011-03-25 71 views
2

我一直在努力解决这个问题了几天,我不能完全发现,我很高兴与解决方案持续对象时。我可以通过不同程度的间接方式解决它,但这似乎是我应该能够做的事情,而我发现其他一些人可能有我的问题,但他们中的任何一个似乎都没有确切的问题或没有提供答案。遭遇数据库死锁与JPA使用持续引用

虽然我这样做过成功使用Hibernate,我一直没能得到它与J​​PA,这是我专注于现在所做的工作。

初步信息:使用所述持续扩展

  • 吉斯3.0应用,休眠(W /注释),和C3P0(带有至多5个连接)。我们正在从提供者注入EntityManager,并且我可以确认EntityManager对象与整个此过程中的事务对象保持相同。
  • 作为Web应用程序的一部分设计,目前的问题只发生在使用JUnit和dbUnit的自动化集成测试中。访问是针对单例对象(.in(Scopes.SINGLETON)),但该Singleton对象仅依赖于注入器,@Transactional是线程安全的。
  • 在数据库存在被映射到一个注释FileContainer和被映射到对象MimeType的MIME类型表中的文件表。在应用程序中,当我们创建一个新文件时,我们首先检索MIMEType对象作为NamedQuery并保存它。
  • 数据库是PostgreSQL 8.4,使用postgresql-8.4-702.jdbc3.jar。
  • 该系统是适度的挑剔下,它失败的情况:我需要生成一个单独的线程,并从那里进行访问。

在第FileContainer对象mime类型字段的映射是:

@ManyToOne(fetch=FetchType.EAGER, cascade={}) 
@JoinColumn(name="mimetype_id", referencedColumnName="id", nullable=false, updatable=true) 

将mimeType对象被注释如下:

@Entity 
@Table(name="mimetypes") 
@org.hibernate.annotations.Immutable 
@Cacheable(true) 
@NamedQuery(name="mt.ext", query="from MIMEType where extension = :ext", 
    hints= {@QueryHint(name="org.hibernate.fetchSize", value="1"), 
      @QueryHint(name="org.hibernate.readOnly", value="true")}) 

以上所有注释均为javax.persistence版本。

addFile方法注解为@com.google.inject.persist.Transactional。将此更改为在类中使用注入的EntityManager似乎不会改变结果。

用于创建对象的方法是这样的:

  1. 抓住的EntityManager和从喷射器的FileDAO。这些都来自提供者。
  2. 抓住我们会从数据库中调用em.createNamedQuery使用的MIME类型对象(“mt.ext”,MIMEType.class).setParameter(“分机”,扩展)。getSingleResult();
  3. 创建FileContainer对象,并填充它,包括`setMimetype(MIME类型)的调用`与对象,我们只是取回
  4. 呼叫dao.save(fileContainer),它设置在FileContainer对象的基本字段,然后调用em.persist(Object)。把它从DAO中拉出来,并使用之前注入的EntityManager不会改变结果。

此时,只要发生EntityManager.flush()(无论发生什么情况 - 在事务结束时或在它之前)INSERT发生并且代码发生死锁。

当我检查pg_stat_activity并将其与pg_locks进行比较时,我看到insert语句被一个“idle in transaction”连接阻塞,并且它处于等待状态。删除MIMEType的插入(并将列设置为允许空值)允许代码正常进行,并检查MIMEType的值表明它已正确检索。

任何想法或想法表示赞赏。

编辑:

这是层级,希望它澄清操作的顺序和正在发生的事情:

  • 测试工具(初始化喷油器,并使用转轮把它注入到具体的测试班)
    • 测试用例
      • 开始线程
      • 在线程内的单例对象(使用相同注入器初始化)上输入事务块,确认所有DAO对象中的对象都是相同的,并且发生错误的位置也是如此。
      • EntityManager对象始终通过injector.getInstance(EntityManager.class)调用或通过获取Provider对象获得。
      • 通过上述过程,所有在同一个线程。
      • 冻结flush()

的代码没有出现有什么,应该是本地线程正在被线程和一切之间共享正在与喷油器,这是初始化是什么莫名其妙对我来说。

回答

2

我发现了这个问题。

在代码中的其他地方,我有一个无关的但正在进行的事务,正在@Before方法中启动。由于某种原因,EntityManager在该交易完成时被阻止。当一切都在同一个线程中并且对于某些只读操作时,这并不是一个问题,但是当它被拆分成一个单独的线程并尝试写入时,它开始等待另一个事务,而这个事务并没有在时间结束的交易,并触及许多相同的表格。

3

我从你在这里说,很可能是这个问题捡的唯一事情是这样的:

该系统是适度的挑剔下,它失败的情况:我需要产生一个单独的线程并从那里执行访问。

Guice-Persist的工作单位和交易范围为使用ThreadLocal的线程。如果你在一个单独的线程上做某件事,我肯定会看到类似的情况发生(尽管我不确定)。你能否更详细地解释何时以及如何使用这个单独的线程?从@Transactional方法里面?它是否使用在原始线程中注入的EntityManager

编辑:嗯......我没有看到你在做什么错。似乎工作和交易都在一个线程上完成。不知道那里发生了什么。

+0

当然,让我们看看我能否澄清这种情况。 – 2011-03-26 03:06:31