【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

執行后  兩個數據庫里都沒有數據

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM