如果可能,我有几个关于春季交易的问题。 让我们假设我有这个DAO课程:
public class MyDAO {
/**
* verifies if a certain record in DB contains 'True' in a certain Column named publishFlag
*/
@Transactional
public bloolean isBeingPublished(Long recordID){
...
}
/**
* sets the record's publishFlag column to true indicating that it's being published
*/
@Transactional
public boolean setBeingPublished(Long recordID){
...
}
}
以下课程使用它:
public class MyClass {
@Autowired
MyDAO dao;
public void publishRecords(List<Long> ids){
for(Long id : ids){
if(!dao.isBeingPublished(id)){
dao.setBeingPublished(id);
//do something to publish the record
}
}
}
}
我的问题是:
首先,!dao.isBeingPublished(id)
和dao.setBeingPublished(id)
是在同一交易中执行还是在单独交易中执行?
关于并发性的第二个问题,可以创建多个MyClass
实例,并且可以对publishRecord
方法进行并发调用,因此对!dao.isBeingPublished(id)
的两个并发调用可能都会提供相同的结果,从而使记录发布两次!
我会考虑使publishRecords
同步,但应用程序可能部署在多个服务器上,这使得同步声明无效,因此我对事务的问题,因为数据库是这些服务器上部署的应用程序之间唯一的共享资源。
究竟是什么解决了我的问题?我读到了关于spring的事务传播,并发现REQUIRES_NEW
会创建一个新事务,即使当前正在执行一个事务,但是,我仍然无法看到这将如何解决我的问题。
提前感谢您的帮助。
答案 0 :(得分:3)
很少有事情需要考虑,DAO专注于单个实体的操作,而服务专注于一个或多个实体的操作,因此事务应该放在服务层上,这样你就可以重用DAO的操作而不需要任何事务,但是让服务决定何时开始和结束交易
<强>接口强>
public interface MyClass {
public void publishRecords(List<Long> ids);
}
<强>实施强>
@Service
@Transactional(readOnly = false)
class DefaultMyClass implements MyClass {
@Autowired
MyDAO dao;
// single transaction
@Override
public void publishRecords(List<Long> ids) {
for(Long id : ids){
if(!dao.isBeingPublished(id)){
dao.setBeingPublished(id);
//do something to publish the record
}
}
}
}
<强> DAO 强>
class MyDAO {
public bloolean isBeingPublished(Long recordID){
// bigbang
}
public boolean setBeingPublished(Long recordID){
// bigbang
}
}
使用上述设计,两个问题都在解决。
答案 1 :(得分:1)
首先,将!dao.isBeingPublished(id)和 dao.setBeingPublished(id)可以在同一个事务中执行,也可以在 单独的?
除非有一个用@Transactional
注释的方法,否则它们将在不同的交易中发生,所以是的,你将有可能出现竞争条件。
如果我是你,我会抛弃isBeingPublished
和setBeingPublished
,转而使用一个@Transactional
publishIfPossible
方法,该方法返回一个布尔值,表明它是否能够获取数据库行锁并执行发布操作。