Spring、MyBatis、Druid、MySQL使用事务执行SQL语句分析

Druid数据库连接 | Spring Boot 集成 Druid实现数据库连接和完善的SQL执行监控 我们知道,应用系统最频繁,最主要的操作还是数据库的操作,所以数据库的性能和安全对于整个系统平台的重要性不言而喻。为了提高数据库性能,我们可以使用数据库连接池,有时候我们需要增加一些列的日志或是数据库性能监控工具来确保数据库的性能,同时还得防范数据库的SQL注入等安全问题。 所以,今天我们来介绍一款集数据库连接池、数据库监控、SQL执行日志于一身的神器:Druid。 一、Druid简介 Druid 是阿里巴巴开源平台上的一个数据库连接池项目,它结合了 C3P0、DBCP 等数据库池的优点,同时加入了S 阅读详情

1. 前言

使用MySQL数据库时,使用事务与不使用事务相比,出现问题时排查更复杂。

不使用事务时,客户端只需要请求MySQL服务一次(只考虑显式执行的SQL语句);使用事务时,客户端至少需要请求MySQL服务四次(开启事务、执行SQL语句、提交/回滚事务、恢复自动提交)。

在Java中存在一些用法会导致事务失效,有的问题比较明显可以较快定位,有的问题隐藏较深可能需要较长时间排查。

因此需要对MySQL的事务执行原理进行分析,并整理用于排查事务相关问题的快速有效的方法。

在Java应用中访问MySQL服务时,涉及Java应用、网络传输、MySQL服务这三层,在每一层都可以对执行的SQL语句与事务操作进行监控与观测,涉及的内容如下图所示:

在这里插入图片描述

以下对Java应用使用Spring、MyBatis、Druid、MySQL Connector,使用事务执行SQL语句的情况进行分析。

可参考以下相关内容:

内容链接
MySQL SQL语句与事务执行及日志分析https://blog.youkuaiyun.com/a82514921/article/details/126563449
tcpdump、Wireshark抓包分析MySQL SQL语句与事务执行https://blog.youkuaiyun.com/a82514921/article/details/126563471
Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析https://blog.youkuaiyun.com/a82514921/article/details/126563515
Spring、MyBatis、Druid、MySQL使用事务执行SQL语句分析https://blog.youkuaiyun.com/a82514921/article/details/126563542
Spring、MyBatis、Druid、MySQL执行SQL语句与事务监控https://blog.youkuaiyun.com/a82514921/article/details/126563558
数据源使用错误导致MySQL事务失效分析https://blog.youkuaiyun.com/a82514921/article/details/126563573
TiDB乐观事务、悲观事务模型验证https://blog.youkuaiyun.com/a82514921/article/details/126563502

2. 使用事务执行SQL语句大致阶段

在使用事务执行SQL语句时,每次执行SQL语句时的大致阶段如下:

从连接池借出连接(可能需要创建新连接)
关闭自动提交
执行SQL语句1
执行SQL语句n
提交/回滚事务
开启自动提交
归还连接至连接池

如下图所示:

在这里插入图片描述

使用事务执行SQL语句时,事务管理主要通过Spring完成,SQL语句执行主要通过MyBatis完成

3. 不使用与使用事务执行SQL语句的区别

3.1. SQL语句执行阶段的区别

  • 不使用事务执行SQL语句

当不使用事务执行SQL语句时,每次执行SQL语句时,都需要先从连接池借出连接,再执行SQL语句,最后归还连接至连接池;

SQL语句会自动提交,不需要执行事务相关的阶段。

  • 使用事务执行SQL语句

当使用事务执行SQL语句时,除了以上阶段外,还需要执行事务相关的阶段,包括:关闭自动提交、提交/回滚事务、开启自动提交等。

3.2. 对系统变量autocommit使用的区别

  • 不使用事务执行SQL语句

当不使用事务执行SQL语句时,可一直使用MySQL系统变量autocommit的默认值(开启),不需要对系统变量autocommit进行修改与恢复;

  • 使用事务执行SQL语句

当使用事务执行SQL语句时,需要先关闭当前连接会话的autocommit,以开启事务,再执行SQL语句;

在归还连接至连接池之前,需要开启当前连接会话的autocommit,使该系统变量恢复为默认值开启(保证当前会话能够继续正常使用)。

3.3. 对数据库连接使用的区别

在执行SQL语句时,无论是否使用事务,在从连接池借出连接,到归还连接至连接池这两个阶段之间的时间段,对应的连接是被当前线程独占的。

  • 不使用事务执行SQL语句

当不使用事务执行SQL语句时,对于所使用的数据库连接没有特殊要求,每次执行SQL语句时从连接池获取一个可用的连接即可;

每条SQL语句执行完毕后,即可归还连接至连接池;

从连接池借出连接、归还连接至连接池的操作,通过MyBatis完成。

  • 使用事务执行SQL语句

当使用事务执行SQL语句时,需要使用同一个连接执行当前事务的所有SQL语句(一条或多条);

执行完所有的SQL语句(以及提交/回滚事务、开启自动提交)之后,才能归还连接至连接池。

从连接池借出连接、归还连接至连接池的操作,通过Spring完成。

4. 示例项目

