spring事务@Transactional

定义一个项目测试 spring 的事务支持情况

项目准备

创建一个名为 mybatis-transaction 的 spring boot 2.x 项目,引入 mybatis 的依赖,如下

使用 spring 官网的脚手架生成项目 https://start.spring.io/

下载完毕后 pom.xml 如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.15</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>mybatis-transaction</artifactId><version>0.0.1-SNAPSHOT</version><name>mybatis-transaction</name><description>Demo project for Spring Boot</description><properties><java.version>8</java.version></properties><dependencies><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.18</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

其中,对应的组件版本如下

组件名版本作用说明
spring-boot-starter-parent2.7.15项目的基础架构spring 2.x 的高版本
mybatis-spring-boot-starter2.3.2mybatis持久层官方支持 spring boot 2.x 的最后一个版本
druid-spring-boot-starter1.2.18数据库连接池官方支持 spring boot 2.x 的最后一个版本
mysql-connector-j无需指定mysql数据库连接驱动jar

从后面的版本开始,就是需要使用 spring boot 3.x,但是需要使用 jdk 17。

数据库使用了 mysql 8.0.28。

创建数据库和对应的 ddl

CREATE DATABASE mybatis_transaction;USE mybatis_transaction;CREATE TABLE `user`
(
`id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'id',
`name` VARCHAR(50) NOT NULL COMMENT '用户名',
PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4 COMMENT='用户表';INSERT INTO `user`(`id`, `name`) VALUES (1, '1');CREATE TABLE `user_login_history`
(
`id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'id',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT  '用户id',
`operate_time` DATETIME NOT NULL COMMENT '操作时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4 COMMENT='用户登录历史表';

代码目录结构

java 代码如下

package com.example.controller;import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.io.Serializable;
import java.util.concurrent.atomic.LongAdder;@RestController
public class UserController implements Serializable {@Resourceprivate UserService userService;private LongAdder counter = new LongAdder();@GetMapping(value = "test")public String test() throws Throwable {User user = new User();user.setId(1L);user.setName("11");counter.add(1L);String string = String.valueOf(counter.longValue());user.setName(string);int no = userService.insert(user);return no > 0 ? "success" : "fail";}
}

package com.example.dao;import com.example.entity.UserLoginHistory;import java.io.Serializable;public interface UserLoginHistoryMapper extends Serializable {int insert(UserLoginHistory userLoginHistory);
}

 

package com.example.dao;import com.example.entity.User;import java.io.Serializable;public interface UserMapper extends Serializable {int insert(User user);int update(User user);
}

package com.example.entity;import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L;private Long id;private String name;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

package com.example.entity;import java.io.Serializable;
import java.time.LocalDateTime;public class UserLoginHistory implements Serializable {private static final long serialVersionUID = 1L;private Long id;private Long userId;private LocalDateTime operateTime;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}public LocalDateTime getOperateTime() {return operateTime;}public void setOperateTime(LocalDateTime operateTime) {this.operateTime = operateTime;}
}

package com.example.service;import com.example.entity.User;import java.io.Serializable;public interface UserService extends Serializable {int insert(User user) throws Exception;
}

修改用户名,插入相关记录,整体执行。

