springboot多數據源整合分佈式事務

如何解決多數據源分包分佈式事務

  • 多數據源分佈式事務問題和真領域中產生的分佈式事務問題是不一樣的。
  • 多數據源分佈式事務的問題產生在同一個項目中,有多個不同的數據庫連接。
  • 分佈式領域中的事務因爲系統的拆分,每個服務都有自己獨立的數據庫。

多數據源項目中如何解決分佈式事務問題

  • 使用springboot+jta+atomikos分佈式事務管理:Atomikos 是一個爲Java平臺提供增值服務的並且開源類事務管理器。

分佈式領域中的如何解決分佈式事務問題

  • TX-LCN分佈式事務框架
  • TCC分佈式事務

  • 基於MQ解決分佈式事務

多數據源怎麼解決分佈式事務?

pom.xml引入

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- 測試 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- mysql 依賴 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- springboot-web組件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--jta-atomikos分佈式事務引入-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>
  <!--lombok引入,讓代碼更簡潔,不用寫GetSet-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>
    </dependencies>

application.yml配置文件信息

###springboot2.0名稱url在多數據源的情況下變成jdbc-url,否則出現debug
###準備兩個數據庫連接:mumber/order
spring:
  datasource:
    ##會員數據庫連接
    member:
      url: jdbc:mysql://localhost:3306/user
      username: root
      password: 123123
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      minPoolSize: 3
      maxPoolSize: 25
      maxLifetime: 20000
      uniqueResourceName: memberDatasource
      ##訂單數據庫連接
    order:
      url: jdbc:mysql://localhost:3306/order
      username: root
      password: 123123
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      minPoolSize: 3
      maxPoolSize: 25
      maxLifetime: 20000
      uniqueResourceName: orderDatasource

創建兩個配置文件MemberConfig.java

package com.xyt.springboot.config.member;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "spring.datasource.member")
public class MemberConfig {
    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;
    private String uniqueResourceName;

}

OrderConfig.java

package com.xyt.springboot.config.order;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "spring.datasource.order")//記得在啓動項目的時候開啓@EnableConfigurationProperties({MemberConfig.class, OrderConfig.class})
public class OrderConfig {
    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;
    private String uniqueResourceName;
}

spring容器注入配置文件代碼

MemberDatasourceConfig.java

package com.xyt.springboot.config.member;

import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
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.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;


