SpringBoot學習小結之多數據源

SpringBoot學習小結之多數據源

本文針對在Springboot使用多數據源的情況下, 滿足分佈式事務進行總結

一、多數據源

1. 數據庫

使用mysql數據庫,這裏有分別位於兩個數據庫的兩張表student.student, teacher.teacher, SQL語句如下

create database student;
use student;
-- ----------------------------
-- Table structure for student
-- ----------------------------
CREATE TABLE `student` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

create database teacher;
use teacher;
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
CREATE TABLE `teacher` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2. 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.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.aabond</groupId>
    <artifactId>mutidatasource</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mutidataresource</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.application.yml

spring:
  datasource:
    student:
      url: jdbc:mysql://localhost:3306/student?useUnicode=true&characterEncoding=utf-8&useSSL=false
      user: root
      password: 
      driver-class-name: com.mysql.jdbc.Driver
    teacher:
      url: jdbc:mysql://localhost:3306/teacher?useUnicode=true&characterEncoding=utf-8&useSSL=false
      user: root
      password: 
      driver-class-name: com.mysql.jdbc.Driver

4. 實體類

package com.aabond.mutidatasource.enity;

/**
 * @className: Student
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 19:49
 * @version: v1.0.0
 **/
public class Student {

    private Integer id;
    private String name;
	// 省略get set toString方法
}

package com.aabond.mutidatasource.enity;

/**
 * @className: Teacher
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 19:50
 * @version: v1.0.0
 **/
public class Teacher {

    private Integer id;
    private String name;
	// 省略get set toString方法
}

5.dao層

需要dao目錄下建兩個包,分別存儲兩個數據庫的dao文件

package com.aabond.mutidatasource.dao.student;

import com.aabond.mutidatasource.enity.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @className: StudentDao
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 19:51
 * @version: v1.0.0
 **/
@Repository
public interface StudentDao {
    @Delete("delete from student where id = #{id}")
    void deleteById(Integer id);
    @Insert("insert into student (`id`, `name`) values (#{id}, #{name})")
    void insert(Student student);
    @Select("select id, name from student")
    List<Student> findAll();
}
package com.aabond.mutidatasource.dao.teacher;

import com.aabond.mutidatasource.enity.Teacher;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @className: TeacherDao
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 19:52
 * @version: v1.0.0
 **/
@Repository
public interface TeacherDao {

    @Delete("delete from teacher where id = #{id}")
    void deleteById(Integer id);
    @Insert("insert into teacher (`id`, `name`) values (#{id}, #{name})")
    void insert(Teacher teacher);
    @Select("select id, name from teacher")
    List<Teacher> findAll();
}

6.Service層

package com.aabond.mutidatasource.service;

import com.aabond.mutidatasource.enity.Student;
import com.aabond.mutidatasource.enity.Teacher;

/**
 * @className: TestService
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 19:53
 * @version: v1.0.0
 **/
public interface TestService {

    void normalDelete(Integer id);
    void exceptionDelete(Integer id) throws Exception;

    void normalInsert(Student student, Teacher teacher);
    void exceptionInsert(Student student, Teacher teacher) throws Exception;
}

package com.aabond.mutidatasource.service.impl;

import com.aabond.mutidatasource.dao.student.StudentDao;
import com.aabond.mutidatasource.dao.teacher.TeacherDao;
import com.aabond.mutidatasource.enity.Student;
import com.aabond.mutidatasource.enity.Teacher;
import com.aabond.mutidatasource.service.TestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @className: TestServiceImpl
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 19:56
 * @version: v1.0.0
 **/
@Service
public class TestServiceImpl implements TestService {

    private static final Logger logger = LoggerFactory.getLogger(TestServiceImpl.class);

    @Autowired
    private StudentDao studentDao;
    @Autowired
    private TeacherDao teacherDao;

    @Override
    public void normalDelete(Integer id) {
        studentDao.deleteById(id);
        teacherDao.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exceptionDelete(Integer id) throws Exception {
        studentDao.deleteById(id);
        teacherDao.deleteById(id);
        int i = 1 / 0;
    }

    @Override
    public void normalInsert(Student student, Teacher teacher) {
        studentDao.insert(student);
        teacherDao.insert(teacher);
    }

    @Override
    public void exceptionInsert(Student student, Teacher teacher)  {
        normalInsert(student, teacher);
        int i = 1 / 0;
    }
}

7. Config配置類

package com.aabond.mutidatasource.config;

import com.atomikos.jdbc.AtomikosDataSourceBean;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;

/**
 * @className: StudentDataSourceConfig
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 19:48
 * @version: v1.0.0
 **/
@Configuration
@MapperScan(basePackages = "com.aabond.mutidatasource.dao.student", sqlSessionTemplateRef = "studentSqlSessionTemplate")
public class StudentDataSourceConfig {
    private static final Logger logger = LoggerFactory.getLogger(StudentDataSourceConfig.class);

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.student")
    public MysqlXADataSource studentXADataSource() {
        return new MysqlXADataSource();
    }

