springboot配置双数据源(Mybatis)

背景介绍

最近的项目需求,需要将两个现有项目进行整合,原因是跨系统访问数据库,使用接口稍显复杂,而且还得不停的维护接口,所以想采取双数据源方案解决。由此,在网上查资料,自己写了个demo,仅供参考。

实现思路

分包思想,将涉及到数据库操作的代码分别放到两个包,在数据源的配置文件中,指定扫描的路径即可,下面来操作一下


环境介绍
  • springboot2.1.6(直接从springboot官网下载的,可以参考官网部署)
  • MySQL8.0(使用select version(); 查询当前版本)
  • 代码结构预览
    在这里插入图片描述
实现步骤
1、配置数据库信息
## test1 database
spring.datasource.test1.jdbc-url=jdbc:mysql://localhost:3306/aipc-sit?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.test1.username=root
spring.datasource.test1.password=123456
spring.datasource.test1.driver-class-name=com.mysql.jdbc.Driver
## test2 database
spring.datasource.test2.jdbc-url=jdbc:mysql://localhost:3306/aipc-uat?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.test2.username=root
spring.datasource.test2.password=123456
spring.datasource.test2.driver-class-name=com.mysql.jdbc.Driver
2、数据源配置文件

这个步骤是比较核心的,主要是用来扫描不同路径的的mapper来实现双库查询,注意其中包名的的指定。

  • 配置第一个数据源:DataSourceConfig1 .java
package com.shenzhouyh.multiDataSource.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.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:9:54
 * @desciption
 */