以下使用的示例项目下载地址为:https://github.com/Adrninistrator/DB-Transaction-test,使用说明可参考“README.md”,

相关的执行日志保存在DB-Transaction-test-log目录中,log-print_stack_trace_off目录中是未打印调用堆栈的日志,log-print_stack_trace_on目录中是打印调用堆栈的日志。

5. 验证环境

  • JDK版本

1.8

  • Spring版本

5.3.20

  • Mybatis版本

org.mybatis:mybatis:3.2.8
org.mybatis:mybatis-spring:1.2.2

  • Druid版本

1.2.10

  • MySQL Connector版本

8.0.29

  • MySQL版本

MariaDB 10.0.36

  • Spring事务传播机制

默认值REQUIRED

6. 通过@Transactional注解使用事务的处理步骤

通过Spring的@Transactional注解使用事务时,与使用事务模板TransactionTemplate存在一些区别。使用@Transactional注解时,会先经过Spring AOP的相关处理,在org.springframework.transaction.interceptor.TransactionAspectSupport类的invokeWithinTransaction()方法中,会进行事务相关的以下处理:

调用TransactionAttributeSource tas的getTransactionAttribute()方法,在该方法中获取@Transactional注解所在方法上的事务属性TransactionAttribute txAttr;

调用determineTransactionManager()方法,在该方法中根据事务属性txAttr获取事务管理器TransactionManager tm。在determineTransactionManager()方法中,优先使用@Transactional注解的value或transactionManager属性指定的事务管理器的名称;若未指定时则使用默认的事务管理器,默认的事务管理器名称可通过Spring XML中“tx:annotation-driven”元素的“transaction-manager”属性指定;

在示例项目中指定的事务管理器类型为org.springframework.jdbc.datasource.DataSourceTransactionManager,未实现ReactiveTransactionManager接口,因此不会执行满足“this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager”判断条件时才会执行的代码;

调用asPlatformTransactionManager()方法,在该方法中检查以上获取的事务管理器TransactionManager tm应为null,或有实现PlatformTransactionManager接口,不满足时会抛出异常。在示例项目中使用的事务管理器DataSourceTransactionManager有实现PlatformTransactionManager接口,因此当前方法不会抛出异常,可以继续执行;

在示例项目中使用的事务管理器类型为DataSourceTransactionManager,未实现CallbackPreferringPlatformTransactionManager接口,因此会执行满足“txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)”判断条件时才会执行的代码;

调用createTransactionIfNecessary()方法,在该方法中调用prepareTransactionInfo()方法,经过以上处理后,获得TransactionInfo类型的对象txInfo,该对象中的transactionManager成员变量为当前使用的事务管理器;

调用invocation.proceedWithInvocation()方法,执行@Transactional注解所在的需要在事务中执行的自定义方法;

调用自定义方法若出现异常,会调用completeTransactionAfterThrowing()方法,在该方法中调用当前使用的事务管理器的rollback()或commit()方法,进行事务回滚或事务提交,详细说明见后续内容;

调用自定义方法未出现异常时,调用commitTransactionAfterReturning()方法,在该方法中调用txInfo.getTransactionManager().commit()方法,即调用当前使用的事务管理器DataSourceTransactionManager的commit()方法进行事务提交。

以上步骤对应的TransactionAspectSupport.invokeWithinTransaction()方法代码如下:

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();
  final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
  final TransactionManager tm = determineTransactionManager(txAttr);

  if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
      ...
  }

  PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
  final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

  if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
    // Standard transaction demarcation with getTransaction and commit/rollback calls.
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, 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);
    }

    if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
      ...
    }

    commitTransactionAfterReturning(txInfo);
    return retVal;
  }
  ...

7. 使用事务执行SQL语句详细过程

使用事务执行SQL语句时,可以分为以下三个主要的阶段:

  • 从连接池借出连接、关闭自动提交阶段,由Spring完成;

  • 执行SQL语句阶段,由MyBatis完成;

  • 提交/回滚事务、开启自动提交、归还连接至连接池阶段,由Spring完成。

7.1. 创建连接

使用事务执行SQL语句,在创建连接时,与“Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析”中的内容类似,略。

7.2. 执行SQL语句前Spring的处理

使用事务执行SQL语句时,执行SQL语句之前,包含以下操作,通过Spring完成,主要步骤在DataSourceTransactionManager及其父类AbstractPlatformTransactionManager中。

7.2.1. 从连接池借出连接

  • 使用@Transactional注解的处理

使用@Transactional注解时,从项目代码开始,到Spring的AbstractPlatformTransactionManager.getTransaction()方法,调用堆栈如下:

test.db.transaction.normal.commit.transaction_manager.TestNormalCommit_Ds1Mapper_Ds1TM.doTest(TestNormalCommit_Ds1Mapper_Ds1TM:15)
test.db.transaction.service.TransactionService$$EnhancerBySpringCGLIB$$26f4c143.runByTransactionalDs1TM(<generated>)
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy:708)
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy:763)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation:186)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor:119)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport:382)
org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport:595)
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager:373)

从以上调用堆栈可以看到,执行过程经过了Spring AOP增强的类(EnhancerBySpringCGLIB),也经过了以上描述的TransactionAspectSupport.invokeWithinTransaction()方法。

  • 使用TransactionTemplate的处理

