• 【Spring Boot学习之四】Spring Boot事务管理


    环境
      eclipse 4.7
      jdk 1.8
      Spring Boot 1.5.2

    一、springboot整合事务
    事务分类:编程事务、声明事务(XML、注解),推荐使用注解方式,springboot默认集成事物,只主要在方法上加上@Transactional即可
    1、controller

    package com.wjy.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.wjy.test1.service.UserServiceTest1;
    
    
    @RestController
    public class UserController {
    
        @Autowired
        public UserServiceTest1 userServiceTest1;
        
        @RequestMapping("/insertTest1ByService")
        public String insertTest1ByService(String name,Integer age) {
            userServiceTest1.insertuser1(name, age);
            return "success";
        }
        
        
    }

    2、service

    /**
     * 
     */
    package com.wjy.test1.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.wjy.test1.dao.UserMapperTest1;
    
    /**
     * @Desc
     * @author wangjy15
     */
    @Service
    public class UserServiceTest1 {
        
        @Autowired
        private UserMapperTest1 userMapperTest1;
        
        /**
         * @Description: 如果没有事务控制 那么报错之后  插入到库里的数据不会回滚  加上 注解@Transactional  就可以回滚
         */
        @Transactional
        public String insertuser1(String name,Integer age) {
            userMapperTest1.insert(name, age);
            int i =1/0;
            return "success";
        }
    
    }

    3、mapper

    /**
     * 
     */
    package com.wjy.test1.dao;
    
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    
    import com.wjy.entity.User;
    
    /**
     * @Desc
     * @author wangjy15
     */
    public interface UserMapperTest1 {
        
        @Select("SELECT * FROM users WHERE NAME = #{name}")
        User findByName(@Param("name") String name);
    
        @Insert("insert into users (name,age) values(#{name},#{age})")
        int insert(@Param("name") String name,@Param("age") Integer age);
    }

    4、APP

    package com.wjy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class APP {
    
        public static void main(String[] args) {
            SpringApplication.run(APP.class, args);
        }
    
    }

    5、测试验证

    http://localhost:8080/insertTest1ByService?name=wangsan0010&age=1000

    二、SpringBoot分布式事务管理

    使用springboot+jta+atomikos分布式事务管理,service层有事务控制,dao层没有,一般情况下都需要调用其他数据源的dao层,这就需要进行分布式事务管理。
    将多个数据源注册到atomikos进行管理,进行2PC(Two-phaseCommit)二阶段提交。

    理解一下分布式事务:

    对于上面一中示例做一下修改,再引入一个数据源test2,修改一下service:

    /**
     * 
     */
    package com.wjy.test1.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.wjy.test1.dao.UserMapperTest1;
    import com.wjy.test2.dao.UserMapperTest2;
    import com.wjy.test2.service.UserServiceTest2;
    
    /**
     * @Desc
     * @author wangjy15
     */
    @Service
    public class UserServiceTest1 {
        
        @Autowired
        private UserMapperTest1 userMapperTest1;
        
        @Autowired
        private UserMapperTest2 userMapperTest2;
        
        @Autowired
        private UserServiceTest2 userServiceTest1;
        
        /**
         * @Desc: 如果没有事务控制 那么报错之后  插入到库里的数据不会回滚  加上 注解@Transactional  就可以回滚
         */
        @Transactional
        public String insertuser1(String name,Integer age) {
            userMapperTest1.insert(name, age);
            userServiceTest1.insertuser2(name, age);//有事务控制  可以回滚        
            int i =1/0;
            return "success";
        }
    
    }
    /**
     * 
     */
    package com.wjy.test2.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.wjy.test2.dao.UserMapperTest2;
    
    /**
     * @Desc
     * @author wangjy15
     */
    public class UserServiceTest2 {
        @Autowired
        private UserMapperTest2 userMapperTest2;
        
        /**
         * @Desc: 如果没有事务控制 那么报错之后  插入到库里的数据不会回滚  加上 注解@Transactional  就可以回滚
         */
        @Transactional
        public String insertuser2(String name,Integer age) {
            userMapperTest2.insert(name, age);
            return "success";
        }
    
    }

    这时test1数据库里没有插入数据,test2数据库也没有插入数据库,因为test2在service层也有事务控制。

    再修改一下test1 service:

    /**
     * 
     */
    package com.wjy.test1.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.wjy.test1.dao.UserMapperTest1;
    import com.wjy.test2.dao.UserMapperTest2;
    import com.wjy.test2.service.UserServiceTest2;
    
    /**
     * @Desc
     * @author wangjy15
     */
    @Service
    public class UserServiceTest1 {
        
        @Autowired
        private UserMapperTest1 userMapperTest1;
        
        @Autowired
        private UserMapperTest2 userMapperTest2;
        /**
         * @Desc: 如果没有事务控制 那么报错之后  插入到库里的数据不会回滚  加上 注解@Transactional  就可以回滚
         */
        @Transactional
        public String insertuser1(String name,Integer age) {
            userMapperTest1.insert(name, age);
            userMapperTest2.insert(name, age);//没有事务控制 不可以回滚
            
            int i =1/0;
            return "success";
        }
    
    }

    这时test1数据库里没有插入数据,test2数据库有数据插入到数据库  没有回滚,因为test2在mapper层也没有事务控制。

    下面引入atomikos  和springboot整合:

    1、数据源配置信息

    引入依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jta-atomikos</artifactId>
    </dependency>
    # Mysql 1
    mysql.datasource.test1.url = jdbc:mysql://192.168.118.102:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8
    mysql.datasource.test1.username = root
    mysql.datasource.test1.password = 123456
    
    mysql.datasource.test1.minPoolSize = 3
    mysql.datasource.test1.maxPoolSize = 25
    mysql.datasource.test1.maxLifetime = 20000
    mysql.datasource.test1.borrowConnectionTimeout = 30
    mysql.datasource.test1.loginTimeout = 30
    mysql.datasource.test1.maintenanceInterval = 60
    mysql.datasource.test1.maxIdleTime = 60
    mysql.datasource.test1.testQuery = select 1
    
    
    # Mysql 2
    mysql.datasource.test2.url =jdbc:mysql://192.168.118.102:3306/springboot2?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8
    mysql.datasource.test2.username =root
    mysql.datasource.test2.password =123456
    
    mysql.datasource.test2.minPoolSize = 3
    mysql.datasource.test2.maxPoolSize = 25
    mysql.datasource.test2.maxLifetime = 20000
    mysql.datasource.test2.borrowConnectionTimeout = 30
    mysql.datasource.test2.loginTimeout = 30
    mysql.datasource.test2.maintenanceInterval = 60
    mysql.datasource.test2.maxIdleTime = 60
    mysql.datasource.test2.testQuery = select 1

    2、数据源配置类

    package com.wjy.datasource;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix="mysql.datasource.test1")
    public class DBConfig1 {
        
        private String url;
        private String username;
        private String password;
        private int minPoolSize;
        private int maxPoolSize;
        private int maxLifetime;
        private int borrowConnectionTimeout;
        private int loginTimeout;
        private int maintenanceInterval;
        private int maxIdleTime;
        private String testQuery;
        public String getUrl() {
            return url;
        }
        public void setUrl(String url) {
            this.url = url;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public int getMinPoolSize() {
            return minPoolSize;
        }
        public void setMinPoolSize(int minPoolSize) {
            this.minPoolSize = minPoolSize;
        }
        public int getMaxPoolSize() {
            return maxPoolSize;
        }
        public void setMaxPoolSize(int maxPoolSize) {
            this.maxPoolSize = maxPoolSize;
        }
        public int getMaxLifetime() {
            return maxLifetime;
        }
        public void setMaxLifetime(int maxLifetime) {
            this.maxLifetime = maxLifetime;
        }
        public int getBorrowConnectionTimeout() {
            return borrowConnectionTimeout;
        }
        public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
            this.borrowConnectionTimeout = borrowConnectionTimeout;
        }
        public int getLoginTimeout() {
            return loginTimeout;
        }
        public void setLoginTimeout(int loginTimeout) {
            this.loginTimeout = loginTimeout;
        }
        public int getMaintenanceInterval() {
            return maintenanceInterval;
        }
        public void setMaintenanceInterval(int maintenanceInterval) {
            this.maintenanceInterval = maintenanceInterval;
        }
        public int getMaxIdleTime() {
            return maxIdleTime;
        }
        public void setMaxIdleTime(int maxIdleTime) {
            this.maxIdleTime = maxIdleTime;
        }
        public String getTestQuery() {
            return testQuery;
        }
        public void setTestQuery(String testQuery) {
            this.testQuery = testQuery;
        }
    
        
    }
    package com.wjy.datasource;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix="mysql.datasource.test2")
    public class DBConfig2 {
        private String url;
        private String username;
        private String password;
        private int minPoolSize;
        private int maxPoolSize;
        private int maxLifetime;
        private int borrowConnectionTimeout;
        private int loginTimeout;
        private int maintenanceInterval;
        private int maxIdleTime;
        private String testQuery;
        
        public String getUrl() {
            return url;
        }
        public void setUrl(String url) {
            this.url = url;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public int getMinPoolSize() {
            return minPoolSize;
        }
        public void setMinPoolSize(int minPoolSize) {
            this.minPoolSize = minPoolSize;
        }
        public int getMaxPoolSize() {
            return maxPoolSize;
        }
        public void setMaxPoolSize(int maxPoolSize) {
            this.maxPoolSize = maxPoolSize;
        }
        public int getMaxLifetime() {
            return maxLifetime;
        }
        public void setMaxLifetime(int maxLifetime) {
            this.maxLifetime = maxLifetime;
        }
        public int getBorrowConnectionTimeout() {
            return borrowConnectionTimeout;
        }
        public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
            this.borrowConnectionTimeout = borrowConnectionTimeout;
        }
        public int getLoginTimeout() {
            return loginTimeout;
        }
        public void setLoginTimeout(int loginTimeout) {
            this.loginTimeout = loginTimeout;
        }
        public int getMaintenanceInterval() {
            return maintenanceInterval;
        }
        public void setMaintenanceInterval(int maintenanceInterval) {
            this.maintenanceInterval = maintenanceInterval;
        }
        public int getMaxIdleTime() {
            return maxIdleTime;
        }
        public void setMaxIdleTime(int maxIdleTime) {
            this.maxIdleTime = maxIdleTime;
        }
        public String getTestQuery() {
            return testQuery;
        }
        public void setTestQuery(String testQuery) {
            this.testQuery = testQuery;
        }
        
    
    }
    package com.wjy.datasource;
    
    import java.sql.SQLException;
    
    import javax.sql.DataSource;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    import com.atomikos.jdbc.AtomikosDataSourceBean;
    import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
    
    ////注册到springboot容器中
    @Configuration  
    ////@MapperScan可以指定要扫描的Mapper类的包的路径  sqlSessionFactoryRef 表示定义了 key ,表示一个唯一 SqlSessionFactory 实例
    @MapperScan(basePackages="com.wjy.test1",sqlSessionFactoryRef="sqlSessionFactory1")
    public class TestMyBatisConfig1 {
    
        @Bean(name="dataSource1")
        @Primary
        public DataSource dataSource1(DBConfig1 dbConfig1) throws SQLException {
            MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
            mysqlXaDataSource.setUrl(dbConfig1.getUrl());
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
            mysqlXaDataSource.setPassword(dbConfig1.getPassword());
            mysqlXaDataSource.setUser(dbConfig1.getUsername());
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
    
            AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
            xaDataSource.setXaDataSource(mysqlXaDataSource);
            xaDataSource.setUniqueResourceName("dataSource1");
    
            xaDataSource.setMinPoolSize(dbConfig1.getMinPoolSize());
            xaDataSource.setMaxPoolSize(dbConfig1.getMaxPoolSize());
            xaDataSource.setMaxLifetime(dbConfig1.getMaxLifetime());
            xaDataSource.setBorrowConnectionTimeout(dbConfig1.getBorrowConnectionTimeout());
            xaDataSource.setLoginTimeout(dbConfig1.getLoginTimeout());
            xaDataSource.setMaintenanceInterval(dbConfig1.getMaintenanceInterval());
            xaDataSource.setMaxIdleTime(dbConfig1.getMaxIdleTime());
            xaDataSource.setTestQuery(dbConfig1.getTestQuery());
            return xaDataSource;
        }
        
        //注意 这里没有事务管理类 因为事务全部交给AtomikosDataSourceBean来管理
        
        @Primary
        @Bean(name = "sqlSessionFactory1")
        public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource dataSource)
                throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            return bean.getObject();
        }
    
        @Primary
        @Bean(name = "sqlSessionTemplate1")
        public SqlSessionTemplate sqlSessionTemplate1(
                @Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
        
    }
    package com.wjy.datasource;
    
    import java.sql.SQLException;
    
    import javax.sql.DataSource;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import com.atomikos.jdbc.AtomikosDataSourceBean;
    import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
    
    @Configuration  
    @MapperScan(basePackages="com.wjy.test2",sqlSessionFactoryRef="sqlSessionFactory2")
    public class TestMyBatisConfig2 {
    
        @Bean(name="dataSource2")
        public DataSource dataSource2(DBConfig2 dbConfig2) throws SQLException {
            MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
            mysqlXaDataSource.setUrl(dbConfig2.getUrl());
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
            mysqlXaDataSource.setPassword(dbConfig2.getPassword());
            mysqlXaDataSource.setUser(dbConfig2.getUsername());
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
    
            AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
            xaDataSource.setXaDataSource(mysqlXaDataSource);
            xaDataSource.setUniqueResourceName("dataSource2");
    
            xaDataSource.setMinPoolSize(dbConfig2.getMinPoolSize());
            xaDataSource.setMaxPoolSize(dbConfig2.getMaxPoolSize());
            xaDataSource.setMaxLifetime(dbConfig2.getMaxLifetime());
            xaDataSource.setBorrowConnectionTimeout(dbConfig2.getBorrowConnectionTimeout());
            xaDataSource.setLoginTimeout(dbConfig2.getLoginTimeout());
            xaDataSource.setMaintenanceInterval(dbConfig2.getMaintenanceInterval());
            xaDataSource.setMaxIdleTime(dbConfig2.getMaxIdleTime());
            xaDataSource.setTestQuery(dbConfig2.getTestQuery());
            return xaDataSource;
        }
        
        //注意 这里没有事务管理类 因为事务全部交给AtomikosDataSourceBean来管理
        
        @Bean(name = "sqlSessionFactory2")
        public SqlSessionFactory sqlSessionFactory2(@Qualifier("dataSource2") DataSource dataSource)
                throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            return bean.getObject();
        }
    
        @Bean(name = "sqlSessionTemplate2")
        public SqlSessionTemplate sqlSessionTemplate2(
                @Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }

    3、controller

    package com.wjy.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.wjy.test1.service.UserServiceTest1;
    
    @RestController
    public class UserController {
        
        @Autowired
        public UserServiceTest1 userServiceTest1;
        
        @RequestMapping("/insertTest1ByService")
        public String insertTest1ByService(String name,Integer age) {
            userServiceTest1.insertuser1(name, age);
            return "success";
        }
        
        
    }

    4、service

    /**
     * 
     */
    package com.wjy.test1.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.wjy.test1.dao.UserMapperTest1;
    import com.wjy.test2.dao.UserMapperTest2;
    
    /**
     * @Desc
     * @author wangjy15
     */
    @Service
    public class UserServiceTest1 {
        
        @Autowired
        private UserMapperTest1 userMapperTest1;
        
        @Autowired
        private UserMapperTest2 userMapperTest2;
        
        
        /**
         * @Desc: 如果没有事务控制 那么报错之后  插入到库里的数据不会回滚  加上 注解@Transactional  就可以回滚
         */
        @Transactional
        public String insertuser1(String name,Integer age) {
            userMapperTest1.insert(name, age);
            userMapperTest2.insert(name, age);//没有事务控制 不可以回滚
            
            int i =1/0;
            return "success";
        }
    
    }

    5、APP

    package com.wjy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    
    import com.wjy.datasource.DBConfig1;
    import com.wjy.datasource.DBConfig2;
    
    @SpringBootApplication
    @EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })  //启动的时候读取两个类对应的配置信息
    public class APP {
    
        public static void main(String[] args) {
            SpringApplication.run(APP.class, args);
        }
    
    }

    6、测试验证

    http://localhost:8080/insertTest1ByService?name=wangsan&age=10

    执行后  两个数据库里都没有数据

  • 相关阅读:
    2019 Multi-University Training Contest 10
    自考新教材-p326_3(1)
    自考新教材-p322
    自考新教材-p321
    自考新教材-p316
    自考新教材-p315
    自考新教材-p313
    自考新教材-p311
    自考新教材-p310
    自考新教材-p309
  • 原文地址:https://www.cnblogs.com/cac2020/p/11230967.html
Copyright © 2020-2023  润新知