Spring的事务使用
大家好,今天小辉就讲一下Spring的事务。事务百度上的解释是
这个事务解释很明确哈就是要做的或所做的事情,在数据库中其实就是你要执行的语句,你要执行多少条语句,这些语句顺序怎样执行的你都可以通过事务来控制,事务还有一个特性就是可以后悔,对已经执行的语句进行回滚,(小辉开车中:想拥有这个特性呢)。转回来 ,Spring 事务其实就是对数据库事务的支持了。Spring 事务的隔离级别和数据库中的隔离级别是相对应的。但是Spring事务有声明式和编程式,小辉就讲一**解声明式,aop声明式事务和编程式局部事务,(由于很多都开始使用Java配置类替换xml,xml现在就不讲了哦,),这里我分的声明式和编程式主要式通过和业务代码的解耦情况来分的,如果有更好的理解欢迎提出哦。
1.注解声明式事务:
其实声明式事务注解在Spring boot项目中可以快速实现,在启动类上加上注解@EnableTransactionManagement,
这就是开启了Spring事务,事务开启肯定式在数据源的基础上嘛,配置文件里面加上数据源的配置就可以了
下面式简单的使用:
(写在方法的上面)
/** * 作用在方法上方法自定有回滚 */ @GetMapping(value = "addBlogFailToMethod") @Transactional public void addBlogFailToMethod() { Blog blog = new Blog("测试文章ToMethod", "书法家深刻搭街坊昆仑山大家快乐的数据库", "描述"); try { blogDao.save(blog); int i = 1 / 0; log.info("新增数据成功,id={}", blog.getId()); } catch (Exception e) { log.error("Blog新增异常事务回滚:id={}", blog.getId()); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }
(写在类的上面)
@RestController // 根据需求自定义配置回滚还是提交。。。 @Transactional(rollbackFor = Exception.class) public class BaseController { }
只要继承了这个类,抛出异常就会进行事务回滚(事务的传di性),括号里面的属性根据自己的需求进行配置就OK了
但是注解的方法必须是public修改。如果是类上写注解那么只会作用在public修饰的方法,但是public调用其他修饰的的方法时是会有作用的哦。如果类和方法都有注解那就方法的注解优先。
下面例子这样是会正常提交事务的,因为protected修饰的不起作用。
@GetMapping(value = "addBlogSuccess") @Transactional protected void addBlogSuccess() { Blog blog = new Blog("测试文章ToSuccess", "书法家深刻搭街坊昆仑山大家快乐的数据库", "描述"); blogDao.save(blog); log.info("新增数据/成功,id={}", blog.getId()); int i = 1/0; }
2.aop声明式事务:
主要是使用了aop编程的特点配置了一个全局属性,根据方法字符串来判断是否自动开启事务,但是由于用到Aspect,启动类上加上
@EnableTransactionManagement(proxyTargetClass = true)注解,否则可能会失效哦。
import org.aspectj.lang.annotation.Aspect; import org.springframework.aop.Advisor; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.interceptor.*; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * 全局事物配置类 */ @Aspect @Configuration public class GlobalTransactionConfig { @Autowired private PlatformTransactionManager platformTransactionManager; @Bean public TransactionInterceptor getTransactionInterceptor() { NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); Map<String, TransactionAttribute> nameMap = new HashMap<>(16); // 只读规则 RuleBasedTransactionAttribute readOnlyRule = new RuleBasedTransactionAttribute(); readOnlyRule.setReadOnly(true); // transactiondefinition 定义事务的隔离级别;PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中 readOnlyRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); RuleBasedTransactionAttribute requireRule = new RuleBasedTransactionAttribute(); // 抛出异常后执行切点回滚 requireRule.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class))); // PROPAGATION_REQUIRED:事务隔离性为1,若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值 requireRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 设置事务失效时间,超时则事务回滚 requireRule.setTimeout(5); // 只要controller方法这些字符开头都会自动开启一个事务然后提交或者回滚 nameMap.put("query*", readOnlyRule); nameMap.put("save*", requireRule); nameMap.put("update*", requireRule); nameMap.put("delete*", requireRule); source.setNameMap(nameMap); return new TransactionInterceptor(platformTransactionManager, source); } /** * 切入设置 * @return */ @Bean public Advisor getAdvisor() { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); // 一般都会写的式service层,但是我这里抛弃了service层就写了controller目录 pointcut.setExpression("execution(* com.yh.blog.transaction.controller.*.*(..))"); return new DefaultPointcutAdvisor(pointcut, getTransactionInterceptor()); } }
示例:
/** * savePerson方法前缀式save开头所以会进入全局事务 */ @GetMapping(value = "savePerson") public void savePerson() { Person person = new Person("savePerson", 23, "广东广州"); // 弄一个异常看看是否会回滚 personDao.save(person); int i = 1 / 0; log.info("Person数据新增成功id={}", person.getId()); } /** * addPerson方法没有配置全局事务也没有开启类,方法的事务 */ @GetMapping(value = "addPerson") public void addPerson() { Person person = new Person("addPerson", 23, "广东广州"); // 弄一个异常看看是否会回滚0 personDao.save(person); log.info("Person数据新增成功id={}", person.getId()); int i = 1 / 0; }
第一个方法事务会回滚,第二个方法事务不会回滚而是提交,因为addPerson方法名在全局配置里没符合的。(目前使用最多的应该是这个(没有使用分布式事务情况下哈))这样要求命名的规范性哦。
3.编程式局部事务:
配置:
@Autowired private DataSource dataSource; @Bean(name = "transactionManager") // @Bean public DataSourceTransactionManager getDataSourceTransactionManager() { return new DataSourceTransactionManager(dataSource); }
看了配置代码就会问直接用@Bean吗?如果报transactionManager找不到就用@Bean(name = "transactionManager")
@Autowired private DataSourceTransactionManager dataSourceTransactionManager; @Autowired private TransactionTemplate transactionTemplate; @Autowired private TransactionDefinition transactionDefinition; @GetMapping(value = "addByDataTran") public void addByDataTran() { TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); // 可以使用默认的,也可以自己new了配置 // DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); // transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); Person person = new Person("编程式", 21, "上海"); try { personDao.save(person); // 弄一个异常看看是否会回滚 int i = 1 / 0; // 需要手动提交事务(不会自动提交的哦) log.info("Person数据新增成功id={}", person.getId()); dataSourceTransactionManager.commit(transactionStatus); } catch (Exception e) { log.error("使用编程式事务回滚:id={}", person.getId()); // 需要手动回滚 dataSourceTransactionManager.rollback(transactionStatus); } } /** * 在transactionTemplate只要发生异常都会自动回滚 */ @GetMapping(value = "addByTemplate") public void addByTemplate() { transactionTemplate.execute((status -> { Person person = new Person("使用Template", 29, "广东广州"); // 弄一个异常看看是否会回滚 personDao.save(person); int i = 1 / 0; log.info("Person数据新增成功id={}", person.getId()); return status; })); }
第一个方法是使用了DataSourceTransactionManager事务管理(手动的),第二个是使用了TransactionTemplate管理事务自动的
像这样编程式的和业务代码耦合在一起的不建议使用,如果有特定要处理的那就可以使用注解来操作。
由于我使用了自增的id,事务回滚了但是id是一直递增的。
以上代码的GitHub地址为:https://github.com/YH0128/blog-code
文章同时会更新到公众号,觉得对你有帮助或者有用的可以关注一下哦