使用TransactionTemplate时,从项目代码开始,到AbstractPlatformTransactionManager.getTransaction()方法,调用堆栈如下:

test.db.transaction.service.TransactionService.runByTransactionTemplate(TransactionService:79)
org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate:137)
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager:373)

从以上调用堆栈可以看到,执行过程中不需要经过Spring AOP处理,在调用TransactionTemplate.execute()方法后,就调用了AbstractPlatformTransactionManager.getTransaction()方法方法。

  • 后续步骤相同的处理

使用@Transactional注解或TransactionTemplate时,后续的步骤相同,从AbstractPlatformTransactionManager.getTransaction()方法开始,到Druid从连接池借出连接,调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager:373)
org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager:400)
org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager:265)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:100)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:1399)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:1407)
com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl:5055)
test.db.druid_filter.DruidMonitorFilter.dataSource_getConnection(DruidMonitorFilter:59)
com.alibaba.druid.filter.FilterAdapter.dataSource_getConnection(FilterAdapter:2756)
com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl:5059)
com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource:1429)
com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource:1783)
com.alibaba.druid.pool.DruidPooledConnection.<init>(DruidPooledConnection:79)

7.2.2. 关闭自动提交

使用事务执行SQL语句时,在从连接池借出连接后,需要关闭自动提交,使当前会话开启事务。

从AbstractPlatformTransactionManager.startTransaction()方法,经过Druid,到MySQL Connector通过NativeProtocol.sendQueryPacket()方法向MySQL发送SQL语句,调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager:400)
org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager:287)
com.alibaba.druid.pool.DruidPooledConnection.setAutoCommit(DruidPooledConnection:740)
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.setAutoCommit(ConnectionProxyImpl:429)
com.alibaba.druid.filter.FilterChainImpl.connection_setAutoCommit(FilterChainImpl:733)
test.db.druid_filter.DruidMonitorFilter.connection_setAutoCommit(DruidMonitorFilter:259)
com.alibaba.druid.filter.FilterAdapter.connection_setAutoCommit(FilterAdapter:986)
com.alibaba.druid.filter.FilterChainImpl.connection_setAutoCommit(FilterChainImpl:738)
com.mysql.cj.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl:2033)
com.mysql.cj.NativeSession.execSQL(NativeSession:663)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryString(NativeProtocol:997)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol:1051)

在MySQL Connector的ConnectionImpl.setAutoCommit()方法中,可能执行“SET autocommit=1”或“SET autocommit=0”语句。在关闭自动提交时,执行的SQL语句为“SET autocommit=0”。

从MySQL Connector的NativeProtocol.sendQueryPacket()方法,到通过输出流向MySQL服务发送SQL语句,调用堆栈如下:

com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol:1051)
com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol:683)
com.mysql.cj.protocol.a.NativeProtocol.send(NativeProtocol:619)
com.mysql.cj.protocol.a.TimeTrackingPacketSender.send(TimeTrackingPacketSender:50)
com.mysql.cj.protocol.a.SimplePacketSender.send(SimplePacketSender:51)
java.io.FilterOutputStream.write(FilterOutputStream:97)
java.io.BufferedOutputStream.write(BufferedOutputStream:117)

7.2.3. 在ThreadLocal中记录ConnectionHolder

在关闭自动提交之后,执行SQL语句之前,需要在ThreadLocal中记录org.springframework.jdbc.datasource.ConnectionHolder,在该类中保存了数据库连接对象,在后续执行SQL语句时需要使用。

在Spring的DataSourceTransactionManager.doBegin()方法中,会调用TransactionSynchronizationManager.bindResource()方法,在ThreadLocal的Map中记录信息,传入的key类型为javax.sql.DataSource(使用Druid时为com.alibaba.druid.pool.DruidDataSource),value为ConnectionHolder。

7.3. 执行SQL语句时MyBatis的处理

使用事务时,以下执行SQL语句的操作通过MyBatis完成。

7.3.1. 从调用MyBatis的Mapper接口到SimpleExecutor

使用事务执行SQL语句,从调用MyBatis的Mapper接口到SimpleExecutor,与“Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析”中的内容类似,略。

7.3.2. 执行第一条SQL语句

在事务中执行第一条SQL语句与执行非第一条SQL语句相比,获取数据库连接的步骤存在区别。

7.3.2.1. 从ThreadLocal中获取ConnectionHolder

在事务中执行第一条SQL语句时,需要从ThreadLocal中获取ConnectionHolder,以获得数据库连接对象。

执行查询操作时,从MyBatis的SimpleExecutor.doQuery()方法开始,到Spring的TransactionSynchronizationManager.doGetResource()方法,调用堆栈如下:

org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor:59)
org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor:72)
org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor:279)
org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction:67)
org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction:81)
org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils:80)
org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils:105)
org.springframework.transaction.support.TransactionSynchronizationManager.getResource(TransactionSynchronizationManager:135)
org.springframework.transaction.support.TransactionSynchronizationManager.doGetResource(TransactionSynchronizationManager:157)

执行更新操作时,SimpleExecutor.doUpdate()方法也会调用prepareStatement()方法,后续调用堆栈相同。

