一,区别声明式事物和编程式事物
所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事物其实就是编程式事物+spring的AOP代理,在里面我们是见不到手动的begin commit 和rollback的。
管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。
声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
下面我们通过案例来看看。
二,案例
2.1,spring事物自带的几种通知
package com.qingruihappy.service; //user 服务层 public interface UserService { public void add(); }
package com.qingruihappy.service.impl; import org.springframework.stereotype.Service; import com.qingruihappy.service.UserService; /** * 注意事物出异常的话一定不要吃掉(try cath ,而是要throw往外抛,否则事物是不起作用的。原因我们后面讲) * @author qingruihappy * @data 2018年11月19日 上午12:21:21 * @说明: */ //user 服务层 @Service public class UserServiceImpl implements UserService { // spring 事务封装呢? aop技术 public void add() { System.out.println("往数据库添加数据........."); } }
package com.qingruihappy.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; // 切面类 @Component//注入到spring的容器中来 @Aspect//表示是一个切面 public class AopLog { // aop 编程里面有几个通知: 前置通知 后置通知 运行通知 异常通知 环绕通知 @Before("execution(* com.qingruihappy.service.UserService.add(..))") public void before() { System.out.println("前置通知 在方法之前执行..."); } // 后置通知 在方法运行后执行 @After("execution(* com.qingruihappy.service.UserService.add(..))") public void after() { System.out.println("前置通知 在方法之后执行..."); } // 运行通知 @AfterReturning("execution(* com.qingruihappy.service.UserService.add(..))") public void returning() { System.out.println("运行通知"); } // 异常通知 @AfterThrowing("execution(* com.qingruihappy.service.UserService.add(..))") public void afterThrowing() { System.out.println("异常通知"); } // 环绕通知 在方法之前和之后处理事情 @Around("execution(* com.qingruihappy.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 调用方法之前执行 System.out.println("环绕通知 调用方法之前执行"); proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码 // 调用方法之后执行 System.out.println("环绕通知 调用方法之后执行"); } }
package com.qingruihappy.test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.qingruihappy.service.UserService; public class Test001 { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) applicationContext.getBean("userServiceImpl"); userService.add(); } }
结果:
前置通知 在方法之前执行... 环绕通知 调用方法之前执行 往数据库添加数据......... 前置通知 在方法之后执行... 运行通知 环绕通知 调用方法之后执行
2.2,一个不加事务的案例
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 --> <!-- 1. 数据源对象: C3P0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 2. JdbcTemplate工具类实例 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 3.配置事务 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
这个spring的配置对下面的两个案例都其作用。
package com.qingruihappy1.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void add(String name, Integer age) { String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);"; int updateResult = jdbcTemplate.update(sql, name, age); System.out.println("updateResult:" + updateResult); } }
package com.qingruihappy1.service; //user 服务层 public interface UserService { public void add(); }
package com.qingruihappy1.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.qingruihappy1.service.UserService; import com.qingruihappy2.dao.UserDaob; //user 服务层 @Service public class UserServiceImpla implements UserService { @Autowired private UserDaob userDao; public void add() { // 注意在这里没有事物的话,执行完userDao.add("test001", 20);就会把数据插入到数据库中去的。 userDao.add("test001", 20); int a=1/0; //因为这里没有事物所以当报错的时候tes001会插入到数据库中,而test002不会插入到数据库中。 System.out.println("################"); userDao.add("test002", 21); } }
package com.qingruihappy1.test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.qingruihappy1.service.UserService; public class Test001 { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) applicationContext.getBean("userServiceImpla"); userService.add(); } }
结果:
Exception in thread "main" updateResult:1 java.lang.ArithmeticException: / by zero at com.qingruihappy1.service.impl.UserServiceImpla.add(UserServiceImpla.java:17) at com.qingruihappy1.test.Test001.main(Test001.java:13)
数据库:
这是因为在没有事物的情况下,执行完service的userDao.add("test001", 20);这一行代码的时候,数据库里面立马就有数值了,也不存在所谓的回滚了。
2.3,手写编程式事物
package com.qingruihappy2.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class UserDaob { @Autowired private JdbcTemplate jdbcTemplate; public void add(String name, Integer age) { String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);"; int updateResult = jdbcTemplate.update(sql, name, age); System.out.println("updateResult:" + updateResult); } }
package com.qingruihappy2.service; //user 服务层 public interface UserService { public void add(); }
package com.qingruihappy2.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import com.qingruihappy2.dao.UserDaob; import com.qingruihappy2.service.UserService; import com.qingruihappy2.transaction.TransactionUtils; //user 服务层 @Service public class UserServiceImplb implements UserService { @Autowired private UserDaob userDao; @Autowired private TransactionUtils transactionUtils; // spring 事务封装呢? aop技术 public void add() { TransactionStatus transactionStatus = null; try { // 开启事务 transactionStatus = transactionUtils.begin(); userDao.add("test001", 20); System.out.println("开始报错啦[email protected]!!"); int i = 1 / 0; System.out.println("################"); userDao.add("test002", 21); // 提交事务 //有错的话就会回滚的,这个时候当执行完userDao.add("test001", 20);,并不会插入到数据库中,只有确保commit的时候才会提交到数据库中去的 //出错的话就会rollback的。 if (transactionStatus != null) transactionUtils.commit(transactionStatus); } catch (Exception e) { e.getMessage(); // 回滚事务 if (transactionStatus != null) transactionUtils.rollback(transactionStatus); } } }
package com.qingruihappy2.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; //编程事务(需要手动begin 手动回滚 手都提交) @Component public class TransactionUtils { // 获取事务源 @Autowired private DataSourceTransactionManager dataSourceTransactionManager; // 开启事务 public TransactionStatus begin() { TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } // 提交事务 public void commit(TransactionStatus transaction) { dataSourceTransactionManager.commit(transaction); } // 回滚事务 public void rollback(TransactionStatus transaction) { dataSourceTransactionManager.rollback(transaction); } }
package com.qingruihappy2.test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.qingruihappy2.service.UserService; public class Test001 { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) applicationContext.getBean("userServiceImplb"); userService.add(); } }
2.4,手写声明式事务
package com.qingruihappy3.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class UserDaoc { @Autowired private JdbcTemplate jdbcTemplate; public void add(String name, Integer age) { String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);"; int updateResult = jdbcTemplate.update(sql, name, age); System.out.println("updateResult:" + updateResult); } }
package com.qingruihappy3.service; //user 服务层 public interface UserService { public void add(); }
package com.qingruihappy3.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.interceptor.TransactionAspectSupport; import com.qingruihappy1.dao.UserDao; import com.qingruihappy3.service.UserService; import com.qingruihappy3.transaction.TransactionUtilsc; //user 服务层 @Service public class UserServiceImplc implements UserService { @Autowired private UserDao userDao; public void add() { // 注意事项: 在使用spring事务的时候,service 不要try 最将异常抛出给外层aop 异常通知接受回滚 try { userDao.add("test001", 20); int i = 1 / 0; System.out.println("################"); userDao.add("test002", 21); } catch (Exception e) { e.printStackTrace(); //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } //在spring的事物中我们一般不要trycatch 因为一旦吃掉异常的话就不会走到反射类的AopTransaction的afterThrowing()异常方法中去了 //这个时候假如我们的业务场景必须的try的话,我们在catch的里面加上手动回滚的代码TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //记住必须每个catch都加的。 /* userDao.add("test001", 20); int i = 1 / 0; System.out.println("################"); userDao.add("test002", 21);*/ } }
package com.qingruihappy3.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; //编程事务(需要手动begin 手动回滚 手都提交) @Component public class TransactionUtilsc { // 获取事务源 @Autowired private DataSourceTransactionManager dataSourceTransactionManager; // 开启事务 public TransactionStatus begin() { TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } // 提交事务 public void commit(TransactionStatus transaction) { dataSourceTransactionManager.commit(transaction); } // 回滚事务 public void rollback(TransactionStatus transaction) { dataSourceTransactionManager.rollback(transaction); } }
package com.qingruihappy3.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.TransactionAspectSupport; import com.qingruihappy3.transaction.TransactionUtilsc; //切面类 基于手手动事务封装 @Component @Aspect public class AopTransaction { @Autowired private TransactionUtilsc transactionUtils; // TransactionUtils 不要实现为单例子: 如果为单例子的话可能会发生线程安全问题 // // 异常通知 @AfterThrowing("execution(* com.qingruihappy3.service.UserService.*(..))") public void afterThrowing() { System.out.println("回滚事务"); // 获取当前事务 直接回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } // 环绕通知 在方法之前和之后处理事情 @Around("execution(* com.qingruihappy3.service.UserService.*(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 调用方法之前执行 System.out.println("开启事务"); TransactionStatus transactionStatus = transactionUtils.begin(); proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码 // 调用方法之后执行 System.out.println("提交事务"); transactionUtils.commit(transactionStatus); } }
package com.qingruihappy3; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.qingruihappy3.service.UserService; public class Test001 { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) applicationContext.getBean("userServiceImplc"); userService.add(); } }
事务是程序运行如果没有错误,会自动提交事物,如果程序运行发生异常,则会自动回滚。
如果使用了try捕获异常时.一定要在catch里面手动回滚。
事务手动回滚代码
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
原文地址:https://www.cnblogs.com/qingruihappy/p/9986524.html