springboot中的声明式事务管理及编程式事务管理
这⼏天在做⼀个功能,具体的情况是这样的:
  项⽬中原有的⼏个功能模块中有数据上报的功能,现在需要在这⼏个功能模块的上报之后⽣成⼀条消息记录,然后⼊库,在写个接⼝供前台来拉取消息记录。
  看到这个需求,⾸先想到的是使⽤AOP来实现了,然后,我去看了下现有功能模块中的代码,发现了问题,这些模块中的业务逻辑并没有放在service层来处理,直接在controller中处理了,controller中包含了两个甚⾄多个service处理,这样是不能保证事务安全的,既然这样,那么我们如何实现能保证事务安全呢。我想直接在controller中定义切⼊点,然后before中⼿动开启事务,在afterReturn之后根据需要来提交或者回滚事务。
  然后趁着这个机会就查了下spring boot中的事务这块,就从最基础的说起。
  1.spring boot中声明式事务的使⽤
  想要在spring boot中使⽤声明式事务,有两种⽅式,⼀种是在各个service层中添加注解,还有⼀种是使⽤AOP配置全局的声明式事务管理
  先来说第⼀种,需要⽤到两个注解就,⼀个是@EnableTransactionManagement⽤来开启注解事务管理,等同于xml配置⽅式的 <tx:annotation-driven />,另⼀个
是@Transactional
  具体代码如下:
ample.demo;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5import ansaction.annotation.EnableTransactionManagement;
6
7// @SpringBootApplication是Sprnig Boot项⽬的核⼼注解,主要⽬的是开启⾃动配置
10 @SpringBootApplication
11 @EnableTransactionManagement // 启注解事务管理,等同于xml配置⽅式的 <tx:annotation-driven />
12public class DemoApplication {
14public static void main(String[] args) {
16        SpringApplication.run(DemoApplication.class, args);
18    }
19
20 }
  然后,注解@Transactional直接加在service层就可以了,放两个service⽤来验证事务是否按预期回滚
ample.demo.service;
ample.demo.bean.ResUser;
import ansaction.annotation.Transactional;
import java.util.List;
/**
* 注解加在接⼝上表名接⼝的所有⽅法都⽀持事务;
* 如果加在⽅法上,则只有该⽅法⽀持事务
* 可以根据需要在CUD操作上加注解
**/
@Transactional
public interface IUserService {
int insertUser(ResUser resUser);
int updateUser(ResUser resUser);
List<ResUser> getResUserList();
}
ample.demo.service;
2
ample.demo.bean.ResPartner;
4import ansaction.annotation.Transactional;
5
6import java.util.List;
7import java.util.Map;
8
9 @Transactional
10public interface IPartnerService {
11
12int add(ResPartner resPartner);
13
14int deleteByIds(String ids);
15
16int update(ResPartner resPartner);
17
18    ResPartner queryById(int id);
19
20    List<ResPartner> queryList(Map<String, Object> params);
21
22 }
  实现类
ample.demo.service.impl;
2
ample.demo.bean.ResPartner;
ample.demo.dao.PartnerMapperXml;
ample.demo.service.IPartnerService;
6import org.slf4j.Logger;
7import org.slf4j.LoggerFactory;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.stereotype.Component;
10
11import java.util.List;
12import java.util.Map;
13
14 @Component("partnerService")
15public class PartnerServiceImpl implements IPartnerService {
16
17private Logger logger = Class());
18    @Autowired
19private PartnerMapperXml partnerMapperXml;
20
21    @Override
22public int add(ResPartner resPartner) {
23        StringBuilder sbStr = new StringBuilder();
24        sbStr.append("id = ").Id())
25                .append(", name = ").Name())
26                .append(", city = ").City())
27                .append(", displayName = ").DisplayName());
28this.logger.String());
29return this.partnerMapperXml.add(resPartner);
30    }
31 }
ample.demo.service.impl;
2
ample.demo.bean.ResPartner;
ample.demo.bean.ResUser;
ample.demo.dao.PartnerMapperXml;
ample.demo.dao.ResUserMapper;
ample.demo.service.IUserService;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.stereotype.Component;
10
11import java.util.List;
12
13 @Component("userService")
14public class UserServiceImpl implements IUserService {
15
16    @Autowired
17private ResUserMapper resUserMapper;
18    @Autowired
19private PartnerMapperXml partnerMapperXml;
20
21    @Override
22public int insertUser(ResUser resUser) {
23
24int i = resUserMapper.insert(resUser);
25//        ResPartner partner = new ResPartner();
26//        partner.Id());
27//        partner.Name());
28//        partner.Login());
29//
30//        if (true) // ⽤来验证异常,使事务回滚
31//            throw new RuntimeException("xxxxxxxxxxxxxxx");
32//        int a = 1/0;
33//        partnerMapperXml.add(partner);
34
35return i;
36    }
37
38 }
  controller代码,JSONMsg是⼀个⾃定义类,就三个属性code,msg,data⽤来给前台返回数据。  llers;