在事务中执行第一条SQL语句时,执行到SpringManagedTransaction类的getConnection()方法时,成员变量Connection connection为null,需要调用openConnection()方法打开连接;

SpringManagedTransaction类的相关方法代码如下:

public Connection getConnection() throws SQLException {
  if (this.connection == null) {
    openConnection();
  }
  return this.connection;
}

private void openConnection() throws SQLException {
  this.connection = DataSourceUtils.getConnection(this.dataSource);
  this.autoCommit = this.connection.getAutoCommit();
  this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

后续经过Spring的DataSourceUtils类,到TransactionSynchronizationManager.doGetResource()方法,从ThreadLocal中根据DataSource获取对应的ConnectionHolder。

7.3.2.2. 在SpringManagedTransaction中记录Connection

如以上所示的SpringManagedTransaction.openConnection()方法的代码,在调用DataSourceUtils.getConnection()方法获取到ConnectionHolder后,会将其记录在成员变量Connection connection中。

7.3.2.3. 执行SQL语句

使用事务执行SQL语句时,与“Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析”中的内容类似,略。

7.3.3. 执行非第一条SQL语句

7.3.3.1. 从SpringManagedTransaction中获取Connection

在事务中执行非第一条SQL语句时,需要从SpringManagedTransaction中获取Connection,以获得数据库连接。

执行更新操作时,从MyBatis的SimpleExecutor.doUpdate()方法开始,到SpringManagedTransaction.getConnection()方法,调用堆栈如下:

org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor:47)
org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor:72)
org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor:279)
org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction:69)

执行查询操作时,SimpleExecutor.doQuery()方法也会调用prepareStatement()方法,后续调用堆栈相同。

如以上所示的SpringManagedTransaction.getConnection()方法的代码,在事务中执行非第一条SQL语句时,成员变量Connection connection非null,会直接返回其值,不会调用openConnection()方法。

7.3.3.2. 执行SQL语句

略。

7.4. 执行SQL语句后Spring的处理

使用事务执行SQL语句时,执行SQL语句之后,以下操作通过Spring完成,主要步骤在AbstractPlatformTransactionManager.commit()/rollback()方法中,分别对应提交事务/回滚事务的场景。

7.4.1. 提交事务

  • 使用@Transactional注解的处理

使用@Transactional注解时,从项目代码开始,到AbstractPlatformTransactionManager.commit()方法,调用堆栈如下:

test.db.transaction.normal.commit.transaction_manager.TestNormalCommit_Ds1Mapper_Ds1TM.doTest(TestNormalCommit_Ds1Mapper_Ds1TM:15)
test.db.transaction.service.TransactionService$$EnhancerBySpringCGLIB$$26f4c143.runByTransactionalDs1TM(<generated>)
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy:708)
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy:763)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation:186)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor:119)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport:407)
org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport:654)
org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager:711)
  • 使用TransactionTemplate的处理

使用TransactionTemplate时,从项目代码开始,到AbstractPlatformTransactionManager.commit()方法,调用堆栈如下:

test.db.transaction.service.TransactionService.runByTransactionTemplate(TransactionService:79)
org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate:152)
org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager:711)
  • 后续步骤相同的处理

使用@Transactional注解或TransactionTemplate时,后续的步骤相同,从AbstractPlatformTransactionManager.commit()方法开始,经过Druid,到MySQL Connector向MySQL发送SQL语句,调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager:711)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager:743)
org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager:333)
com.alibaba.druid.pool.DruidPooledConnection.commit(DruidPooledConnection:782)
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.commit(ConnectionProxyImpl:122)
com.alibaba.druid.filter.FilterChainImpl.connection_commit(FilterChainImpl:194)
test.db.druid_filter.DruidMonitorFilter.connection_commit(DruidMonitorFilter:198)
com.alibaba.druid.filter.FilterAdapter.connection_commit(FilterAdapter:782)
com.alibaba.druid.filter.FilterChainImpl.connection_commit(FilterChainImpl:199)
com.mysql.cj.jdbc.ConnectionImpl.commit(ConnectionImpl:794)
com.mysql.cj.NativeSession.execSQL(NativeSession:663)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryString(NativeProtocol:997)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol:1051)

在MySQL Connector的ConnectionImpl.commit()方法中,会执行“commit”语句,用于提交事务。

7.4.2. 回滚事务

  • 使用@Transactional注解的处理

使用@Transactional注解时,从项目代码开始,到AbstractPlatformTransactionManager.rollback()方法,调用堆栈如下:

test.db.transaction.normal.rollback.transaction_manager.TestNormalRollback_Ds1Mapper_Ds1TM.lambda$doTest$0(TestNormalRollback_Ds1Mapper_Ds1TM:16)
test.db.transaction.service.TransactionService$$EnhancerBySpringCGLIB$$26f4c143.runByTransactionalDs1TM(<generated>)
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy:708)
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy:763)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation:186)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor:119)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport:392)
org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport:672)
org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager:809)

在TransactionAspectSupport.completeTransactionAfterThrowing()方法中,判断以下两个条件是否都满足:

a. 参数中的TransactionInfo txInfo对象的transactionAttribute非空;