package com.example.service;import com.example.dao.UserLoginHistoryMapper;
import com.example.dao.UserMapper;
import com.example.entity.User;
import com.example.entity.UserLoginHistory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;@Service
public class UserServiceImpl implements UserService {@Resourceprivate UserMapper userMapper;@Resourceprivate UserLoginHistoryMapper userLoginHistoryMapper;@Transactional(rollbackFor = Exception.class)public int insert(User user) throws Exception {UserLoginHistory userLoginHistory = new UserLoginHistory();userLoginHistory.setUserId(1L);// userLoginHistory.setId(1L);userLoginHistory.setOperateTime(LocalDateTime.now());userLoginHistoryMapper.insert(userLoginHistory);int no = userMapper.update(user);File file = new File("fdsfd");File newFile = new File("fsdfs");try {FileCopyUtils.copy(file, newFile);} catch (IOException e) {throw e;}// Integer temp = null;// temp++;// int i = 1 / 0;return 0;}
}

package com.example.service;import com.example.dao.UserLoginHistoryMapper;
import com.example.entity.UserLoginHistory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.io.Serializable;@Service
public class UserLoginHistoryService implements Serializable {@Resourceprivate UserLoginHistoryMapper userLoginHistoryMapper;@Transactionalpublic int insert(UserLoginHistory userLoginHistory) {int no = userLoginHistoryMapper.insert(userLoginHistory);return no;}
}

xml 代码如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.UserMapper"><insert id="insert" parameterType="com.example.entity.User">insert into `user`(`id`, `name`) values (#{id}, #{name})</insert><update id="update" parameterType="com.example.entity.User">update `user` set `name` = #{name} where `id` = #{id}</update>
</mapper>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.UserLoginHistoryMapper"><insert id="insert" parameterType="com.example.entity.UserLoginHistory">insert into `user_login_history`(`id` , `user_id`, `operate_time`) values (#{id}, #{userId}, #{operateTime})</insert>
</mapper>

配置文件

application.yml

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_transaction?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=trueusername: rootpassword: 123456mybatis:mapper-locations: classpath:mapper/*.xml

设置数据库连接信息和 mybatis 的 mapper 位置

启动类

package com.example;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan(basePackages = {"com.example.dao"})
@SpringBootApplication
public class MybatisTransactionApplication {public static void main(String[] args) {SpringApplication.run(MybatisTransactionApplication.class, args);}}

指定 mapper 接口的位置

上述代码刚开始第一次不行,后面项目删除后重新弄了一遍好了。

验证过程

RuntimeException 异常 

@Transactional 未设置任何值验证一个除0异常,UserServiceImpl 修改代码如下

package com.example.service;import com.example.dao.UserLoginHistoryMapper;
import com.example.dao.UserMapper;
import com.example.entity.User;
import com.example.entity.UserLoginHistory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;@Service
public class UserServiceImpl implements UserService {@Resourceprivate UserMapper userMapper;@Resourceprivate UserLoginHistoryMapper userLoginHistoryMapper;@Transactionalpublic int insert(User user) throws Exception {UserLoginHistory userLoginHistory = new UserLoginHistory();userLoginHistory.setUserId(1L);// userLoginHistory.setId(1L);userLoginHistory.setOperateTime(LocalDateTime.now());userLoginHistoryMapper.insert(userLoginHistory);int no = userMapper.update(user);// File file = new File("fdsfd");// File newFile = new File("fsdfs");// try {//     FileCopyUtils.copy(file, newFile);// } catch (IOException e) {//     throw e;// }// Integer temp = null;// temp++;int i = 1 / 0;return 0;}
}

即 @Transactional 不加任何设置,默认回滚类型异常为 RuntimeException 或者 Error 的子类,ArithmeticException 为 RuntimeException 的子类符合要求。

异常堆栈信息如下

java.lang.ArithmeticException: / by zeroat com.example.service.UserServiceImpl.insert(UserServiceImpl.java:48) ~[classes/:na]at com.example.service.UserServiceImpl$$FastClassBySpringCGLIB$$a5ace360.invoke(<generated>) ~[classes/:na]at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.29.jar:5.3.29]at com.example.service.UserServiceImpl$$EnhancerBySpringCGLIB$$11a39b4c.insert(<generated>) ~[classes/:na]at com.example.controller.UserController.test(UserController.java:32) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0-292]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0-292]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0-292]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0-292]at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.79.jar:9.0.79]at java.lang.Thread.run(Thread.java:748) [na:1.8.0-292]

执行完后表 user_login_history 无记录。

非 RuntimeException 异常

@Transactional 设置 rollbackFor 为 Exception

UserServiceImpl 代码回退到刚开始,即在两个不存在的文件中复制数据。

异常堆栈信息如下

java.nio.file.NoSuchFileException: fdsfdat sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) ~[na:1.8.0-292]at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) ~[na:1.8.0-292]at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) ~[na:1.8.0-292]at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) ~[na:1.8.0-292]at java.nio.file.Files.newByteChannel(Files.java:361) ~[na:1.8.0-292]at java.nio.file.Files.newByteChannel(Files.java:407) ~[na:1.8.0-292]at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) ~[na:1.8.0-292]at java.nio.file.Files.newInputStream(Files.java:152) ~[na:1.8.0-292]at org.springframework.util.FileCopyUtils.copy(FileCopyUtils.java:68) ~[spring-core-5.3.29.jar:5.3.29]at com.example.service.UserServiceImpl.insert(UserServiceImpl.java:41) ~[classes/:na]at com.example.service.UserServiceImpl$$FastClassBySpringCGLIB$$a5ace360.invoke(<generated>) ~[classes/:na]at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.29.jar:5.3.29]at com.example.service.UserServiceImpl$$EnhancerBySpringCGLIB$$11a39b4c.insert(<generated>) ~[classes/:na]at com.example.controller.UserController.test(UserController.java:32) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0-292]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0-292]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0-292]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0-292]at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.79.jar:9.0.79]at java.lang.Thread.run(Thread.java:748) [na:1.8.0-292]

数据库数据无新增,说明正常。

@Transactional 未设置任何值

UserServiceImpl 代码修改如下

package com.example.service;import com.example.dao.UserLoginHistoryMapper;
import com.example.dao.UserMapper;
import com.example.entity.User;
import com.example.entity.UserLoginHistory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;@Service
public class UserServiceImpl implements UserService {@Resourceprivate UserMapper userMapper;@Resourceprivate UserLoginHistoryMapper userLoginHistoryMapper;@Transactionalpublic int insert(User user) throws Exception {UserLoginHistory userLoginHistory = new UserLoginHistory();userLoginHistory.setUserId(1L);// userLoginHistory.setId(1L);userLoginHistory.setOperateTime(LocalDateTime.now());userLoginHistoryMapper.insert(userLoginHistory);int no = userMapper.update(user);File file = new File("fdsfd");File newFile = new File("fsdfs");try {FileCopyUtils.copy(file, newFile);} catch (IOException e) {throw e;}// Integer temp = null;// temp++;// int i = 1 / 0;return 0;}
}

异常堆栈信息如下

java.nio.file.NoSuchFileException: fdsfdat sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) ~[na:1.8.0-292]at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) ~[na:1.8.0-292]at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) ~[na:1.8.0-292]at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) ~[na:1.8.0-292]at java.nio.file.Files.newByteChannel(Files.java:361) ~[na:1.8.0-292]at java.nio.file.Files.newByteChannel(Files.java:407) ~[na:1.8.0-292]at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) ~[na:1.8.0-292]at java.nio.file.Files.newInputStream(Files.java:152) ~[na:1.8.0-292]at org.springframework.util.FileCopyUtils.copy(FileCopyUtils.java:68) ~[spring-core-5.3.29.jar:5.3.29]at com.example.service.UserServiceImpl.insert(UserServiceImpl.java:41) ~[classes/:na]at com.example.service.UserServiceImpl$$FastClassBySpringCGLIB$$a5ace360.invoke(<generated>) ~[classes/:na]at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.29.jar:5.3.29]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.29.jar:5.3.29]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.29.jar:5.3.29]at com.example.service.UserServiceImpl$$EnhancerBySpringCGLIB$$50627c19.insert(<generated>) ~[classes/:na]at com.example.controller.UserController.test(UserController.java:32) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0-292]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0-292]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0-292]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0-292]at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.79.jar:9.0.79]at java.lang.Thread.run(Thread.java:748) [na:1.8.0-292]

查询后有数据,说明事务未生效。

这两种区别产生的原因是什么?

通过 @Transactional 在源码中进行搜索,发现通过 SpringTransactionAnnotationParser 获取 @Transactional 修饰的方法后进行事务属性处理,最终包装为 RuleBasedTransactionAttribute 对象进行传递。

https://github.com/spring-projects/spring-framework/blob/v5.3.29/spring-tx/src/main/java/org/springframework/transaction/annotation/SpringTransactionAnnotationParser.java#L68

@Transactional 配置了 rollbackFor 的值为 Exception.class

 https://github.com/spring-projects/spring-framework/blob/v5.3.29/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java#L670

@Transactional 未配置任何值

可以看到,@Transactional 配置了 rollbackFor 的值为 Exception.class 后对应的 RuleBasedTransactionAttribute 多了指定的值。

判断是否符合回滚要求

 调用 TransactionAttribute 的实现类进行责任链模式调用匹配最终的异常类型判断是否进行回滚。

DelegatingTransactionAttribute
↓
RuleBasedTransactionAttribute
↓
DefaultTransactionAttribute

如果方法上的 @Transactional 配置了 rollbackFor 的值且未指定 noRollbackFor 的值,则将其填充到 RollbackRuleAttribute 中,在 RuleBasedTransactionAttribute 中调用 RollbackRuleAttribute  的  getDepth() 层层递归获取到设置的类的全路径名,不调用 DefaultTransactionAttribute。

 

如果方法上的 @Transactional 未配置回滚或者不回滚的异常,则对应的 winner 变量为 null,调用父类 DefaultTransactionAttribute 中的 rollbackOn()

public boolean rollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error);
}

判断是否是 RuntimeException 或者 Error 的子类,如果是,则符合回滚要求,执行回滚。

通过源码得知如下

如果需要事务的方法上添加了 @Transactional(rollbackFor = Exception.class),则遇到异常都会抛出,否则就会执行事务管理器的提交处理。

@Transactional 的 rollbackFor 源码中标明了需要指定的是 Throwable 的子类,按照异常类的继承关系,Exception 是所有异常的父类,所以指定了 Exception 就包含大部分遇到的异常了。

 

事务怎么实现的

UserServiceImpl 中的 @Transactional 代码行注释掉

执行堆栈如下

java.nio.file.NoSuchFileException: fdsfdat sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) ~[na:1.8.0-292]at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) ~[na:1.8.0-292]at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) ~[na:1.8.0-292]at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) ~[na:1.8.0-292]at java.nio.file.Files.newByteChannel(Files.java:361) ~[na:1.8.0-292]at java.nio.file.Files.newByteChannel(Files.java:407) ~[na:1.8.0-292]at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) ~[na:1.8.0-292]at java.nio.file.Files.newInputStream(Files.java:152) ~[na:1.8.0-292]at org.springframework.util.FileCopyUtils.copy(FileCopyUtils.java:68) ~[spring-core-5.3.29.jar:5.3.29]at com.example.service.UserServiceImpl.insert(UserServiceImpl.java:41) ~[classes/:na]at com.example.controller.UserController.test(UserController.java:32) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0-292]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0-292]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0-292]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0-292]at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.29.jar:5.3.29]at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.29.jar:5.3.29]at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.79.jar:4.0.FR]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.29.jar:5.3.29]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.29.jar:5.3.29]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.79.jar:9.0.79]at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.79.jar:9.0.79]at java.lang.Thread.run(Thread.java:748) [na:1.8.0-292]

对比之后发现是通过 aop 实现的,默认 spring boot 用的是 cglib 进行字节码增强。

这样执行下来全局事务就不生效了,数据插入到数据库中,因为事务机制是通过扫描 @Transactional 修饰的方法生成对应的代理对象来实现的。

其中,代理类实现有两种方式,java 自带的动态代理和 cglib 代理。

修改 application.yml 如下

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_transaction?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=trueusername: rootpassword: 123456aop:proxy-target-class: falsemybatis:mapper-locations: classpath:mapper/*.xml

即,单独指定了 spring.aop.proxy-target-class=false,这样就不默认使用 cglib 生成代理类了,但是这样需要单独定义 interface 以及对应实现类。

对比发现,CglibAopProxy 变成了 JdkDynamicAopProxy。

 

总结

spring 事务注解 @Transactional 添加后,通过 aop 为当前方法创建代理对象,默认使用 cglib,也可以使用 java 的动态代理。

默认回滚异常为 RuntimeException 或者 Error 子类,如果抛出的异常非 RuntimeException 子类,需要单独指定 rollbackFor 为 Exception,否则会捕捉不到异常导致数据异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/1331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【GPTs分享】GPTs分享之 AskYourPDF Research Assistant​

一、简介 AskYourPDF Research Assistant 是一款高级人工智能研究助手&#xff0c;专门设计用于帮助用户高效从PDF文件和文章中提取信息。它结合了深度学习技术和自然语言处理能力&#xff0c;以便用户能够快速地查询、总结及处理文档内容&#xff0c;并能够生成与文章内容相关…

Redux入门:使用@reduxjs/toolkit构建React应用程序状态管理

随着应用程序复杂性的增加,有效管理应用程序状态变得越来越重要。Redux是一种流行的状态管理解决方案,随着应用程序复杂性的增加,有效管理应用程序状态变得越来越重要。Redux是一种流行的状态管理解决方案,但传统的Redux设置和使用过程比较繁琐。幸运的是,Redux官方团队推出了r…

Nginx防盗链:资源保护新利器,拒绝盗链行为!

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ Nginx防盗链是一种安全机制&#xff0c;用于防止其他网站直接链接到你的网站上的资源&#xff08;如图片、视频、文件等&#xff09;&#xff0c;从而避免不必要的带宽消耗…

P1278 单词游戏 简单搜索+玄学优化

单词游戏 传送门 题目描述 Io 和 Ao 在玩一个单词游戏。 他们轮流说出一个仅包含元音字母的单词&#xff0c;并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。 游戏可以从任何一个单词开始。 任何单词禁止说两遍&#xff0c;游戏中只能使用给定词典中含有…

Linux 操作系统指令和Vscdoe安装

1、Linux系统介绍 Linux系统的背景介绍我就不介绍了&#xff0c;有兴趣的可以去看看其发展史。 1.1 Linux操作系统的主要特点 Linux操作系统的重要思想&#xff1a;一切皆文件 Linux操作系统的特性&#xff1a; 完全免费 支持多平台 支持多用户、多任务 有良好的界面 完美兼容…

温湿度LCD显示并上传服务器

项目需求 通过温湿度传感器将值传到LCD1602&#xff0c;并实时通过蓝牙透传到手机。 硬件介绍 温湿度传感器 DHT11温湿度传感器 DHT11_温湿度传感器数据格式-CSDN博客 LCD1602LCD1602-CSDN博客 HC-01 继电器模块 硬件接线 LCD1602 D0~D7 --> A0~A7VDD, A --> 5v…

STM32H750外设ADC之双重 ADC 模式

目录 概述 1 双重 ADC 模式介绍 1.1 双重 ADC模式 1.2 双重 ADC 模式的类型 2 双重 ADC 模式寄存器的配置 3 模式功能实现 3.1 注入同步模式 3.2 支持独立注入的常规同步模式 3.2.1 中断的方式 3.2.2 DMA 读取常规数据 3.3 支持独立注入的交替模式 3.3.1 中断触发…

色彩的魔力:渐变色在设计中的无限可能性

夕阳&#xff0c;天空&#xff0c;湖面&#xff0c;夕阳...随着距离和光影的变化&#xff0c;颜色的渐变色&#xff0c;近大远小、近实远虚的透视&#xff0c;为大自然营造了浪漫的氛围。延伸到UI/UX设计领域&#xff0c;这种现实、惊艳、独特的渐变色也深受众多设计师的喜爱。…

setmapAVL树红黑树

目录 关联式容器树形结构的关联式容器setset的模板参数列表set的构造函数set的迭代器set的容量操作set其他操作 multisetmap键值对map的模板参数列表map的迭代器map中元素的修改map的容量与元素访问 multimap底层结构avl树avl树概念AVL树结点的定义AVL树的插入AVL树的旋转AVL树…

python-flask结合bootstrap实现网页小工具实例-半小时速通版

参考&#xff1a; Python之flask结合Bootstrap框架快速搭建Web应用_支持bootstrap的python软件-CSDN博客 https://blog.csdn.net/lovedingd/article/details/106696832 Bootstrap 警告框 | 菜鸟教程 https://www.runoob.com/bootstrap/bootstrap-alert-plugin.html flask框架…

数据结构——7.3 树形查找

7.3 树形查找 概念 二叉排序树&#xff08;BST&#xff09; 二叉排序树&#xff08;Binary Sort Tree&#xff0c;BST&#xff09;&#xff0c;又称为二叉查找&#xff08;搜索&#xff09;树&#xff08;Binary Search Tree&#xff09;&#xff0c;是一种特殊的二叉树&am…

力扣---从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]示…

使用Docker搭建Redis主从集群

文章目录 ☃️前言☃️搭建❄️❄️架构❄️❄️实例说明❄️❄️搭建第一个服务器上的两个实例❄️❄️搭建第二个服务器上的一个实例 ☃️开启主从❄️❄️改配置❄️❄️重启从节点 ☃️验证 ☃️前言 单节点 Redis 的并发能力是有上限的&#xff0c;要进一步提高Redis的并…

[大模型]TransNormerLLM-7B FastApi 部署调用

TransNormerLLM-7B FastApi 部署调用 1. TransNormer 介绍 TransNormerLLM 是一个基于线性注意力的 LLM&#xff0c;在准确性和效率方面均优于传统的基于 softmax 注意力的模型。它是在包含多达1.4 万亿个令牌的高质量语料库上进行训练的&#xff0c;TransNormerLLM 从之前的…

K-means和DBSCAN

目录 一、K-means和DBSCAN之间的主要区别 二、DBSCAN聚类算法 2.1DBSCAN聚类算法实现点集数据的聚类 2.2DBSCAN聚类算法实现鸢尾花数据集的聚类 三、K-means聚类算法 3.1K-means聚类算法实现随机数据的聚类 3.2K-means聚类算法实现鸢尾花数据集的聚类 一、K-means和DBSC…

014Node.js时间格式包silly-datetime安装与使用

下载&#xff1a; https://www.npmjs.com/网站上下载silly-datetime 安装 npm i silly-datetime --save var sd require(silly-datetime);console.log(new Date()); //2024-04-18T04:40:38.505Zvar dsd.format(new Date(), YYYY-MM-DD HH:mm);console.log(d); //2024…

idea在controller或者service使用ctrl+alt+b进入方法后,如何返回到 进入前的那一层

idea在controller或者service使用ctrlaltb进入方法后&#xff0c;如何返回到进入方法的最外层 解决方案使用 ctrlalt ← /→← /→ 键盘上的左右键盘

stm32中断发送接收数据

配置hal库 1配置时钟 2配置uart 3打开中断 程序结构 uart中断函数 中断接收和发送函数 HAL_UART_Receive_IT()&#xff1a;启动中断驱动的接收过程&#xff0c;当接收到指定数量的字节后会产生中断&#xff0c;并调用HAL_UART_RxCpltCallback()回调函数。 HAL_StatusTypeD…

STL分解

效果图 ref&#xff1a;时序数据分析的利器——STL分解 - 知乎 (zhihu.com)

2024化工制造企业数字化白皮书

来源&#xff1a;蓝凌研究院 中国石油和化学工业联合会发布2023年中国石油和化工行业经济运行情况。数据显示&#xff0c;2023年&#xff0c;我国石化行业实现营业收入15.95万亿元&#xff0c; 同比下降1.1%&#xff0c;利润总额8733.6亿元&#xff0c;行业经济运行总体呈现低…