spring事物

本文深入探讨了Spring中的事务管理机制,包括声明式事务的使用、事务的四大特性(ACID)、传播行为和回滚规则。通过分析@Transaction注解的原理,展示了Spring如何利用AOP动态代理在方法调用前后进行事务控制。此外,还介绍了数据库事务的实现以及自定义事务管理器的可能性。总结了Spring事务管理的关键流程,帮助读者理解事务在实际应用中的工作方式。
摘要由CSDN通过智能技术生成

spring事物

引言

事物是一种概念,可以将一些分散的操作划分成一组原子性操作,要么全部成功,要么全部失败。常见的例子有银行转账操作。

事物具有4种性质(ACID)

  • 原子性(atomicity):强调事务的不可分割.
  • 一致性(consistency):事务的执行的前后数据的完整性保持一致.
  • 隔离性(isolation):一个事务执行的过程中,不应该受到其他事务的干扰.
  • 持久性(durability) :事务一旦结束,数据就被修改.

日常只知事物完成的工作却不知其原理,本文详细介绍下spring事物的原理。

spring中的声明式事物

spring中也有事物,通常我们使用@Transaction注解来标注我们需要事物的方法

/**
 * @author Mcj
 * @date 2020-01-15 20:11
 */

@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    StudentService studentService;

    /**
     * 添加一个学生
     * @return 200
     */
    @GetMapping
    public ResponseEntity addStudent() {
        Student student = new Student();
        student.setName("孙亚龙");
        student.setAge(666);
        student.setNum(0);
        studentService.addStudent(student);
        return ResponseEntity.ok(200);
    }
}
@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentDao studentDao;

    /**
     * 添加一个学生
     *
     * @param student 学生
     */
    @Override
    @Transactional(rollbackFor = IOException.class)
    public void addStudent(Student student) {
        studentDao.save(student);
    }

    @Override
    public Student findStudentByName(String name) {
        return studentDao.findAllByNameEquals(name).get(0);
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Student {

    @Id
    @GeneratedValue
    private Integer id;


    /**
     * 学生姓名
     */
    private String name;

    /**
     * 学生年龄
     */
    private Integer age;

    /**
     * 学生完成的工作数量
     */
    private Integer num;

    public Student(String name) {
        this.name = name;
    }

    public Student(Integer age) {
        this.age = age;
    }
}

当我们访问http接口之后会在数据库中保存一个学生,但是我们在这里打印一个1/0,这里应该会报出by zero的异常并且数据库不会保存

@Service
@Transactional
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentDao studentDao;

    /**
     * 添加一个学生
     * @param student 学生
     */
    @Override
    public void addStudent(Student student) {
        studentDao.save(student);
        System.out.println(1/0);
    }
}

总结

本章讲解了日常使用spring事物时遇到的现象,当出现错误时,@Transaction事物配置发生了作用,如果没有配置声明式事物则数据库内会新增一条数据然后再报出异常。

spring事物原理

spring声明式事物原理是利用aop、动态代理技术在目标方法被执行之前拦截住,执行一些增强逻辑。

利用与数据库的Connection对象进行提交或者会滚操作

同时利用了ThreadLocal对象,将当前数据库连接绑定到当前线程中来保证每次对数据库的操作都是同一条连接。

  • 数据库实现了事物功能
#当student表存储引擎为myisam时,执行以下语句则会直接保存,但是当存储引擎为innodb时,并不会保存。所以是否有事物功能与数据库表有关。
BEGIN;
INSERT INTO student (id,age,name) VALUES(10,666,'吴奇隆');
SELECT * from student;

innodb与myisam引擎区别,对事物的支持

  • spring事物管理并不直接管理事物,而是定义接口供相关平台实现,spring事物核心接口是PlatformTransactionManager,接口定义了commit()rollback()方法,PlatformTransactionManager接口实现类中有JpaTransactionManager类。同时跟踪JpaTransactionManagerdoCommit()方法,最终是jdbc操作connection来完成事物提交。

@Transaction原理

