【发布时间】:2010-11-22 03:22:18
【问题描述】:
今天我为我的应用程序编写了一个测试用例,以了解事务的行为方式。而且我发现没有什么能像我想象的那样起作用。
我有一个基于 Spring 的应用程序,使用 Hibernate 作为 JPA 提供程序,由 MySQL 提供支持。 我有 DAO 对象,扩展了 Spring 的 JpaDaoSupport。这些都包含在 Spring 的事务管理中。
我创建的测试用例是这样的: 1) 创建了一个实体,其中一些计数器设置为 0。 2) 然后创建了两个线程,它们都在循环中调用了一个DAO方法incrementCounter()。
我认为当 DAO 方法被事务覆盖时,其中只有一个线程(即 Spring 将负责同步)。但这已被证明是错误的假设。
通过在DAO方法中添加synchronized(临时)解决这个问题后,我发现Hibernate不存储DAO方法所做的更改,而另一个线程在find()实体时,已经旧数据。只有明确调用 this.getJpaTemplate().flush(); 才有帮助。
我还认为实体管理器会从持久化上下文的缓存中给我相同的实体实例,但这也是错误的。我检查了 hashCode() 和 equals(),它们很好 - 基于实体的业务键。
欢迎任何 cmets,因为我似乎错过了 JPA / Spring 如何处理事务的一些基本概念。
- DAO 方法应该是
synchronized吗? - 我应该在每个 DAO 方法结束时调用 flush() 吗?
- spring 是否负责使对 DAO 方法的调用以事务方式运行? (即不要让两个线程同时处理同一个实体)
- 如果没有,我该如何实现?
请注意,在我的测试用例中,我使用单个 DAO 对象,但这应该没问题,因为 Spring 的 bean 是单例 - 对吧?
感谢您的帮助。
public class EntityDaoImpl extends JpaDaoSupport implements EntityDao {
public synchronized void incrementCounter( String znacka )
{
String threadName = Thread.currentThread().getName();
log.info(threadName + " entering do incrementCounter().");
Entity ent = this.getJpaTemplate().find( Entity.class, znacka );
log.info("Found an entity "+ent.getZnacka()+"/"+ent.hashCode()+" - " + ObjectUtils.identityToString( ent ) );
log.info(threadName + ": Actual count: "+ent.getCount() );
ent.setCount( ent.getCount() + 5 );
int sleepTime = threadName.endsWith("A") ? 700 : 50;
try { Thread.sleep( sleepTime ); }
catch( InterruptedException ex ) { }
ent.setCount( ent.getCount() + 5 );
this.getJpaTemplate().flush();
log.info(threadName + " leaving incrementCounter().");
}
}
没有synchronized 和flush(),这给了我这样的输出
Thread A: Actual count: 220
...
Thread B: Actual count: 220
...
Thread A: Actual count: 240
...
Thread B: Actual count: 250
...
Thread A: Actual count: 250
...等,意味着一个线程已经覆盖了另一个线程的更改。
【问题讨论】:
标签: hibernate spring jpa transactions persistence