@Configuration
@MapperScan(basePackages = "com.xyt.springboot.member.mapper", sqlSessionFactoryRef = "memberSqlSessionFactory")
public class MemberDatasourceConfig {
    /**
     * 創建我們的datasource
     *
     * @return
     */
    @Bean("memberDataSource")//spring容器注入
    public DataSource memberDataSource(MemberConfig memberConfig) {
        //1.創建xaDatasource
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(memberConfig.getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(memberConfig.getPassword());
        mysqlXaDataSource.setUser(memberConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        //註冊到全局事務上
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName(memberConfig.getUniqueResourceName());
        xaDataSource.setMinPoolSize(memberConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(memberConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(memberConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(memberConfig.getBorrowConnectionTimeout());
        try {
            xaDataSource.setLoginTimeout(memberConfig.getLoginTimeout());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        xaDataSource.setMaintenanceInterval(memberConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(memberConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(memberConfig.getTestQuery());
        return xaDataSource;
    }

    @Bean(name = "memberSqlSessionFactory")
    public SqlSessionFactory memberSqlSessionFactory(@Qualifier("memberDataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);//注入Datasource
        // bean.setMapperLocations(
        // new
        // PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test2/*.xml"));
        return bean.getObject();
    }

//    /**
//     * 事務管理
//     *
//     * @param dataSource * @return
//     */
//    @Primary
//    @Bean(name = "memberTransactionManager")
//    public DataSourceTransactionManager memberTransactionManager(@Qualifier("memberDataSource") DataSource dataSource) {
//        return new DataSourceTransactionManager(dataSource);
//    }

    @Bean(name = "memberSqlSessionTemplate")
    public SqlSessionTemplate memberSqlSessionTemplate(
            @Qualifier("memberSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

OrderDatasourceConfig.java

package com.xyt.springboot.config.order;

import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
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.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration//@Configuration等同於OrderDatasourceConfig.xml
@MapperScan(basePackages = "com.xyt.springboot.order.mapper", sqlSessionFactoryRef = "orderSqlSessionFactory")//com.xyt.springboot.order.mapper是mapper路徑
public class OrderDatasourceConfig {

    /**
     * 創建我們的datasource
     *
     * @return
     */
    @Bean("orderDataSource")//spring容器注入
    public DataSource orderDataSource(OrderConfig orderConfig) {
        //1.創建xaDatasource
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(orderConfig.getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(orderConfig.getPassword());
        mysqlXaDataSource.setUser(orderConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        //註冊到全局事務上
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName(orderConfig.getUniqueResourceName());
        xaDataSource.setMinPoolSize(orderConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(orderConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(orderConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(orderConfig.getBorrowConnectionTimeout());
        try {
            xaDataSource.setLoginTimeout(orderConfig.getLoginTimeout());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        xaDataSource.setMaintenanceInterval(orderConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(orderConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(orderConfig.getTestQuery());
        return xaDataSource;
    }

    @Bean(name = "orderSqlSessionFactory")
    public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // bean.setMapperLocations(
        // new
        // PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test2/*.xml"));
        return bean.getObject();
    }

//    /**
//     * 事務管理
//     *
//     * @param dataSource
//     * @return
//     */
//    @Bean(name = "orderTransactionManager")
//    public DataSourceTransactionManager orderTransactionManager(@Qualifier("orderDataSource") DataSource dataSource) {
//        return new DataSourceTransactionManager(dataSource);
//    }

    @Bean(name = "orderSqlSessionTemplate")
    public SqlSessionTemplate orderSqlSessionTemplate(
            @Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

服務層代碼Service

package com.xyt.springboot.service;

import com.xyt.springboot.member.mapper.UserMapper;
import com.xyt.springboot.order.mapper.OrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private OrderMapper orderMapper;

    /**
     * 調用User數據庫
     *
     * @param name
     * @param age
     * @return
     * @Transactional事務底層採用Aop技術 增強
     */
    @Transactional//開啓事務
    public int addUser(String name, int age) {
        //操作會員數據庫
        int i = userMapper.addUser(name, age);
        //操作訂單數據庫
        int b = orderMapper.addOrder(name);
        int j = 1 / age;
        return i;
    }

    /**
     * 調用Order數據庫
     *
     * @param order_no
     * @return
     */
    public int addOrder(String order_no) {
        return orderMapper.addOrder(order_no);
    }
}

mapper代碼

UserMaaper.java

package com.xyt.springboot.member.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

public interface UserMapper {
	@Insert("insert into users values(null,#{name},#{age});")
	public int addUser(@Param("name") String name, @Param("age") Integer age);
}

OrderMapper.java

package com.xyt.springboot.order.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

public interface OrderMapper {
    @Insert("insert into orders values(null,#{order_no});")
    public int addOrder(@Param("order_no") String order_no);
}

項目結構

啓動代碼

package com.xyt.springboot;

import com.xyt.springboot.config.member.MemberConfig;
import com.xyt.springboot.config.member.MemberDatasourceConfig;
import com.xyt.springboot.config.order.OrderConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@MapperScan("com.xyt.springboot.mapper")
@EnableConfigurationProperties({MemberConfig.class, OrderConfig.class})//開啓讓@ConfigurationProperties生效
public class AppSpringBootMybatis {
    public static void main(String[] args) {
        SpringApplication.run(AppSpringBootMybatis.class, args);
    }
}

 

發佈了31 篇原創文章 · 獲贊 1 · 訪問量 6529
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章