自定义的dao接口继承的JpaRepository接口,此接口的默认实现SimpleJpaRepository类也有声明式事物注解@Transaction

public interface StudentDao extends JpaRepository<Student,Integer>
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

在studentservice中也写了这个注解

@Service
@Transactional
public class StudentServiceImpl implements StudentService {

在第一章的例子中也是写了这个注解而让sql语句没有提交。

注解只是一个标志,在项目启动时SpringTransactionAnnotationParser#parseTransactionAnnotation()类已经扫描并且获取注解上配置的属性并且注册事物拦截器。

@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
  //获取配置的属性
   AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
         element, Transactional.class, false, false);
   if (attributes != null) {
      return parseTransactionAnnotation(attributes);
   }
   else {
      return null;
   }
}

spring为标记了@Transactional注解的类或者方法利用aop创建动态代理对象在目标方法调用前后创建提交事物.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cs3W8Bt-1602061137147)(拦截器.jpg)]

当某个目标类中有@Transaction注解声明过的方法时,cglib就会为目标类生成一个代理对象,StudentController中的studentservice在项目启动时是一个cglib生成的代理对象,这个代理对象有许多回调函数,DynamicAdvisedInterceptor会获取方法的拦截器链并且执行。只有代理对象调用目标方法才会执行拦截器逻辑。但是在调用目标方法时不使用代理对象而是调用使用原始bean对象调用。

步骤:StudentController中我调用了studentservice#addStudent()会进入以下逻辑

cglibAopProxy.DynamicAdvisedInterceptor
//cglib代理
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Object target = null;
  //获取aop的目标
   TargetSource targetSource = this.advised.getTargetSource();
   try {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
     //获取了StudentServiceImpl对象(目标bean)
      target = targetSource.getTarget();
     //获取class对象
      Class<?> targetClass = (target != null ? target.getClass() : null);
     //获取拦截器链,配置的before、after等就是一个个拦截器,addStudent方法只配置了一个拦截器就是Transactionintercrpt
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
         // We need to create a method invocation...
        //将参数传入cglib类方法调用类,要开始进行目标方法的调用
        //proxy:StudentServiceImpl$ 代理对象
        //target:StudentServiceImpl@9574 目标类Object对象
        //method:addstudent
        //args:方法参数
        //targetclass:目标对象类型Class类
        //chain:拦截器链
        //methodProxy:方法代理类
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).
           //调用拦截器逻辑
           proceed();
      }
     
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}
ReflectiveMethodInvocation
@Override
@Nullable
public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.

  //查看当前拦截器是否是最后一个拦截器了。如果后面没有拦截器了则执行目标方法
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
     //连接点,拦截的方法,addstudent();
      return invokeJoinpoint();
   }

  //获取拦截器对象
Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
         

   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
         return proceed();
      }
   }
   else {
      // It's an interceptor, so we just invoke it: The pointcut will have
      // been evaluated statically before this object was constructed.
     //调用拦截器逻辑 ,现在只有一个拦截器transactionintercept
     return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}
transactioninterceptor
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
   // Work out the target class: may be {@code null}.
   // The TransactionAttributeSource should be passed the target class
   // as well as the method, which may be from an interface.
   Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

   // Adapt to TransactionAspectSupport's invokeWithinTransaction...
  //调用拦截器逻辑
   return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
TransactionAspectSupport
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {

  // If the transaction attribute is null, the method is non-transactional.
   TransactionAttributeSource tas = getTransactionAttributeSource();
  //获取事物属性txAttr
  //txAttr , PROPAGATION_REQUIRED事物传播行为,事物的隔离级别
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
  //获取事物管理器 此处为JpaTractionManager
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
  //获取方法连接点 此处为addStudent
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      // Standard transaction demarcation with getTransaction and commit/rollback calls.
      //创建一个事物基本信息对象,里面有事物管理器、事物传播行为、事物隔离级别、动态代理方法连接点、事物状态(是否新事物、是否只读、保存点等信息),并开启事物,返回对象(下面会解释这个方法内做了什么)
     //***重点
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

      Object retVal;
      try {
         // This is an around advice: Invoke the next interceptor in the chain.
         // This will normally result in a target object being invoked.
         //执行下一个拦截器方法
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // target invocation exception
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         //清除当前线程的事物信息
          cleanupTransactionInfo(txInfo);
      }
      //调用JpaTransationManager的doCommit()方法提交事物
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }

