假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情请点击http://106.12.206.16:8080/qingruihappy/index.html
一,spring事务的注解
1.1,spring自带的@Transactional例子
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 org.springframework.transaction.annotation.Transactional; import com.qingruihappy1.dao.UserDao; import com.qingruihappy1.service.UserService; //user 服务层 @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; //声明:@Transactional 或者XML方式 //方法执行开始执行前,开启提交事务 @Transactional public void add() { userDao.add("test001", 20); int i = 1 / 0; System.out.println("################" + i); userDao.add("test002", 21); //注意下面的代码是不会回滚的,因为异常给吃掉了,所以是不会回滚的。 //因为回滚的机制在异常中的,现在吃掉异常之后就不会进入异常的方法中,所以自然而然的不会回滚的。 /* try { userDao.add("test001", 20); int i = 1 / 0; System.out.println("################" + i); userDao.add("test002", 21); } catch (Exception e) { e.printStackTrace(); }*/ } // 方法执行完毕之后,才会提交事务 }
<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" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.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> <!-- 开启注解事务 注意自定义注解的时候把它注释掉,用spring自带的话则加上 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager" /> </beans>
测试:
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("userServiceImpl"); userService.add(); } }
1.2,自定义注解案例
package com.qingruihappy2.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Target(value = ElementType.METHOD) 设置注解权限<br> * @author qingruihappy * */ @Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) // @interface 定义注解 public @interface AddAnnotation { // 手写Spring事务注解 int userId() default 0; String userName() default "默认名称"; String[]arrays(); }
package com.qingruihappy2.annotation; import java.lang.reflect.Method; public class User { @AddAnnotation(userName = "张三", userId = 18, arrays = { "1" }) public void add() { } public void del() { } public static void main(String[] args) throws ClassNotFoundException { // 怎么样获取到方法上注解信息 反射机制 Class<?> forName = Class.forName("com.qingruihappy2.annotation.User"); // 获取到当前类(不包含继承)所有的方法 Method[] declaredMethods = forName.getDeclaredMethods(); for (Method method : declaredMethods) { // 获取该方法上是否存在注解 System.out.println("####方法名称" + method.getName()); AddAnnotation addAnnotation = method.getAnnotation(AddAnnotation.class); if (addAnnotation == null) { // 该方法上没有注解 System.out.println("该方法上没有加注解.."); continue; } // 在该方法上查找到该注解 System.out.println("userId:" + addAnnotation.userId()); System.out.println("userName:" + addAnnotation.userName()); System.out.println("arrays:" + addAnnotation.arrays()); System.out.println(); } } }
####方法名称add userId:18 userName:张三 arrays:[Ljava.lang.String;@18760838 ####方法名称main 该方法上没有加注解.. ####方法名称del 该方法上没有加注解..
1.3,手写spring注解事务
步骤
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 UserDaoa { @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(); public void del(); }
package com.qingruihappy3.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.qingruihappy3.annotation.ExtTransaction; import com.qingruihappy3.dao.UserDaoa; import com.qingruihappy3.service.UserService; //user 服务层 @Service public class UserServiceImpla implements UserService { @Autowired private UserDaoa userDao; /* * 声明:@Transactional 或者XML方式 方法执行开始执行前,开启提交事务 */ @ExtTransaction public void add() { userDao.add("test001", 20); int i = 1 / 0; System.out.println("################" + i); userDao.add("test002", 21); // 获取当前事务,手动进行回滚 } // 方法执行完毕之后,才会提交事务 public void del() { System.out.println("del"); } }
注解:
package com.qingruihappy3.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 事务注解 设置传播行为 @Target({ ElementType.METHOD })//只在方法上起作用 @Retention(RetentionPolicy.RUNTIME)//运行时的注解 public @interface ExtTransaction { }
工具类
package com.qingruihappy3.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; // 编程事务(需要手动begin 手动回滚 手都提交) @Component @Scope("prototype") // 每个事务都是新的实例 目的解决线程安全问题 多例子 prototype就是多例的。 public class TransactionUtils { // 全局接受事务状态 TransactionStatus声明为全局的原因就是让每个事务都创建一个实例,防止出现线程安全的问题 private TransactionStatus transactionStatus; // 获取事务源 @Autowired private DataSourceTransactionManager dataSourceTransactionManager; // 开启事务 public TransactionStatus begin() { System.out.println("开启事务"); transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transactionStatus; } // 提交事务 public void commit(TransactionStatus transaction) { System.out.println("提交事务"); dataSourceTransactionManager.commit(transaction); } // 回滚事务 public void rollback() { System.out.println("回滚事务..."); dataSourceTransactionManager.rollback(transactionStatus); } }
切面类:
package com.qingruihappy3.aop; import java.lang.reflect.Method; 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.aspectj.lang.reflect.MethodSignature; 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.annotation.ExtTransaction; import com.qingruihappy3.transaction.TransactionUtils; // 自定义事务注解具体实现 @Aspect @Component public class AopExtTransaction { // 一个事务实例子 针对一个事务 @Autowired private TransactionUtils transactionUtils; // 使用异常通知进行 回滚事务 @AfterThrowing("execution(* com.qingruihappy3.service.*.*.*(..))") public void afterThrowing() { // 获取当前事务进行回滚 // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); transactionUtils.rollback(); } // 环绕通知 在方法之前和之后处理事情 @Around("execution(* com.qingruihappy3.service.*.*.*(..))") public void around(ProceedingJoinPoint pjp) throws Throwable { // 1.获取该方法上是否加上注解 ExtTransaction extTransaction = getMethodExtTransaction(pjp); TransactionStatus transactionStatus = begin(extTransaction); // 2.调用目标代理对象方法 pjp.proceed(); // 3.判断该方法上是否就上注解 commit(transactionStatus); } private TransactionStatus begin(ExtTransaction extTransaction) { if (extTransaction == null) { return null; } // 2.如果存在事务注解,开启事务 return transactionUtils.begin(); } private void commit(TransactionStatus transactionStatus) { if (transactionStatus != null) { // 5.如果存在注解,提交事务 transactionUtils.commit(transactionStatus); } } // 获取方法上是否存在事务注解 private ExtTransaction getMethodExtTransaction(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException { String methodName = pjp.getSignature().getName(); // 获取目标对象 Class<?> classTarget = pjp.getTarget().getClass(); // 获取目标对象类型 Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes(); // 获取目标对象方法 Method objMethod = classTarget.getMethod(methodName, par); ExtTransaction extTransaction = objMethod.getAnnotation(ExtTransaction.class); return extTransaction; } }
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("userServiceImpla"); userService.add(); } }
二:事务的传播行为
我们通过一张图引入
这个怎么实现呢?
我们来看这7中传播行为
Propagation(key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。)有以下选项可供使用:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
我们通过代码来看:
package com.qingruihappy4.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class LogDao { @Autowired private JdbcTemplate jdbcTemplate; public void add(String name) { String sql = "INSERT INTO t_log(log_name) VALUES(?);"; int updateResult = jdbcTemplate.update(sql, name); System.out.println("##LogDao##updateResult:" + updateResult); } }
package com.qingruihappy4.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.qingruihappy4.service; public interface LogService { public void addLog(); }
package com.qingruihappy4.service; //user 服务层 public interface UserService { public void add(); public void del(); }
package com.qingruihappy4.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.qingruihappy4.dao.LogDao; import com.qingruihappy4.service.LogService; @Service public class LogServiceImpl implements LogService { @Autowired private LogDao logDao; @Transactional(propagation = Propagation.REQUIRES_NEW)关键就是这一行代码 public void addLog() { logDao.add("addLog" + System.currentTimeMillis()); } }
package com.qingruihappy4.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.qingruihappy4.dao.UserDaoc; import com.qingruihappy4.service.LogService; import com.qingruihappy4.service.UserService; //user 服务层 @Service public class UserServiceImplc implements UserService { @Autowired private UserDaoc userDao; @Autowired private LogService logService; @Transactional public void add() { // 调用接口的时候 接口失败 需要回滚,但是日志记录不需要回滚。 logService.addLog(); // 后面程序发生错误,不能影响到我的回滚### 正常当addLog方法执行完毕,就应该提交事务 userDao.add("test001", 20); int i = 1 / 0; System.out.println("################"); userDao.add("test002", 21); } // 方法执行完毕之后,才会提交事务 public void del() { System.out.println("del"); } }
package com.qingruihappy4; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.qingruihappy4.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(); } }
关键就是上面标红的代码。定义一个两套事务互不影响的。
三:事务的特性
1.原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做要么全不做
2.一致性:数据不会因为事务的执行而遭到破坏
3.隔离性:一个事物的执行,不受其他事务的干扰,即并发执行的事物之间互不干扰
4.持久性:一个事物一旦提交,它对数据库的改变就是永久的
四,事务的隔离级别
1.default:默认的事务隔离级别,跟具体的数据有关,mysql默认的事务隔离级别是repeatable_read
2.read_uncommitted: 读未提交,一个事务可以感知或者操作另外一个未提交的事务,可能会出现脏读、不可重复读、幻读
3.read_committed:读已提交,一个事务只能感知或者操作另一个已经提交的事务,可能会出现不可重复读、幻读
4.repeatable_read:可重复读,能够避免脏读,不可重复读,不能避免幻读
4.serializable:串行化,隔离级别最高,消耗资源最低,代价最高,能够防止脏读, 不可重复读,幻读。
假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情请点击http://106.12.206.16:8080/qingruihappy/index.html