2
3import com.alibaba.fastjson.JSONObject;
ample.demo.bean.JSONMsg;
ample.demo.bean.ResPartner;
ample.demo.bean.ResUser;
ample.demo.service.IPartnerService;
ample.demo.service.IUserService;
9import org.springframework.beans.factory.annotation.Autowired;
10import org.springframework.jdbc.datasource.DataSourceTransactionManager;
11import ansaction.PlatformTransactionManager;
12import ansaction.TransactionDefinition;
13import ansaction.TransactionStatus;
14import org.springframework.web.bind.annotation.*;
15
16import java.util.List;
17
18 @RestController
19 @RequestMapping("/users")
20public class UserController {
21
22    @Autowired
23private IUserService userService;
24    @Autowired
25private IPartnerService partnerService;
26    @Autowired
27private PlatformTransactionManager platformTransactionManager;
28    @Autowired
29private TransactionDefinition transactionDefinition;
30
31    @RequestMapping(value = "/insert", method = RequestMethod.POST)
32    @ResponseBody
33public JSONMsg insertUser(@RequestBody String data){
34
35        JSONMsg jsonMsg = new JSONMsg();
36        jsonMsg.setCode(400);
37        jsonMsg.setMsg("error");
38        System.out.println(data);
39        JSONObject jsonObject = JSONObject.parseObject(data);
40if (!ainsKey("data")){
41return jsonMsg;
42        }
43
44        ResUser user = JSONObject.("data").toString(), ResUser.class);
45int i = userService.insertUser(user);
46
47        System.out.println(i);
48if (i!=0){
49            jsonMsg.setCode(200);
50            jsonMsg.setMsg("成功");
51            jsonMsg.setData(user);
52        }
53
54return jsonMsg;
55    }
56
57//    该⽅法中的代码⽤来验证⼿动控制事务时使⽤
58//    @RequestMapping(value = "/insert", method = RequestMethod.POST)
59//    @ResponseBody
60//    public JSONMsg insertUser(@RequestBody String data){
61//        TransactionStatus transactionStatus = Transaction(transactionDefinition);
62//
63//        System.out.println(transactionStatus.isCompleted());
64//        System.out.println(transactionStatus.isRollbackOnly());
65//
66//
67//        JSONMsg jsonMsg = new JSONMsg();
68//        jsonMsg.setCode(400);
69//        jsonMsg.setMsg("error");
70//        System.out.println(data);
71//        try{
72//            JSONObject jsonObject = JSONObject.parseObject(data);
73//            if (!ainsKey("data")){
74//                return jsonMsg;
75//            }
76//
77//            ResUser user = JSONObject.("data").toString(), ResUser.class);
78//            int i = userService.insertUser(user);
79//
80//            i= 1/0;
81//
82//            ResPartner partner = new ResPartner();
83//            partner.Id());
84//            partner.Name());
85//            partner.Login());
86//            partnerService.add(partner);
87//
88//            if (i!=0){
89//                jsonMsg.setCode(200);
90//                jsonMsg.setMsg("成功");
91//                jsonMsg.setData(user);
92//            }
93//
94//            platformTransactionManagermit(transactionStatus);
95//            System.out.println("提交事务");
96//        }catch (Exception e){
97//            e.printStackTrace();
98//            llback(transactionStatus);
99//            System.out.println("回滚事务");
100//        }finally {
101//
102//        }
103//        return jsonMsg;
104//    }
105 }
  接下来说下spring boot中配置全局的声明式事务,定义⼀个configure类,具体代码如下
