SQL业务完成简介

​ 首要咱们来了解下,最简略的业务是怎样完成的呢?以JDBC为例,当一个数据库Connection目标创立后,其会默许自动提交业务;每次履行SQL句子时,假如成功,就会向数据库自动提交,不能回滚。

​ 经过调用setAutoCommit(false)办法能够撤销自动提交业务。比及一切的SQL句子都履行成功后,调用commit()办法提交业务。假如其间某个操作失败或出现反常时,则调用rollback()办法回滚业务。详细代码如下所示:

    public void noTransaction() {
        Connection connection = null;
        String sql = "update account set balance=balance-100 where id=1";
        String sql2 = "update account set balance=balance+100 where id=2";
        //创立PreparedStatement目标
        PreparedStatement preparedStatement = null;
        try {
            connection = JDBCUtils.getConnection();// 获取数据库衔接
            connection.setAutoCommit(false);//业务开端
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate();//履行第一个sql
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();//履行sql2
            //提交业务
            connection.commit();
        } catch (SQLException e) {
            //进行业务回滚,默许回滚到业务开端的地方
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //封闭流
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }

​ 将代码笼统成履行过程,首要有以下四步:

  1. 获取Mysql链接
  2. 履行SQL句子
  3. 提交SQL业务
  4. 存在反常则做Mysql的业务回滚。

​ 能够发现,惯例情况下只要履行SQL句子的内容存在差异。假如能将相同部分抽取出来,接入方接入时只考虑SQL句子内容,就能够削减接入的成本。同时观察到抽取的部分处于履行SQL句子的前后,那么很自然的就能够想到两种解决方案:

​ 1、在JAVA8中,供给了函数式编程。咱们能够即将履行的SQL句子封装成函数,作为入参传入并履行。

​ 2、选用动态署理对履行SQL的前后做增强。

编程式业务

​ Spring中选用函数式编程完成的业务,被称为编程式业务。编程式业务的完成相对简略,首要由类TransactionTemplate担任完成。详细代码能够见如下所示:

@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
   Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
   if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
      return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
   }
   else {
       //获取业务 
      TransactionStatus status = this.transactionManager.getTransaction(this);
      T result;
      try {
          //履行SQL句子内容
         result = action.doInTransaction(status);
      }
      catch (RuntimeException | Error ex) {
         //反常回滚
         rollbackOnException(status, ex);
         throw ex;
      }
      catch (Throwable ex) {
         // 反常回滚
         rollbackOnException(status, ex);
         throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
      }
       //提交业务
      this.transactionManager.commit(status);
      return result;
   }
}

​ TransactionCallBack作为入参传入,其间就首要是咱们要履行的SQL句子内容。而其余部分能够看到,其实就和咱们前面所描绘的四步根本类似:

  1. 获取Mysql链接
  2. 履行SQL句子
  3. 提交SQL业务
  4. 存在反常则做Mysql的业务回滚。

声明式业务

​ 在Spring中,选用AOP做增强逻辑的被称为声明式业务。相比起编程式业务,声明式业务相对复杂。因而,在了解声明式业务之前,咱们需求先简略了解一下Spring是如何支撑AOP(动态署理)。首要咱们知道,Spring中Bean的存在方法有以下几个阶段:

image-20221108101023883.png
​ 其间非常要害点就在BeanFactory。当咱们对一个Bean界说署理目标后,BeanFactory生成的就不会是单纯的Bean实例目标,而是Bean的动态署理。经过调用Bean的动态署理中的办法,来完成AOP。那么如何自界说自己的AOP呢?要完成AOP需求明确两个点:

1、需求在哪里做增强?(界说切点)

2、需求做什么样的增强逻辑?(界说增强逻辑)

​ 关于这两点,Spring首要经过**业务署理办理装备类(ProxyTransactionManagementConfiguration)**进行完成。

image-20221106194731948.png

​ 从类图中能够看到,业务署理办理装备类首要界说了三个Bean目标:

  • 注释业务特点源(AnnotationTransactionAttributeSource),其首要担任判别当时类是否为需求增强的类,即”哪里需求做增强”。
  • 业务拦截器(TransactionInterceptor),该类首要担任对业务做链接获取、业务提交以及业务回滚。即”怎样做增强”。
  • Bean工厂业务特点源辅导(BeanFactoryTransactionAttributeSourceAdvisor),这个与业务自身无关,首要是在Bean工厂生产Bean实例的时分,方便对Bean进行替换使用的。其间首要是担任将界说的切点和增强逻辑注入到Spring中。

这里咱们逐一来介绍这三个Bean目标。

注释业务特点源

​ “哪里需求做增强”,意味着类要具有判别是否需增强的才能。为此,注释业务特点源供给了一个要害的办法:isCandidateClass()

但声明业务的注解必定不只一种。假如需求辨认一切包下的业务型注解,必定会需求屡次判别。因而,在注解业务特点源中,还保存了一组接口目标业务注释解析器(TransactionAnnotationParser),经过循环遍历这组业务注释解析器,就能够对不同结构注解进行处理。详细源码如下:

@Override
public boolean isCandidateClass(Class<?> targetClass) {
   for (TransactionAnnotationParser parser : this.annotationParsers) {
      if (parser.isCandidateClass(targetClass)) {
         return true;
      }
   }
   return false;
}