###createTransactionIfNecessary

@SuppressWarnings("serial")
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
      @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

   // If no name specified, apply method identification as transaction name.
   if (txAttr != null && txAttr.getName() == null) {
      txAttr = new DelegatingTransactionAttribute(txAttr) {
         @Override
         public String getName() {
            return joinpointIdentification;
         }
      };
   }

   TransactionStatus status = null;
   if (txAttr != null) {
      if (tm != null) {
        //开启事物并且获取事物状态
         status = tm.getTransaction(txAttr);
      }
      else {
         if (logger.isDebugEnabled()) {
            logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                  "] because no transaction manager has been configured");
         }
      }
   }
  //封装事物对象属性,并且将事物绑定到本地线程中
   return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
AbstractPlatformTransactionManager
//---------------------------------------------------------------------
// Implementation of PlatformTransactionManager
//---------------------------------------------------------------------

/**
 * This implementation handles propagation behavior. Delegates to
 * {@code doGetTransaction}, {@code isExistingTransaction}
 * and {@code doBegin}.
 * @see #doGetTransaction
 * @see #isExistingTransaction
 * @see #doBegin
 */
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
  //获取事物对象,返回jpatransactionManager类中的JpaTransactionObject对象
   Object transaction = doGetTransaction();

   // Cache debug flag to avoid repeated checks.
   boolean debugEnabled = logger.isDebugEnabled();

   if (definition == null) {
      // Use defaults if no transaction definition given.
      definition = new DefaultTransactionDefinition();
   }

   if (isExistingTransaction(transaction)) {
      // Existing transaction found -> check propagation behavior to find out how to behave.
      return handleExistingTransaction(definition, transaction, debugEnabled);
   }

   // Check definition settings for new transaction.
   if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
      throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
   }

   // No existing transaction found -> check propagation behavior to find out how to proceed.
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
      throw new IllegalTransactionStateException(
            "No existing transaction found for transaction marked with propagation 'mandatory'");
   }
   else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
      SuspendedResourcesHolder suspendedResources = suspend(null);
      if (debugEnabled) {
         logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
      }
      try {
         boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
         DefaultTransactionStatus status = newTransactionStatus(
               definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        //调用事物管理器的begin方法,事物开启,将自动提交设置为false,并且将获取的链接绑定到当前线程上
         doBegin(transaction, definition);
         prepareSynchronization(status, definition);
         return status;
      }
      catch (RuntimeException | Error ex) {
         resume(null, suspendedResources);
         throw ex;
      }
   }
   else {
      // Create "empty" transaction: no actual transaction, but potentially synchronization.
      if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
         logger.warn("Custom isolation level specified but no actual transaction initiated; " +
               "isolation level will effectively be ignored: " + definition);
      }
      boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
      return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
   }
}

执行拦截的方法了,回到TransactionAspectSupport方法中

try {
         // This is an around advice: Invoke the next interceptor in the chain.
         // This will normally result in a target object being invoked.
         //执行动态代理的目标方法
         retVal = invocation.proceedWithInvocation();
      }

由于只有一个transactionintercept拦截器

public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
  //查看当前是否还有拦截器需要执行,如果已经是最后一个拦截器则执行目标方法
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
     //执行addstudent方法
      return invokeJoinpoint();
   }

所以执行joinpoint,就是写上注解的那个方法addStudent();

此时不调用jpa的话,执行commitTransactionAfterReturning方法,事物就结束了。


但是addStudent()方法内调用了jpa的save()方法。

