关于Spring事务控制方面的问题,就是不在service层做控制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于Spring事务控制方面的问题,就是不在service层做控制相关的知识,希望对你有一定的参考价值。

现在我在写一个小系统,需要引入事务控制相关的东西。
原来与spring事务都是在service层做的,现在我这个系统有需求是在controller层(action层)做事务控制,请问这个可能吗?还有有没有具体的实现方法和事例。

同意1楼的兄弟说的,业务逻辑应该由service处理,造成需要在action开启事务的最根本原因主要是你的组件层API设计有问题,action中不该处理业务逻辑代码的,需要调多个service完成的功能其实就是一个业务,不过不按1楼说的把action放到spring托管也有办法在action手动开启事务,下边是我以前写的代码,当时是为了解决遗留系统的事务问题,你可以参考测试一下:
public interface ITransactionManager
/**
* 该方法中是需要进行事务控制的内容
* @return
* @throws Exception
*/
Object doInTransaction() throws Exception;

public class DataSourceTransactionUtil
public Object execute(final ITransactionManager transactionManager)
DataSourceTransactionManager dataSourceTransactionManager = getDataSourceTransactionManager();
TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager);
return transactionTemplate.execute(new TransactionCallback()
public Object doInTransaction(TransactionStatus transactionStatus)
Object savepoint = transactionStatus.createSavepoint();
Object result = null;
try
result = transactionManager.doInTransaction();
catch(Exception e)
transactionStatus.rollbackToSavepoint(savepoint);
e.printStackTrace();
finally
transactionStatus.releaseSavepoint(savepoint);

return result;

);


private DataSourceTransactionManager getDataSourceTransactionManager()
return (DataSourceTransactionManager)getBean("transactionManager");


解释一下:
这段代码我记得是看了spring源码后自己写的。
1、getBean("transactionManager");是获得spring中事务管理器那个bean,你自己改名字。
2、把这段代码放到你的BaseAction中:
protected Object beginTransaction(ITransactionManager transactionManagerImpl)
DataSourceTransactionUtil dt = new DataSourceTransactionUtil();
return dt.execute(transactionManagerImpl);

3.在你需要开启事务的action中调用beginTransaction方法transactionManagerImpl这个对象你写个匿名内部类然后实现doInTransaction() 方法,在doInTransaction() 中去写你调用多个service的代码
参考技术A 调用的service层方法不要加事务,直接试试用@transaction到controller层方法呢。
controller层一个业务方法调用多个service层方法,是完全正常的,controller层仅仅做业务逻辑处理而不调用多个service层方法太少了。可以事务用于controller层,自己的理解:一个controller层的方法才是真正的业务方法,才是一个完整的业务块,数据就应该一致,所以事务就该加在该层(讨论交流,允许不一样的声音?)
参考技术B 做是肯定可以。但是不怎么好。
首先action应该只有具有请求services与基本逻辑控制,真正的业务处理与数据交互应该是放在service层。所以事物在这里再好不过了。建议考虑一下
如果要配置,把之前spring配置路径改成action的,然后在方法上加上Transactional注解就可以了吧。不知道你系统是不是这样。谢谢。追问

是的,我看到大部分都是放在service层的,不过我现在一个action方法里面一般都有几个service方法的调用 ,要这些全部成功了,成一次提交到数据库的,所以 我才想到在action层上做事务控制的,不知道这样会有什么后果

追答

这个问题。所以说还是跟程序的结构有关。
如果你action没有多个service方法。而是将其在service层统一处理。现在也没这些事。
配置在action层没什么后果、只是你的结构对于MVC设计模式不是很规范而已。
就是说你现在配置在action层应该最好。

关于spring事务提交的传播行为

保持事务一致性

技术图片

首先我们的spring配置文件的配置如上图,除了指定方法外其他都是受事务控制,在某个aop切面配置路径下,如果方法有异常 则进行回滚,并且还是方法内涉及到增删改的回滚;

关于事务的使用:

为了保证整体方法的事务一致性,方法内如果有多处对数据进行增删改,那么最好提取成一个service进行事务控制,如果全成功,则全部提交,否则全部回滚;

以下主要是在工作中遇到的实际问题:

Service层test()方法:

while(条件)
    // 满足条件,则对数据库增删改

由于公司限制,不允许贴代码

这里我在Service中进行一个wile循环,如果满足条件,我就对满足条件的这一条数据进行多次增删改;

这里有个问题,不论是否出现异常,我都catch处理了,导致了事务的大量堆积,因为while循环没跑完,test()方法没执行完,事务并不会进行提交,如果while循环中的数据量过大,很有可能造成锁库锁表的可能,会造成很多问题;

实际工作中的解决方案:

方案一:向上抽取,分割成多个独立事务

我们将test()方法中的while(条件)循环向上抽取,抽取至Controller层中,然后对满足while条件的每一条数据处理时放至一个service中,这样每次循环一次时,就整体对while中的处理提交一次事务,完美解决;

方案二:事务传播,嵌套事务,创建新的事务

带有注解@Transactional(propagation=Propagation.REQUIRES_NEW)的方法走完之后,数据就会被提交入库

 while循环中如果对数据库的交互仅有一次,其他都为查询时,我建议使用这个注解,也就是说 service方法中嵌套另一个service方法,第二个service方法加上此注解,那么第二个servie方法就是新创建的事务,并进行独立提交入库;

另外需要注意方法内部调用@Transactional(propagation=Propagation.REQUIRES_NEW)注解不生效,比如A和B都在同一个方法中,A调用B,B方法是此注解,则不生效。同样,AOP拦截也拦截不到B

----REQUIRES_NEW传播说明-----------------------------------------------------------------------------------------
@Service
public class TestUserImpl implements TestUser 

    @Transactional(propagation=Propagation.REQUIRED )
    public void saveUserAll()
        //1、保存用户--
        this.saveUser();
        //2--情况一、记录日志的方法在本service-新事务
        this.saveUserLog();
        //2--情况二、记录日志的方法在另一个LogService-新事务
        logService.saveUserLog();

         int a=1/0;//异常
    
   
    public void saveUser() 
        User user = new User();
        userDao.save(user);

    

    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public void  saveUserLog() 
        UserLog log = new UserLog();
        UserLogDao.save(log);
    

------------------------------saveUserAll在位置一,出异常,执行结果------------------------------------
       情况一、记录日志的方法在本service,saveUser回滚,saveUserLog回滚
       情况二、记录日志的方法在另一个LogService-新事务,saveUser回滚,saveUserLog不回滚
总结:要想总的方法的异常不影响 新事务方法REQUIRES_NEW的提交,新事务的方法,要写在另一个service里

以上是个人工作中的一些见解和想法,如有错误,欢迎指正。

 

以上是关于关于Spring事务控制方面的问题,就是不在service层做控制的主要内容,如果未能解决你的问题,请参考以下文章

Spring事务隔离级别和传播特性

关于spring事务提交的传播行为

spring中的事物都有哪些

Spring事务传播性与隔离级别

深入剖析Mysql事务和Spring事务

Spring面试题汇总4:关于Spring事务的面试题