b. txInfo.transactionAttribute.rollbackOn(ex)方法返回值为true。

当以上判断条件都满足时,调用txInfo.getTransactionManager().rollback()方法,通过当前使用的事务管理器的rollback()方法回滚事务;

当以上判断条件存在不满足时,调用txInfo.getTransactionManager().commit()方法,通过当前使用的事务管理器的commit()方法提交事务。

以上rollbackOn()方法用于判断出现异常时是否需要使事务回滚,当@Transactional注解的rollbackFor、rollbackForClassName、noRollbackFor、noRollbackForClassName属性与异常对象ex的类型匹配时,代表需要回滚事务;

若以上不匹配,再通过RuleBasedTransactionAttribute的父类DefaultTransactionAttribute的rollbackOn()方法,判断异常对象是否满足RuntimeException、Error类型,若满足则代表需要回滚事务;

若以上都不满足,则代表不回滚事务。

根据以上内容可知,通过@Transactional注解使用事务时,若出现异常,对于需要回滚的异常类型,会使事务回滚;对于其他异常类型,事务会提交。

TransactionAspectSupport.completeTransactionAfterThrowing()方法的代码如下:

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
  if (txInfo != null && txInfo.getTransactionStatus() != null) {
    if (logger.isTraceEnabled()) {
      logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
          "] after exception: " + ex);
    }
    if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
      try {
        txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
      }
      catch (...) {
        ...
      }
    }
    else {
      // We don't roll back on this exception.
      // Will still roll back if TransactionStatus.isRollbackOnly() is true.
      try {
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
      }
  • 使用TransactionTemplate的处理

使用TransactionTemplate时,从项目代码开始,到AbstractPlatformTransactionManager.rollback()方法,调用堆栈如下:

test.db.transaction.service.TransactionService.runByTransactionTemplate(TransactionService:79)
org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate:144)
org.springframework.transaction.support.TransactionTemplate.rollbackOnException(TransactionTemplate:168)
org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager:809)

在Spring的TransactionTemplate.execute()方法中,在调用TransactionCallback<T> action参数的doInTransaction()方法,执行需要在事务中执行的自定义方法时,会进行异常处理:

当捕获到满足RuntimeException或Error类型的异常时,调用rollbackOnException()方法进行回滚处理;

当捕获到其他类型的异常时,在调用rollbackOnException()方法进行回滚处理后,还会抛出UndeclaredThrowableException异常。

TransactionTemplate.execute()方法的代码如下:

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 {
      result = action.doInTransaction(status);
    }
    catch (RuntimeException | Error ex) {
      // Transactional code threw application exception -> rollback
      rollbackOnException(status, ex);
      throw ex;
    }
    catch (Throwable ex) {
      // Transactional code threw unexpected exception -> rollback
      rollbackOnException(status, ex);
      throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
    }
    this.transactionManager.commit(status);
    return result;
  }
}
  • 后续步骤相同的处理

使用@Transactional注解或TransactionTemplate时,后续的步骤相同,从AbstractPlatformTransactionManager.rollback()方法开始,到MySQL Connector向MySQL发送SQL语句,调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager:809)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager:835)
org.springframework.jdbc.datasource.DataSourceTransactionManager.doRollback(DataSourceTransactionManager:348)
com.alibaba.druid.pool.DruidPooledConnection.rollback(DruidPooledConnection:814)
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.rollback(ConnectionProxyImpl:399)
com.alibaba.druid.filter.FilterChainImpl.connection_rollback(FilterChainImpl:709)
test.db.druid_filter.DruidMonitorFilter.connection_rollback(DruidMonitorFilter:216)
com.alibaba.druid.filter.FilterAdapter.connection_rollback(FilterAdapter:974)
com.alibaba.druid.filter.FilterChainImpl.connection_rollback(FilterChainImpl:714)
com.mysql.cj.jdbc.ConnectionImpl.rollback(ConnectionImpl:1824)
com.mysql.cj.jdbc.ConnectionImpl.rollbackNoChecks(ConnectionImpl:1927)
com.mysql.cj.NativeSession.execSQL(NativeSession:663)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryString(NativeProtocol:997)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol:1051)
com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol:635)

在MySQL Connector的ConnectionImpl.rollbackNoChecks()方法中,会执行“rollback”语句,用于回滚事务。

7.4.3. 清理ThreadLocal中的ConnectionHolder

在提交事务或回滚事务时,都会清理ThreadLocal中的ConnectionHolder,提交事务与回滚事务分别调用AbstractPlatformTransactionManager类的commit()/rollback()方法,后续都会调用到cleanupAfterCompletion()方法。

从AbstractPlatformTransactionManager类的commit()方法到cleanupAfterCompletion()方法的调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager:711)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager:790)
org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager:992)

从AbstractPlatformTransactionManager类的rollback()方法到cleanupAfterCompletion()方法的调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager:809)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager:875)
org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager:992)

从AbstractPlatformTransactionManager.cleanupAfterCompletion()方法到TransactionSynchronizationManager.unbindResource()方法的调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager:992)
org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager:371)
org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource(TransactionSynchronizationManager:197)