jpa内又配置了10个拦截器,并且jpa是jdk动态代理的

  • JdkDynamicAopProxy
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         // The target does not implement the equals(Object) method itself.
         return equals(args[0]);
      }
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         // The target does not implement the hashCode() method itself.
         return hashCode();
      }
      else if (method.getDeclaringClass() == DecoratingProxy.class) {
         // There is only getDecoratedClass() declared -> dispatch to proxy config.
         return AopProxyUtils.ultimateTargetClass(this.advised);
      }
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         // Service invocations on ProxyConfig with the proxy config...
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;

      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      // Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // Get the interception chain for this method.
     //获取拦截器链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
         // We can skip creating a MethodInvocation: just invoke the target directly
         // Note that the final invoker must be an InvokerInterceptor so we know it does
         // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // We need to create a method invocation...
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
        //调用拦截器逻辑,并且跳到下一个拦截器
         retVal = invocation.proceed();
      }

10个拦截器按照上面的方式顺序调用。

jpa配置的拦截器中同样有transactioninterceptor,同样会createTransactionIfNecessary创建事物,这时候方法返回值是TransactionInfo对象,这个对象保存着外层的事物信息,如addstudent的事物信息,这个对象是用链表结构保存嵌套事物的。

jpa拦截器执行完毕需要提交数据时会调用commitTransactionAfterReturning方法,

/**
 * Execute after successful completion of call, but not after an exception was handled.
 * Do nothing if we didn't create a transaction.
 * @param txInfo information about the current transaction
 */
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
   if (txInfo != null && txInfo.getTransactionStatus() != null) {
      if (logger.isTraceEnabled()) {
         logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
      }
     //配置的是jpatransactionmanager方法的commit。最终跟踪下去是connection的commit方法
      txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
   }
}

AbstractPlatformTransactionManager

//提交时会判断是否是新事物,jpa的save方法不是最外层事物,addStudent方法是最外层事物
else if (status.isNewTransaction()) {
   if (status.isDebug()) {
      logger.debug("Initiating transaction commit");
   }
   unexpectedRollback = status.isGlobalRollbackOnly();
  //执行提交方法
   doCommit(status);
}

内部jpa的事物结束后,再提交addStudent的事物,提交完成后事物才算结束。

至此事物提交成功。

利用springaop自定义事物

根据动态代理的方法自定义一个事物处理机制
定义注解

/**
 * @author mcj
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface McjTransaction {
}

定义注解解释类

@Aspect
@Configuration
@Slf4j
public class CglibConfig {

    @Autowired
    PlatformTransactionManager jpaTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    /* 定义一个切入点 */
    @Pointcut("@annotation(com.springboot.transactiondemo.annotation.McjTransaction)")
    public void doPointCut() {

    }

    @Before("doPointCut()")
    public void before(JoinPoint joinPoint) {
        log.info("PersonAspect ==> before method : {}", joinPoint.getSignature().getName());
        log.info("注解的类型名称为{}",joinPoint.getSignature().getDeclaringTypeName());
        log.info("方法修饰符个数为{}",joinPoint.getSignature().getModifiers());
        log.info("方法名称为{}",joinPoint.getSignature().getName());

    }
    @After("doPointCut()")
    public void after(JoinPoint joinPoint) throws FileNotFoundException {
        log.info("PersonAspect ==> after method : {}", joinPoint.getSignature().getName());
    }

    @Around("doPointCut()")
    public void aroundMethod(ProceedingJoinPoint point)  {
        TransactionStatus transaction = jpaTransactionManager.getTransaction(new DefaultTransactionAttribute());
        point.proceed();
        jpaTransactionManager.commit(transaction);
    }
}

最后将注解替换

    /**
     * 添加一个学生
     *
     * @param student 学生
     */
    @Override
    @McjTransaction
    public void addStudent(Student student) {
        studentDao.save(student);
    }

还可以自定义一个transactionManager

@Component("mcjtrans")
public class McjTransactionManager implements PlatformTransactionManager {

    @Autowired
    JdbcUtil jdbcUtil;