​ 以SpringTransactionAnnotationParser注释解析器为例,其完成的isCandidateClass()办法判别类是否被@Transactional类注释了,假如是,那么该类便是潜在的候选类。

@Override
public boolean isCandidateClass(Class<?> targetClass) {
   return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);
}

​ 依次类推,对@TransactionAttribute等其他结构的注释,咱们都能够选用这样办法完成。

业务拦截器

​ 具有了判别哪些类需求履行业务的才能后,咱们还需求确定详细的增强逻辑是什么姿态的。而这便是业务拦截器首要功用。要完成这个功用,需求在对应办法被调用时,履行增强办法。

image-20221106225748631.png

​ 从类图首要能够看到,为了能够察觉到办法的调用,业务拦截器完成了办法拦截器接口(MethodInterceptor)的invoke办法,在invoke办法中先判别当时履行的办法归于哪个类,紧接着会用invokeWithinTransaction()对办法进行业务性的包装。其源码如下:

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
    // 判别履行的办法归于哪个类
   Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    //再调用业务进行履行
   return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
      @Override
      @Nullable
      public Object proceedWithInvocation() throws Throwable {
         return invocation.proceed();
      }
      @Override
      public Object getTarget() {
         return invocation.getThis();
      }
      @Override
      public Object[] getArguments() {
         return invocation.getArguments();
      }
   });
}

​ 首要逻辑放在invokeWithinTransaction()办法中。在该办法中,首要考虑了三类不同的编程方法的业务,分别是:呼应式业务(ReactiveTransactionManager)回调优先型业务(CallbackPreferringPlatformTransactionManager)非回调优先型业务(非CallbackPreferringPlatformTransactionManager)

三者的差异首要在于:

1、呼应式编程常选用Mono或Flux完成,需求对两种方法挑选相应适配器做适配。

2、后两者从名字上能够看出差异,回调型优先的业务,会先履行回调再履行业务。而非回调优先型业务,则关注于业务的履行,至于回调的失败与否不需求影响业务的回滚。

虽然三者存在一些差异,但他们关于业务的完成其实是类似的,这里以非回调优先型业务为比如:

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {
   TransactionAttributeSource tas = getTransactionAttributeSource();
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
   final TransactionManager tm = determineTransactionManager(txAttr);
	.......
   PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
   if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
      // 创立业务
      TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
      Object retVal;
      try {
         // 履行办法
         retVal = invocation.proceedWithInvocation();
      } catch (Throwable ex) {
         // 回滚处理 + 抛出反常终止履行
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      } finally {
         cleanupTransactionInfo(txInfo);
      }
		// 正常履行了业务,此时再履行回调
      if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
         TransactionStatus status = txInfo.getTransactionStatus();
         if (status != null && txAttr != null) {
            retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
         }
      }
		// 提交业务
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
}

​ 源码自身不复杂,能够看到也是四步:

  1. 获取Mysql链接信息
  2. 履行SQL句子
  3. 提交SQL业务
  4. 存在反常则做Mysql的业务回滚。

Bean工厂业务特点源辅导

​ 关于Bean工厂业务特点源辅导,其首要担任用于界说切点和增强逻辑,并将这些业务的逻辑注册到Spring中用于完成。如下是Bean工厂业务特点源辅导的类图。

image-20221106222809284.png

​ 从类图上能够看到,其承继了AbstractPointcutAdvisor要害模版类,该类是Spring中用于界说切点和增强逻辑。经过指定PointCut和Advice,就能够完成自界说的增强逻辑。因而,Bean工厂业务特点源辅导只要将业务拦截器标记为增强逻辑,将注释业务特点源标记为切点,就能够让其在Spring中作为AOP收效。

​ 经过这三者的协作:注释业务特点源标示了切点(说明我那些办法需求做增强);业务拦截器界说了要履行的增强逻辑(说明我对这些办法怎样做增强);Bean工厂业务特点源辅导则将切点和增强逻辑注入到Spring中使其收效。然后完成了Spring的声明式业务的内容。

业务多样性支撑

​ 在前述内容中,咱们思考了SQL情况下如何完成业务。但有个问题,假如数据源换成Redission、换成分布式业务的API,代码还能快速复用么?简而言之,Spring是如何支撑数据源多样性?如何确保新数据源的快速接入?

​ 对完成业务的流程做进一步笼统,不难发现一次业务中,结构需求关注的功用其实只要三个:

  1. 获取业务链接
  2. 提交业务
  3. 业务回滚

​ 因而,对不同的数据源,都能够将其笼统成这三个才能。应用层只需求对这三个才能进行调用,就不会在因为基层数据源的差异而需求大幅度的改动。而这正与面向接口规划的思想不谋而合

image-20221107081257598.png

​ 为此,Spring专门规划了接口PlatformTransactionManager,其首要担任对外供给三个办法:getTransaction(definition)、commit(status)、rollback(status)。就用来笼统的上述的三个功用。由此一来,应用层的代码完成类(这里以TransactionTemplate为比如)就不再需求依靠于我的数据源究竟是JDBC、Redission仍是DataSource。面对笼统编程,然后削减了接入需求考虑不同类型所带来的成本。

总结

​ 本文介绍了Spring中针对SQL业务完成的两种方法:编程式业务声明式业务。同时介绍了关于多种不同的数据源,Spring在规划上的架构完成,期望对大家后续的开发规划有所协助。

参考文献

JDBC的业务与处理

详解 Spring 注解@Transactional业务控制原理

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。