在调用TransactionSynchronizationManager.unbindResource()方法时,传入参数key的类型为com.alibaba.druid.pool.DruidDataSource,会将ThreadLocal的Map中以上key对应的键值对清理,对应的value类型为ConnectionHolder。

7.4.4. 开启自动提交

在归还连接至连接池之前,需要开启自动提交,用于将当前会话的系统变量autocommit恢复默认值。

从AbstractPlatformTransactionManager.cleanupAfterCompletion()方法开始,经过Druid,到MySQL Connector向MySQL发送SQL语句,调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager:992)
org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager:378)
com.alibaba.druid.pool.DruidPooledConnection.setAutoCommit(DruidPooledConnection:740)
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.setAutoCommit(ConnectionProxyImpl:429)
com.alibaba.druid.filter.FilterChainImpl.connection_setAutoCommit(FilterChainImpl:733)
test.db.druid_filter.DruidMonitorFilter.connection_setAutoCommit(DruidMonitorFilter:259)
com.alibaba.druid.filter.FilterAdapter.connection_setAutoCommit(FilterAdapter:986)
com.alibaba.druid.filter.FilterChainImpl.connection_setAutoCommit(FilterChainImpl:738)
com.mysql.cj.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl:2033)
com.mysql.cj.NativeSession.execSQL(NativeSession:663)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryString(NativeProtocol:997)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol:1051)

在MySQL Connector的ConnectionImpl.setAutoCommit()方法中,在开启自动提交时,会执行“SET autocommit=1”语句。

7.4.5. 归还连接至连接池

从AbstractPlatformTransactionManager.cleanupAfterCompletion()方法开始,到Druid归还连接至连接池,调用堆栈如下:

org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager:992)
org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion(DataSourceTransactionManager:391)
org.springframework.jdbc.datasource.DataSourceUtils.releaseConnection(DataSourceUtils:360)
org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils:393)
org.springframework.jdbc.datasource.DataSourceUtils.doCloseConnection(DataSourceUtils:406)
com.alibaba.druid.pool.DruidPooledConnection.close(DruidPooledConnection:286)
com.alibaba.druid.filter.FilterChainImpl.dataSource_recycle(FilterChainImpl:5045)
test.db.druid_filter.DruidMonitorFilter.dataSource_releaseConnection(DruidMonitorFilter:119)
com.alibaba.druid.filter.FilterAdapter.dataSource_releaseConnection(FilterAdapter:2750)
com.alibaba.druid.filter.FilterChainImpl.dataSource_recycle(FilterChainImpl:5049)
com.alibaba.druid.pool.DruidPooledConnection.recycle(DruidPooledConnection:351)
com.alibaba.druid.pool.DruidDataSource.recycle(DruidDataSource:1951)
com.alibaba.druid.pool.DruidConnectionHolder.reset(DruidConnectionHolder:296)

7.5. 关闭连接

使用事务执行SQL语句,在关闭连接时,与“Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析”中的内容类似,略。

druid mysql 事务处理_Springboot+Mybatis+Druid 实现多数据源,与事务管理 1.需求一个主库的数据已经不能满足我们生产需求,需要额外的辅助库集成到项目里来获取所需的数据。或是将读写分开为两个库,完成自有的逻辑。2.实现原理如同在Springboot中配置一个数据源一样,我们需要一个主数据源,然后再用同样的方法配置另外一个数据源作为辅,两个数据源有各自独立的DataSourceTransactionManager与SqlSessionFactroy。3.具体的实现1) po... 阅读详情

相关推荐

mysql+Spring + SpringMVC + Druid + MyBatisi源码/框架分析-druid参数

Spring + SpringMVC + Druid + MyBatis ------------- sqlSessionFactory 提前加载找不到${jdbc.username}------------- 参考:https://www.cnblogs.com/java-class/p/6229920.html a.MapperScannerConfigurer 提前初始化导致 spring 注入配置文件失效 <bean class="org.mybatis.spri...

qq_15458763的博客 366

mybatis中的自动提交事务的设置

通过源码、在mybatis中只要在创建SQLSession时设置里面的值为true就可以了。

m0_67400972的博客 2185

老年人教程:MyBatis拦截器动态修改SQL(更新与插入)语句

注:本文编写与 2019年12月17日, 内容可能存在时效性问题。 数据库使用MySQL5.7 集成于SpringBoot 2.0.X , 引用国产的开源工具类Hutool 本教程建议显示大纲视图 配合食用 一 、简介 设定使用场景 任意 insert 、update 语句,都需要记录下语句的操作用户(CREATOR), 但由于系统早期设计不规范,大量SQL语句中,部分有写入CREAT...

波特多的博客 4530

SpringBoot 连接mysql踩到的坑

首先对于用SpringBoot连接mysql我先说明一下pom文件中需要引入那些jar: &lt;dependency&gt; &lt;groupId&gt;mysql&lt;/groupId&gt; &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt; &lt;version&gt;6.0.6...

wangliang369的博客 5393

Druid连接池与Spring AOP监控配置实战:实现业务方法级SQL追踪