figs;
2
3import org.aspectj.lang.annotation.Aspect;
4import org.springframework.aop.Advisor;
5import org.springframework.aop.aspectj.AspectJExpressionPointcut;
6import org.springframework.aop.support.DefaultPointcutAdvisor;
7import org.springframework.beans.factory.annotation.Autowired;
8import t.annotation.Bean;
9import t.annotation.Configuration;
10import ansaction.PlatformTransactionManager;
11import ansaction.TransactionDefinition;
12import ansaction.interceptor.DefaultTransactionAttribute;
13import ansaction.interceptor.NameMatchTransactionAttributeSource;
14import ansaction.interceptor.TransactionInterceptor;
15
16/**
17 * @ClassName: GlobalTransactionAdviceConfig
18 * @Description: AOP全局事务管理配置
19 *
20 * 声明式事务说明:
21 *      1.如果将业务逻辑放到service层⾯来处理,则能够保证事务安全,即便使⽤了AOP来切⼊service⽅法也能保证事务安全;
22 *      2.如果多个service在controller层做业务逻辑(本⾝就是错误的),则不能保证事务安全。
23 * 对于2中的情况,应该尽量避免,因为本⾝就是错误的;
24 * 这种情况在⾯向切⾯编程中也有可能碰到,如,因为必要切⼊点为controller(应尽量避免,原则应切service),切⾯程序跟controller业务逻辑不同,
25 *  service不同,会导致事务混乱;
26 *
27 * 如果出现上述情况,则可以使⽤编程式事务管理(也就是⼿动控制事务)
28 *      在controller逻辑开始之前⼿动开启/获取事务,然后在controller逻辑结束后再根据需要提交或者回滚事务;
29 *      在AOP中也是如此,在before中⼿动开启/获取事务(这⼀步是必须的),在after中处理切⾯逻辑,然后根据需要提交或者回滚事务,如果由于异常需要回滚事务,记得修改返回信息
30 *
31 * @Author:
32 * @Date: 2019-08-01
33 * @Version: V2.0
34 **/
35
36 @Aspect
37 @Configuration
38public class GlobalTransactionAdviceConfig {
39private static final String AOP_POINTCUT_EXPRESSION = "execution (* ample.demo.service..*.*(..))";
40
41    @Autowired
42private PlatformTransactionManager transactionManager;
43
44    @Bean
45public TransactionInterceptor txAdvice() {
46        DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
47        txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
48
49        DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
50        txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATI
ON_REQUIRED);
51        txAttr_REQUIRED_READONLY.setReadOnly(true);
52
53        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
54        source.addTransactionalMethod("add*", txAttr_REQUIRED);
55        source.addTransactionalMethod("insert*", txAttr_REQUIRED);
56        source.addTransactionalMethod("save*", txAttr_REQUIRED);
57        source.addTransactionalMethod("create*", txAttr_REQUIRED);
58        source.addTransactionalMethod("delete*", txAttr_REQUIRED);
59        source.addTransactionalMethod("update*", txAttr_REQUIRED);
60        source.addTransactionalMethod("exec*", txAttr_REQUIRED);
61        source.addTransactionalMethod("set*", txAttr_REQUIRED);
62        source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
63        source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
64        source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
65        source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
66        source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
67        source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);
68        source.addTransactionalMethod("select*", txAttr_REQUIRED_READONLY);
69return new TransactionInterceptor(transactionManager, source);
70    }
71
72    @Bean
73public Advisor txAdviceAdvisor() {
74        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
75        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
76return new DefaultPointcutAdvisor(pointcut, txAdvice());
77    }
78 }
  添加这个类,根据知⼰需要修改切⼊点,然后放到能被spring boot扫描到的包下即可,如果出现事务失败的情况,请查看下addTransactionalMethod是否配置正确,我当初就是⽤的insert*,⽽没有添加导致失败。
  2.切⼊点为controller时,如何使⽤编程式事务管理控制事务