//表示这个类为一个配置类
@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.shenzhouyh.multiDataSource.mapper.test01", sqlSessionFactoryRef = "test1SqlSessionFactory") public class DataSourceConfig1 {
	// 将这个对象放入Spring容器中
	@Bean(name = "test1DataSource")
	// 表示这个数据源是默认数据源
	@Primary
	// 读取application.properties中的配置参数映射成为一个对象
	// prefix表示参数的前缀
	@ConfigurationProperties(prefix = "spring.datasource.test1") public DataSource getDateSource1() {
		return DataSourceBuilder.create().build();
	}

	@Bean(name = "test1SqlSessionFactory")
	// 表示这个数据源是默认数据源
	@Primary
	// @Qualifier表示查找Spring容器中名字为test1DataSource的对象
	public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(datasource);
		bean.setMapperLocations(
				// 设置mybatis的xml所在位置
				new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test01/*.xml"));
		return bean.getObject();
	}

	@Bean("test1SqlSessionTemplate")
	// 表示这个数据源是默认数据源
	@Primary public SqlSessionTemplate test1sqlsessiontemplate(
			@Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) {
		return new SqlSessionTemplate(sessionfactory);
	}
}

如果是自己搭建的话,这块建议直接复制,里面已经有了较为详细的说明,只要注意扫描的文件路径即可。@Primary标签不可缺少,指明了默认数据源。

  • 配置第二个数据源文件:DataSourceConfig2.java
package com.shenzhouyh.multiDataSource.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.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:11:41
 * @desciption
 */
@Configuration @MapperScan(basePackages = "com.shenzhouyh.multiDataSource.mapper.test02", sqlSessionFactoryRef = "test2SqlSessionFactory") public class DataSourceConfig2 {
	@Bean(name = "test2DataSource") @ConfigurationProperties(prefix = "spring.datasource.test2") public DataSource getDateSource2() {
		return DataSourceBuilder.create().build();
	}

	@Bean(name = "test2SqlSessionFactory") public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(datasource);
		bean.setMapperLocations(
				new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test02/*.xml"));
		return bean.getObject();
	}

	@Bean("test2SqlSessionTemplate") public SqlSessionTemplate test2sqlsessiontemplate(
			@Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) {
		return new SqlSessionTemplate(sessionfactory);
	}
}


3、补全其他组件代码
bean包,里面随意放了两个简单的类

bean包结构

  • Student.java
package com.shenzhouyh.multiDataSource.bean;

import lombok.Data;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:11:53
 * @desciption
 */
 
@Data public class Student {
	private String name;
	private String studentId;
	private String classId;

	@Override public String toString() {
		return "student{" + "name='" + name + '\'' + ", studentId='" + studentId + '\'' + ", classId='" + classId + '\''
				+ '}';
	}
}

  • Teacher.java
package com.shenzhouyh.multiDataSource.bean;

import lombok.Data;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:11:53
 * @desciption
 */
@Data public class Teacher {

	private String name;
	private String teacherId;
	private String classId;

	@Override public String toString() {
		return "teacher{" + "name='" + name + '\'' + ", teacherId='" + teacherId + '\'' + ", classId='" + classId + '\''
				+ '}';
	}
}

注:里面使用了lombok组件,没有使用过的童鞋建议可以了解一下,简化代码好用的一bi,还有更高阶的用法。

mapper包

数据库的配置文件,主要就是扫描这个包下面的类
mapper包结构

-TransactionMapping1.java

package com.shenzhouyh.multiDataSource.mapper.test01;

import com.shenzhouyh.multiDataSource.bean.Student;
import org.springframework.stereotype.Repository;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:12:02
 * @desciption
 */
@Repository public interface TransactionMapping1 {
	/**
	 * 保存学生信息
	 *
	 * @param student 学生实体
	 */
	void save(Student student);
}

-TransactionMapping2.java

package com.shenzhouyh.multiDataSource.mapper.test02;

import com.shenzhouyh.multiDataSource.bean.Teacher;
import org.springframework.stereotype.Repository;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:12:02
 * @desciption
 */
@Repository public interface TransactionMapping2 {
	/**
	 * 保存老师信息
	 *
	 * @param teacher 老师实体
	 */
	void save(Teacher teacher);
}

注:@Repository、@Service、@Controller 和 @Component 将类标识为Bean,作用一样,但是语义不一样,分别对应存储层Bean,业务层Bean,和展示层Bean以及通用Bean

dao包

dao包结构

  • TransactionDao1.java
package com.shenzhouyh.multiDataSource.dao.test01;

import com.shenzhouyh.multiDataSource.bean.Student;
import com.shenzhouyh.multiDataSource.mapper.test01.TransactionMapping1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:12:09
 * @desciption
 */
@Component public class TransactionDao1 {
	@Autowired private TransactionMapping1 mapping1;

	public void save(Student student) {
		mapping1.save(student);
	}
}

  • TransactionDao1.java
package com.shenzhouyh.multiDataSource.dao.test02;

import com.shenzhouyh.multiDataSource.bean.Teacher;
import com.shenzhouyh.multiDataSource.mapper.test02.TransactionMapping2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:12:09
 * @desciption
 */
@Component public class TransactionDao2 {
	@Autowired private TransactionMapping2 mapping2;

	public void save(Teacher teacher) {
		mapping2.save(teacher);
	}
}

service包

service包结构

  • TransactionService1.java
package com.shenzhouyh.multiDataSource.service;

import com.shenzhouyh.multiDataSource.bean.Student;
import com.shenzhouyh.multiDataSource.dao.test01.TransactionDao1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:12:25
 * @desciption
 */
@Service public class TransactionService1 {
	@Autowired private TransactionDao1 transactionDao1;

	public void save(Student student) {
		transactionDao1.save(student);
	}
}

  • TransactionService2.java
package com.shenzhouyh.multiDataSource.service;

import com.shenzhouyh.multiDataSource.bean.Teacher;
import com.shenzhouyh.multiDataSource.dao.test02.TransactionDao2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:12:25
 * @desciption
 */
@Service public class TransactionService2 {
	@Autowired private TransactionDao2 transactionDao2;

	public void save(Teacher teacher) {
		transactionDao2.save(teacher);
	}
}

controller包
package com.shenzhouyh.multiDataSource.controller;

import com.shenzhouyh.multiDataSource.bean.Student;
import com.shenzhouyh.multiDataSource.bean.Teacher;
import com.shenzhouyh.multiDataSource.service.TransactionService1;
import com.shenzhouyh.multiDataSource.service.TransactionService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

/**
 * @author: yinhao
 * @date:2019/9/9
 * @time:12:29
 * @desciption
 */
@RestController public class TransactionColler {
	@Autowired private TransactionService1 studentService;
	@Autowired private TransactionService2 teacherService;

	@RequestMapping(value = "saveStudent") public String saveStudent() {
		Student student = new Student();
		student.setStudentId(UUID.randomUUID().toString().replaceAll("-", ""));
		student.setClassId("一班");
		student.setName("张三同学");
		studentService.save(student);
		return "success1";
	}

	@RequestMapping(value = "/saveTeacher") public String saveTeacher() {
		Teacher teacher = new Teacher();
		teacher.setTeacherId(UUID.randomUUID().toString().replaceAll("-", ""));
		teacher.setClassId("一班");
		teacher.setName("李四老师");
		teacherService.save(teacher);
		return "success2";
	}
}

启动类
package com.shenzhouyh.multiDataSource;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

import java.util.Arrays;

@SpringBootApplication(exclude = {
        DataSourceAutoConfiguration.class }) @MapperScan("com.shenzhouyh.multiDataSource.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {

            System.out.println("Let's inspect the beans provided by Spring Boot:");

            String[] beanNames = ctx.getBeanDefinitionNames();
            Arrays.sort(beanNames);
            for (String beanName : beanNames) {
                System.out.println(beanName);
            }

        };
    }

}

注意:

  • 启动类的SpringBootApplication注解,相当于@Configuration,@EnableAutoConfiguration,@ComponentScan,因为他们基本一块使用,所以springboot2.0直接就进行了整合。详细请参考SpringBootApplication的使用
  • SpringBootApplication后使用exclude = {
    DataSourceAutoConfiguration.class },否则会报:Failed to determine a suitable driver class异常,原因是应用没有使用到DataSource,但是在pom.xml里引入了mybatis-spring-boot-starter,排除一个就好了。
  • @MapperScan注解用于,要扫描mapper类包的路径,可配置多路径且可使用通配符
资源包

资源包结构

-TransactionMapping1.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shenzhouyh.multiDataSource.mapper.test01.TransactionMapping1">
    <insert id="save" parameterType="com.shenzhouyh.multiDataSource.bean.Student">
        insert into student (name,student_id,class_id) values (#{name},#{studentId},#{classId});
    </insert>
</mapper>

TransactionMapping2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shenzhouyh.multiDataSource.mapper.test02.TransactionMapping2">
    <insert id="save" parameterType="com.shenzhouyh.multiDataSource.bean.Teacher">
        insert into teacher (name,teacher_id,class_id) values (#{name},#{teacherId},#{classId});
    </insert>
</mapper>
pom文件
<?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>

    <groupId>org.springframework</groupId>
    <artifactId>gs-spring-boot</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- tag::actuator[] -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- end::actuator[] -->
        <!-- tag::tests[] -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- end::tests[] -->
        <!--日志组件-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
        <!-- <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-jdbc</artifactId>
         </dependency>-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
    </dependencies>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

总结

这个demo是网上现有的例子,我只是在本地手敲了一遍,但是不可否认,遇坑无数,老是卡壳,本文中也就挑了一个说明了一下,我也会在后面的博客中进行总结和介绍。这个demo只是一个双库查询的简单demo,距离生产操作还差的很远,最明显的一点是没有进行事务控制,我参考的大神文章中都有介绍,除了这种方式之外,还有好多种方案,比如AOP请求拦截等分割数据源。还需努力啊!!!

本文主要参考
https://blog.csdn.net/tuesdayma/article/details/81081666,推荐一下,里面还提供了github地址,多向大佬学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值