在Java企业级应用开发中,数据库连接池和面向切面编程(AOP)是构建高性能、可维护系统的两大核心技术。数据库连接池负责高效管理数据库连接资源,而Spring AOP则通过拦截特定连接点,实现了日志、事务、监控等横切关注点的统一管理。将两者结合,其技术价值在于能够将JDBC层面的SQL执行监控,与业务逻辑层的方法调用进行精准关联,从而构建从业务方法到具体SQL的完整可观测链路。这一融合方案尤其适用于需要深度分析性能瓶颈、审计数据操作行为的应用场景,例如高并发电商系统或复杂的企业级应用。通过配置Druid的`

aocaiti5781的博客 690

SSM+MySQL农场信息管理系统实战:从建库到避坑的完整落地指南

在农业信息化与农产品溯源需求持续增长的背景下,农场信息管理系统需要处理地块、批次、农事记录、投入品出入库等多实体间的复杂关联关系。SSM框架通过Spring管理事务与依赖、SpringMVC分发请求、MyBatis将SQL显式收拢,配合MySQL 8的窗口函数与JSON类型,能够有效支撑这类字段频繁调整、报表SQL反复优化的业务场景。相比全自动ORM,MyBatis的XML映射让SQL可控可调,避免N+1查询与深分页性能陷阱;Druid连接池的空闲检测机制则解决了MySQL 8小时断连的经典问题。从地块录入

weixin_34239592的博客 367

mybatis结合spring执行Sql的流程

mybatis执行Sql的流程 首先讲上一篇出现过的一段代码new SqlSessionTemplate public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } 进入new SqlSessio

a429095958的博客 606

Spring Boot + MyBatis + Druid整合实践项目

本文还有配套的精品资源,点击获取 简介:本示例项目"spring-boot-mybatis-druid.7z"结合了Spring Boot、MyBatis和Druid数据源,演示了在Spring Boot框架中整合这些组件来构建一个基于MySQL 5.6的应用程序。项目包含了完整配置文件、源代码、数据库脚本等,旨在帮助开发者理解和学习如何搭建、运行此类系统。关键知识点涵盖S...

weixin_32311823的博客 1025

Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析

使用MySQL数据库时,使用事务与不使用事务相比,出现问题时排查更复杂。不使用事务时,客户端只需要请求MySQL服务一次(只考虑显式执行的SQL语句);使用事务时,客户端至少需要请求MySQL服务四次(开启事务、执行SQL语句、提交/回滚事务、恢复自动提交)。在Java中存在一些用法会导致事务失效,有的问题比较明显可以较快定位,有的问题隐藏较深可能需要较长时间排查。因此需要对MySQL的事务执行原理进行分析,并整理用于排查事务相关问题的快速有效的方法。在Java应用中访问MySQL服务时,涉及Java应用、

adrninistrat0r的博客 2025

黑马Java第二阶段:JavaWeb(笔记)

黑马Java第二阶段:JavaWeb(笔记)

chen的博客 5658

FFmpeg音频转AAC/M4A终极指南:从基础到高级参数调优

本文详细介绍了使用FFmpeg将音频转换为AAC/M4A格式的完整指南,从基础安装到高级参数调优。涵盖FDK-AAC编码器的使用、VBR/CBR模式选择、音质控制技巧以及实战案例,帮助用户在不同场景下实现高质量的音频转换。特别适合需要优化音频文件大小与音质平衡的开发者和音频处理专业人士。

rust6ferris的博客 726

Spring、MyBatis、Druid、MySQL执行SQL语句与事务监控

Druid支持使用自定义Filter,可对SQL语句与事务执行按照需要进行监控。使用Druid自定义Filter监控SQL语句与事务执行的效果,可参考“数据源使用错误导致MySQL事务失效分析”。在Spring XML中,可通过以下方式,在“com.alibaba.druid.pool.DruidDataSource”对应的bean中配置Druid自定义Filter。上类FilterEventAdapter为FilterAdapter的子类。...

adrninistrat0r的博客 1886

SPSS(二)SPSS实现多因素方差分析模型(图文教程+数据集)

SPSS(二)SPSS实现多因素方差分析模型 单因素方差分析上一篇博客https://blog.youkuaiyun.com/LuYi_WeiLin/article/details/89917656已经介绍完毕 这篇博客我们主要来学习多因素方差分析 多因素方差分析,就是同时考虑若干个控制因素的情况下,分别分析它们的改变是否造成观察变量的显著变动 (多个自变量,一个因变量)自变量类型以分类变量为主也可以...

可乐联盟 6万+

【SpringBoot框架篇】12.使用druid的monitor工具查看sql执行性能

使用druid的monitor工具查看sql执行性能

皓亮君的博客 8456

【CIC-IDS数据集+机器学习】实现网络流量异常检测

本项目基于CIC-IDS数据集,使用多种机器学习算法实现网络流量异常检测系统。项目采用了完整的机器学习工作流程,从数据预处理到模型部署,实现了高精度的网络入侵检测。

XiaoMu_001的博客 1935

【32】软考软件设计师——算法题(一):分治法与动态规划深度精讲|下午第4题常考满分攻略

本文是《软件设计师·50讲通关|从零基础到工程师职称》专栏第32篇,正式开启**模块四:应用技术(下午题)算法大题专项**,聚焦下午第4道高频必考大题核心两大算法:分治法与动态规划。全文超4000字深度拆解两大经典算法的底层核心思想、算法策略识别技巧、递归边界设计、状态转移方程推导,全覆盖代码填空高频考点、时间复杂度分析、经典题型解题套路;配套可直接默写的递归代码模板、DP状态设计范式、真题完整填空案例,彻底解决考生分不清分治与DP、递归边界写错、状态转移推不出、复杂度算不对四大核心痛点。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 638

SpringBoot使用Mybatis执行多条SQL

在SpringBoot中实现Mybatis执行多条SQL,先Google了一波,发现只需要在连接Mysql的url后面加上这个就可以了: allowMultiQueries=true 添加后的url是这样滴: jdbc:mysql://...:3306/test?useUnicode=true&amp;amp;characterEncoding=gbk&amp;amp;zeroDateTimeBehavior=c...

biubiu2it的博客 5404

微波技术仿真:微波谐振器分析_(2).微波谐振器理论

谐振器是一种能够在特定频率范围内存储和释放电磁能量的器件。在微波频段,谐振器通常用于滤波、频率选择和能量存储等应用。一个理想的谐振器在谐振频率下能够存储无限大的能量,但在实际应用中,由于损耗的存在,谐振器的性能会受到一定限制。微波谐振器在微波通信、雷达系统、微波测量和微波电路设计等领域中发挥着重要作用。谐振器的设计需要考虑多个因素,包括谐振频率的精确控制、损耗的最小化、频率稳定性的提高和小型化设计。通过理论分析和仿真工具的使用,可以有效优化谐振器的性能,满足实际应用的需求。

2401_87715305的博客 934

解决SpringBoot+Druid+Mybatis Plus 执行MySQL批量插入,更新 报错的问题

项目实际开发中,使用到了MySQL批量插入的语法,在MySQL中执行成功后,移植到项目时发生了问题,语句如下: REPLACE INTO table1( id, works_fusion_id, statistics_date, chapter_number, new_chapter, works_hot, works_like_number, ...

技术博客 6684

【Mode】微带传输线阻抗

微带线,英文名称为 (Microstrip) 是一种常见的平面传输线,主要用于传输微波频率信号(通常在射频和微波电路重中使用)。

u011699626的博客 244

边缘计算与产业生态系统

目录 文章目录目录边缘计算产业参与者边缘计算产业生态系统 边缘计算产业参与者 边缘计算领域参与者众多,主流公司以打造生态优势为目标,基于自身服务层级向产业链上下两端进行拓展,目前市场主要参与者有: 系统集成商:在产业头部,具有广泛的能力,可以集成不同生态系统公司的产品,具有一定的全球性属性,在边缘计算领域大多由云服务商、电信运营商担任。 云服务商:具有领先的云技术优势与应用生态体系,同时拥有全球性资源布局与市场资源生态,并渴望成为边缘计算主导者。 电信运营商:具有边缘基础设施优势,以及不可替代的

烟云的计算 2103

Spring Boot 使用p6spy 分析MyBatis-Plus 执行sql性能 出现的问题

问题 Caused by: java.lang.IllegalStateException: dbType not support : null, url jdbc:p6spy:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false at com.alibaba.druid.w...

IceStreamLab 2168

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Spring Boot 集成Mybatis Mysql Druid

这次准备使用Spring Boot来集成Mybatis,数据库则使用MySql,数据库连接池采用阿里巴巴的Druid,其实可以用其他连接池的,但是看了好几篇博客都推荐用这个就试了一下。而Mybatis目前有两种配置方式,一种是传统的XML配置,一种是基于注解的配置,在下面我将会简单的介绍一下两种配置方式。

TkmTtT的博客 4786

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

druid 执行sql_Spring Boot从零入门8_mybatis + druid + mysql + workbench + docker 入门

1 简介2 名词术语3 相关技术用法综述4 环境搭建4.1 开发工具4.2 Docker 搭建 MySQL 环境3 手动 MyBatis 映射器 Demo 实现3.1 实现步骤3.2 实现细节4 小结5 参考资料1 简介这篇文章主要学习关系型数据库主流的技术栈,我们使用 Docker 快速搭建一个 MySQL 数据库学习环境,通过 MySQL 官方提供的 Workbench 可视化工具的...

weixin_30957307的博客 300

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Spring Boot整合MyBatis

整合MyBatis之前,先搭建一个基本的Spring Boot项目开启Spring Boot。然后引入mybatis-spring-boot-starter和数据库连接驱动(这里使用关系型数据库mysql5.7.30)。

wno704的博客 728

SpringBoot集成MySQL数据库:tech-pdai-spring-demos中的JPA与MyBatis最佳实践

tech-pdai-spring-demos项目提供了Spring Framework5/SpringBoot 2.5.x的丰富示例,其中SpringBoot集成MySQL数据库的实现尤为全面,涵盖了JPA和MyBatis两大主流持久层框架的最佳实践。本文将带你快速掌握这两种技术在实际项目中的应用方法。 ## 一、环境准备:MySQL数据库配置指南 在SpringBoot项目中集成MySQL的

gitblog_00194的博客 1113
上一篇: Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析
下一篇: Spring、MyBatis、Druid、MySQL执行SQL语句与事务监控
adrninistrat0r
博客等级 码龄18年 395粉丝 136原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值