Hibernate JPA和Spring交易-请解释
今天,我为我的应用程序编写了一个测试用例,以查看事务的行为。我发现没有什么能像我想象的那样工作 我有一个基于Spring的应用程序,使用Hibernate作为JPA提供程序,由MySQL支持。 我有DAO对象,扩展了Spring的JpaDaoSupport。Spring的事务管理涵盖了这些方面 我创建的测试用例如下所示: 1) 创建了一个实体,其中一些计数器设置为0。 2) 然后创建两个线程,它们都在循环中调用DAO方法incrementCounter() 我认为当DAO方法被事务覆盖时,只有一个线程在其中(即Spring将负责同步)。但这被证明是错误的假设 通过向DAO方法中添加Hibernate JPA和Spring交易-请解释,hibernate,spring,jpa,transactions,persistence,Hibernate,Spring,Jpa,Transactions,Persistence,今天,我为我的应用程序编写了一个测试用例,以查看事务的行为。我发现没有什么能像我想象的那样工作 我有一个基于Spring的应用程序,使用Hibernate作为JPA提供程序,由MySQL支持。 我有DAO对象,扩展了Spring的JpaDaoSupport。Spring的事务管理涵盖了这些方面 我创建的测试用例如下所示: 1) 创建了一个实体,其中一些计数器设置为0。 2) 然后创建两个线程,它们都在循环中调用DAO方法incrementCounter() 我认为当DAO方法被事务覆盖时,只有一
synchronized
(临时)解决了这个问题之后,我发现Hibernate没有存储DAO方法所做的更改,而另一个线程在查找实体时,有旧数据。仅显式调用this.getJpaTemplate().flush()代码>帮助
我还认为实体管理器会从持久性上下文的缓存中为我提供相同的实体实例,但这也是错误的。我已经检查了hashCode()和equals(),它们都很好——基于实体的bussines键
欢迎任何评论,因为我似乎错过了JPA/Spring如何处理事务的一些基本概念
DAO方法是否应该同步
我应该在每个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
…等等,这意味着一个线程已经覆盖了另一个线程的更改
DAO方法应该同步吗?我的通常不是,因为他们没有任何状态。没必要李>
我应该在每个DAO方法的末尾调用flush()吗?不,我不这么做
Spring是否负责使对DAO方法的调用以事务方式进行?(即不允许两个线程同时使用同一实体)。我认为您混淆了事务、隔离和同步李>
如果没有,我如何做到这一点?我认为您必须担心同步和隔离李>
你的例子不是我认为的刀。我认为你的测试真的不是正确的习惯用法。如果我有一个对象Foo,我会有一个FooDao接口,该接口将声明该对象的CRUD方法:
public interface FooDao
{
List<Foo> find();
Foo find(Serializable id);
void saveOrUpdate(Foo foo);
void delete(Foo foo);
}
公共接口FooDao
{
列表查找();
Foo-find(可序列化的id);
作废保存或更新(Foo-Foo);
作废删除(Foo-Foo);
}
正如您从示例中猜到的那样,可以编写通用DAO
Spring通常有一个服务层,它使用域和持久性对象来实现用例。这就是需要声明事务的地方,因为工作单元与用例而不是DAO相关联。DAO无法知道它是否是更大事务的一部分。这就是为什么事务通常在服务上声明
public interface FooDao
{
List<Foo> find();
Foo find(Serializable id);
void saveOrUpdate(Foo foo);
void delete(Foo foo);
}
公共接口FooDao
{
列表查找();
Foo-find(可序列化的id);
作废保存或更新(Foo-Foo);
作废删除(Foo-Foo);
}
正如您从示例中猜到的那样,可以编写通用DAO
Spring通常有一个服务层,它使用域和持久性对象来实现用例。这就是需要声明事务的地方,因为工作单元与用例而不是DAO相关联。DAO无法知道它是否是更大事务的一部分。这就是事务通常在服务上声明的原因。在我使用过的大多数应用程序中,事务通常在DAO之上的服务层声明。这意味着,如果您在不同实体之间进行一些插入,那么它可以具有事务语义 答案取决于您的应用程序:
- 如果您希望在同一个实体上有很多争用,那么可以使用它来获得数据库锁,这样另一个线程就不能在上面更新。与同步DAO类似,这将限制应用程序的整体性能,您需要注意避免死锁
- 如果您希望这种情况很少发生,请使用乐观锁定,它将维护一个版本列,如果更新丢失,将抛出一个异常以在应用程序代码中处理
- 如果您希望在同一个实体上发生大量争用,那么可以使用