    @Override
    public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        return new SimpleTransactionStatus();
    }

    @Override
    public void commit(TransactionStatus status) throws TransactionException {
        try {
            jdbcUtil.getConnectionThreadLocal().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void rollback(TransactionStatus status) throws TransactionException {
        try {
            jdbcUtil.getConnectionThreadLocal().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

不用jpa后我选择使用jdbc

@Component
public class JdbcUtil {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private ThreadLocal<Connection> t = new ThreadLocal<Connection>();

    public Connection getConnectionThreadLocal() {
        if (t.get() == null) {
            try {
                Connection connection = jdbcTemplate.getDataSource().getConnection();
                connection.setAutoCommit(false);
                t.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }else{
            return t.get();
        }
        return t.get();
    }

    public void removeConnection(){
        try {
            t.get().close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        t.remove();
    }
}

定义一个自己的dao

@Service
public class McjStudentDao {

    @Autowired
    private JdbcUtil jdbcUtil;

    @McjTransaction
    public void save(Student student){
        String format = String.format("insert into student (id,age,name,num) VALUES (null,'%s','%s','55')",student.getAge(),student.getName());
        Connection connectionThreadLocal = jdbcUtil.getConnectionThreadLocal();
        try {
            Statement statement = connectionThreadLocal.createStatement();
            statement.execute(format);
        } catch (SQLException e) {
            e.printStackTrace();
        }

    };
}

运行程序发送http请求,数据库中保存了一条记录。

事物的传播行为

原本每个事物都是独立的互相不干扰的,于是通过配置事物的传播行为将不相关的操作变成了原子性的操作。事物的传播行为是指定了当有两个事物时是否需要回滚的问题

  • PROPAGATION_REQUIRED
    如果当前存在事物则加入该事物,不存在事物则新建事物(默认的事物机制),finishWork方法产生异常,数据库中任务没有被接下,两个方法都回滚

  • PAOPAGATION_REQUIRE_NEW

    若当前没有事务,则新建一个事务。若当前存在事务,则新建一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交,执行完成后数据库内有4个学生

  • PROPAGATION_SUPPORTS

    如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行,运行完成后添加了5个学生

  • PROPAGATION_MANDATORY

    如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。

  • PROPAGATION_NOT_SUPPORTED

    总是非事务地执行,并挂起任何存在的事务。数据库中有5名学生

  • PROPAGATION_NEVER

    总是非事务地执行,如果存在一个活动事务,则抛出异常。

  • PROPAGATION_NESTED

    内部事物抛出异常没被捕获则会影响外部事物会滚,与required区别是对外部事物的依赖

事物回滚

由sql语句看起来最为直观,前提是需要mysql数据库表引擎为innodb

#设置保存点,创建一个存档
SAVEPOINT point;
#执行insert语句
insert into student (id,name) VALUES (15,'吴彦祖');
select * from student;

#返回到保存点,回档
ROLLBACK to point;
select * from student;

#提交事物
COMMIT;

事物中出现异常如果被捕获了则不会回滚,默认运行时异常全部会回滚,非运行时异常不回滚,但是可以配置

spring事物中的事物回滚是利用了savepoint,在事物中配置了savepoint

@Transactional(rollbackFor = Exception.class)

总结

spring事物利用动态代理的方式,生成代理对象,在代理对象调用拦截器逻辑(transactioninterceptor)在方法前后增强,然后使用原始对象调用目标方法完成事物调用。

数据库实现了事物的功能,spring不直接操作数据库,而是让各大持久化平台实现这些与数据库事物打交道的方法,spring使用实现好的接口可以用在自己的框架中,通过事物的传播行为将一组方法组合成原子性操作。嵌套事物信息利用链表存储。

spring事物的管理,spring有一个默认主数据源,与一个默认主数据源的事物管理器(一个数据源一个事物管理器).同时transaction注解只对这个数据源管理器起作用.

流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UmrOxZ8C-1602061137157)(事物流程.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-brRZH9Do-1602061137159)(代理.jpg)]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值