figs;
2
ample.demo.bean.JSONMsg;
ample.demo.bean.ResPartner;
ample.demo.bean.ResUser;
ample.demo.service.IPartnerService;
7import org.aspectj.lang.JoinPoint;
8import org.aspectj.lang.annotation.AfterReturning;
9import org.aspectj.lang.annotation.Aspect;
10import org.aspectj.lang.annotation.Before;
11import org.aspectj.lang.annotation.Pointcut;
12import org.springframework.beans.factory.annotation.Autowired;
13import org.springframework.stereotype.Component;
14import ansaction.PlatformTransactionManager;
15import ansaction.TransactionDefinition;
16import ansaction.TransactionStatus;
17import ansaction.annotation.Transactional;
18
19 @Component
20 @Aspect
21public class ResUserAspect {
22
23    @Autowired
24private IPartnerService partnerService;
25    @Autowired
26private PlatformTransactionManager platformTransactionManager;
27    @Autowired
28private TransactionDefinition transactionDefinition;
29
30private TransactionStatus transactionStatus;
31
32    @Pointcut("execution(public * llers.UserController.insertUser(..))")
33//    @Pointcut("execution(public * ample.demo.service.IUserService.insertUser(..))") // 验证切⼊点为service时,AOP编程中的事务问题
34private void insertUser(){}
35
36    @Before(value = "insertUser()")
37public void before(){
38//在切⼊点程序执⾏之前⼿动开启事务 - 必须的操作
39        transactionStatus = Transaction(transactionDefinition);
40    }
41    // 验证切⼊点为service时,AOP编程中的事务问题
42//    @AfterReturning(pointcut = "insertUser()", returning = "result")
43//    public void afterReturning(JoinPoint joinPoint, Object result){
44//
45//        Object[] args = Args();
46//        System.out.println(args[0]);
47//        if (((Integer)result) != 0){
48//            ResPartner partner = new ResPartner();
49//            ResUser user = (ResUser) args[0];
50//            partner.Id());
51//            partner.Name());
52//            partner.Login());
53//
54//            int a = 1/0;
55//            int i = partnerService.add(partner);
56//
57//            System.out.println(i);
58//        }
59//    }
60   //切⼊点为controller时的事务验证
61    @Transactional
62    @AfterReturning(pointcut = "insertUser()", returning = "result")
63public void afterReturning(JoinPoint joinPoint, Object result){
64
65if (!(result instanceof JSONMsg)){
66            System.out.Class());
67return;
68        }
69        JSONMsg jsonMsg = (JSONMsg) result;
70try{
71
72if (Code() == 200){
73                ResPartner partner = new ResPartner();
74                ResUser user = (ResUser) Data();
75                partner.Id());
76                partner.Name());
77                partner.Login());
78
79int a = 1/0;
80int i = partnerService.add(partner);
81
82                System.out.println(i);
83            }
84
85            platformTransactionManagermit(transactionStatus);  // ⼿动提交事务
86            System.out.println("提交事务");
87        }catch (Exception e){
88            llback(transactionStatus); // 出现异常,回滚事务
89            System.out.println("回滚事务");
90            System.out.Message());
91
92//修改返回数据
93            jsonMsg.setCode(400);
94            jsonMsg.Message());
95        }
96
97    }
98 }
  ⽤到的实体bean
1// ResUser.java中的属性
springboot aop2private Integer id;
3private String login;
4private String name;
5private Integer age;
6
7// ResPartner.java中的属性
8private int id;
9private String name;
10private String city;
11private String displayName;
  最后总结我写在了GlobalTransactionAdviceConfig 类中,也就是如下
* 声明式事务说明:
*      1.如果将业务逻辑放到service层⾯来处理,则能够保证事务安全,即便使⽤了AOP来切⼊service⽅法也能保证事务安全;
*      2.如果多个service在controller层做业务逻辑(本⾝就是错误的),则不能保证事务安全。
* 对于2中的情况,应该尽量避免;
* 这种情况在⾯向切⾯编程中也有可能碰到,如,因为必要切⼊点为controller(应尽量避免,原则应切service),切⾯程序跟controller业务逻辑不同,
*  service不同,会导致事务混乱;
*
* 如果出现上述情况,则可以使⽤编程式事务管理(也就是⼿动控制事务)
*      在controller逻辑开始之前⼿动开启/获取事务,然后在controller逻辑结束后再根据需要提交或者回滚事务;
*      在AOP中也是如此,在before中⼿动开启/获取事务(这⼀步是必须的),在after中处理切⾯逻辑,然后根据需要提交或者回滚事务,如果由于异常需要回滚事务,记得修改返回信息 
  注:
    有时候项⽬中使⽤了分布式框架,⽐如dubbo,则可能存在service层跟controller层分布式部署的问题,这会导致这种⽅式在controller中获取不到transactionManager,后续有时间在来看下分布式中的事务处理问题。
参考链接:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。