• SpringBoot设置事务管理


      关于事务就不介绍了,前面在研究spring的时候就已经研究过了,参考:https://www.cnblogs.com/qlqwjy/p/7296493.html

      这里直接研究springboot中事务的开启以及测试方法。

      在Spring Boot中推荐使用@Transactional注解来申明事务。

    首先需要导入依赖:

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>

      当引入jdbc依赖之后,Spring Boot会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager,所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用。

      

      在Service中添加@Transactional注解:

    ===================测试事务效果===========

    Service层代码:

    package cn.qlq.service.impl.user;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import cn.qlq.bean.user.User;import cn.qlq.mapper.user.UserMapper;
    import cn.qlq.service.user.UserService;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Override
        public void addUser(User user) {
            userMapper.insert(user);
            int i = 1 / 0;
        }
    
    }

    (1)测试未添加事务

      数据库原来记录:

    测试添加后查看日志:(插入数据之后报异常)

    2019-02-21 16:25:20.983  INFO 20960 --- [nio-8088-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
    2019-02-21 16:25:20.984  INFO 20960 --- [nio-8088-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
    2019-02-21 16:25:21.004  INFO 20960 --- [nio-8088-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 20 ms
    2019-02-21 16:25:21.047  INFO 20960 --- [nio-8088-exec-1] cn.qlq.action.UserController             : user -> User [id=null, username=2, password=2, userfullname=2, createtime=Thu Feb 21 16:25:21 CST 2019, isdeleted=null, sex=女, address=2]
    2019-02-21 16:25:21.320 DEBUG 20960 --- [nio-8088-exec-1] cn.qlq.mapper.user.UserMapper.insert     : ==>  Preparing: insert into user (id, username, password, userfullname, createtime, isdeleted, sex, address) values (?, ?, ?, ?, ?, ?, ?, ?) 
    2019-02-21 16:25:21.335 DEBUG 20960 --- [nio-8088-exec-1] cn.qlq.mapper.user.UserMapper.insert     : ==> Parameters: null, 2(String), 2(String), 2(String), 2019-02-21(Date), null, 女(String), 2(String)
    2019-02-21 16:25:21.674 DEBUG 20960 --- [nio-8088-exec-1] cn.qlq.mapper.user.UserMapper.insert     : <==    Updates: 1
    java.lang.ArithmeticException: / by zero
        at cn.qlq.service.impl.user.UserServiceImpl.addUser(UserServiceImpl.java:27)
        at cn.qlq.action.UserController.addUser(UserController.java:31)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:798)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
    2019-02-21 16:25:21.763  WARN 20960 --- [nio-8088-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: java.lang.ArithmeticException: / by zero

     查看数据库:(数据增加,没有事务回滚)

    (2)测试添加事务

    只需在类加上@Transactional注解就可以了。

    测试查看日志同上面一样,但是事务进行回滚了,只是没有打出日志。(下次提交发现id会跳过本次提交的值)

    查看数据库记录:

    补充:原理理解

      在再次使用事务的时候发现其回滚没有打印日志,于是就想的研究一下原理。

      其自动配置是在springboot-autoconfig-xxx.jar中。入口类是:DataSourceTransactionManagerAutoConfiguration

    (1)查看DataSourceTransactionManagerAutoConfiguration源码:

    /** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. */
    package org.springframework.boot.autoconfigure.jdbc;
    
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.boot.autoconfigure.AutoConfigureOrder;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
    import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    
    /**
     * {@link EnableAutoConfiguration Auto-configuration} for
     * {@link DataSourceTransactionManager}.
     *
     * @author Dave Syer
     * @author Stephane Nicoll
     * @author Andy Wilkinson
     * @author Kazuki Shimizu
     * @since 1.0.0
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
    @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
    @EnableConfigurationProperties(DataSourceProperties.class)
    public class DataSourceTransactionManagerAutoConfiguration {
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnSingleCandidate(DataSource.class)
        static class DataSourceTransactionManagerConfiguration {
    
            @Bean
            @ConditionalOnMissingBean(PlatformTransactionManager.class)
            DataSourceTransactionManager transactionManager(DataSource dataSource,
                    ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
                DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
                transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
                return transactionManager;
            }
    
        }
    
    }

    ConditionalOnClass表示在当前类路径下存在对应的类。

    第一步:去掉上面的pom配置,并且去掉JPA等配置,发现不存在PlatformTransactionManager类。(没有这个class类,但是没报错是因为是class文件不会再次编译)

    如果需要我们自己写判断可以用name判断,值是包全路径。源码如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnClassCondition.class)
    public @interface ConditionalOnClass {
    
        Class<?>[] value() default {};
    
        String[] name() default {};
    
    }

    第二部:加上上面的pom配置发现在当前工程的classpath环境中可以找到上面类。

      也可以在反编译的class文件中加断点测试,如果类路径有上面两个类会进入断点创建transactionManager对象;如果不存在不会进入断点。

    在我们需要设置自己的事务管理器的时候可以重新返回一个PlatformTransactionManager对象,这些经常在使用动态数据源的时候需要设置自己的事务管理器,如下:

        @Bean
        public PlatformTransactionManager transactionManager() {
            // 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
            return new DataSourceTransactionManager(dynamicDataSource());
        }

     (2)

  • 相关阅读:
    init_machine 在Kernel中被调用的过程
    maven-surefire-plugin的forkMode分析
    执行Maven install或Maven test命令时控制台输出乱码的解决办法
    Maven 中的dependencies与dependencyManagement的区别
    使用maven profile实现多环境可移植构建
    Maven最佳实践:划分模块
    MySQL的登陆错误:ERROR 1049 (42000): Unknown database 'root'
    Java 连接操作 Redis 出现错误
    关闭多个screen
    linux sed 批量替换字符串
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/8547999.html
Copyright © 2020-2023  润新知