    @Bean
    public DataSource studentDataSource(@Qualifier("studentXADataSource") MysqlXADataSource druidXADataSource) {
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(druidXADataSource);
        xaDataSource.setUniqueResourceName("studentDataSource");
        return xaDataSource;
    }

    @Bean
    public SqlSessionFactory studentSqlSessionFactory(@Qualifier("studentDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
//        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/student/*Mapper.xml"));//掃描指定目錄的xml
        return bean.getObject();
    }

    @Bean
    public SqlSessionTemplate studentSqlSessionTemplate(@Qualifier("studentSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
package com.aabond.mutidatasource.config;

import com.atomikos.jdbc.AtomikosDataSourceBean;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * @className: TeacherDataSourceConfig
 * @description: TODO
 * @author: root
 * @date: 2019/12/10 20:01
 * @version: v1.0.0
 **/
@Configuration
@MapperScan(basePackages = "com.aabond.mutidatasource.dao.teacher", sqlSessionTemplateRef = "teacherSqlSessionTemplate")
public class TeacherDataSourceConfig {
    private static final Logger logger = LoggerFactory.getLogger(TeacherDataSourceConfig.class);

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.teacher")
    public MysqlXADataSource teacherXADataSource() {
        return new MysqlXADataSource();
    }

    @Bean
    public DataSource teacherDataSource(@Qualifier("teacherXADataSource") MysqlXADataSource druidXADataSource) {
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(druidXADataSource);
        xaDataSource.setUniqueResourceName("teacherDataSource");
        return xaDataSource;
    }

    @Bean
    public SqlSessionFactory teacherSqlSessionFactory(@Qualifier("teacherDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
//        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/teacher/*Mapper.xml"));//掃描指定目錄的xml
        return bean.getObject();
    }

    @Bean
    public SqlSessionTemplate teacherSqlSessionTemplate(@Qualifier("teacherSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

8.測試類

包含兩個測試方法,testNormal測試了兩個數據源的方法可以正常插入、刪除,testTransaction測試了發生異常情況下,兩個數據源事務能否正常運行

package com.aabond.mutidatasource;

import com.aabond.mutidatasource.dao.student.StudentDao;
import com.aabond.mutidatasource.dao.teacher.TeacherDao;
import com.aabond.mutidatasource.enity.Student;
import com.aabond.mutidatasource.enity.Teacher;
import com.aabond.mutidatasource.service.TestService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MutidataresourceApplicationTests {

    private static final Logger logger = LoggerFactory.getLogger(MutidataresourceApplicationTests.class);


    @Autowired
    private TestService testService;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private TeacherDao teacherDao;

    @Test
    public void testTransaction()  {

        testService.normalDelete(100);

        Student student = new Student();
        student.setId(100);
        student.setName("studnet100");

        Teacher teacher = new Teacher();
        teacher.setId(100);
        teacher.setName("teacher100");

        testService.normalInsert(student, teacher);
        print();
        try {
            testService.exceptionDelete(100);
        } catch (Exception e) {
            logger.info("testTransaction發生異常,{}", e);
        }
        print();
        
        Assert.assertNotNull(studentDao.findAll());
        Assert.assertNotNull(teacherDao.findAll());

        testService.normalDelete(100);

    }

    public void print() {
        List<Student> students = studentDao.findAll();
        List<Teacher> teachers = teacherDao.findAll();
        logger.info("{},{}", students, teachers);
    }


    @Test
    public void testNormal() {


        Student student = new Student();
        student.setId(100);
        student.setName("studnet100");

        Teacher teacher = new Teacher();
        teacher.setId(100);
        teacher.setName("teacher100");

        testService.normalInsert(student, teacher);

        print();

        Assert.assertNotNull(studentDao.findAll());
        Assert.assertNotNull(teacherDao.findAll());

        testService.normalDelete(100);
    }

}

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