Spring boot实现多数据源的一种方式

参考博客感谢作者

之所以说是一种方式,因为还有一种方式是通过继承AbstractRoutingDataSource来动态选择DataSource路由,通过AOP的方式进行切换。

该程序Dao层实现使用mybatis框架。

该方式实现的思路是:有多少个数据源就写多少个数据源的配置类,mybatis的Mapper接口分别对应放在不同的包下,Mapper.xml文件根据数据源的不同放在不同的目录下。不同的数据源扫描不同的Mapper接口和Mapper.xml文件。

项目结构:

Spring boot实现多数据源的一种方式

application.properties配置文件

spring.datasource.cluster.driverClassName = com.mysql.jdbc.Driver
spring.datasource.cluster.url = jdbc:mysql://localhost:3306/std1?useUnicode=true&characterEncoding=utf-8
spring.datasource.cluster.username = root
spring.datasource.cluster.password = root


spring.datasource.master.driverClassName = com.mysql.jdbc.Driver
spring.datasource.master.url = jdbc:mysql://localhost:3306/std2?useUnicode=true&characterEncoding=utf-8
spring.datasource.master.username = root
spring.datasource.master.password = root

配置了两个数据源,一个是master,一个是cluster。有多少的数据源,就可以配置多少个数据源。

需要注意的是,Spring boot 2.0以后,数据源url和driveClassName的写法发生了变化,具体可以查找资料。

master数据源配置类

/**
 * mybaits Master库配置类
 * com.wanlihong.multi_mybatis.mapper.master 为Master数据源对应的Mapper接口类
 */
@Configuration
@MapperScan(basePackages = "com.wanlihong.multi_mybatis.mapper.master",sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class MasterDataSourceConfig {

    /**
     * 创建数据源
     *@return DataSource
     */
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    @Primary
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 创建工厂
     *@param dataSource
     *@throws Exception
     *@return SqlSessionFactory
     */
    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //master 数据源对应的mapper.xml
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
        return bean.getObject();
    }

    /**
     * 创建事务
     *@param dataSource
     *@return DataSourceTransactionManager
     */
    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterDataSourceTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 创建模板
     *@param sqlSessionFactory
     *@return SqlSessionTemplate
     */
    @Bean(name = "masterSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

cluster数据源配置文件

/**
 * mybatis cluster数据源配置类
 * com.wanlihong.multi_mybatis.mapper.cluster 是cluster数据源扫描的Mapper接口类
 */
@Configuration
@MapperScan(basePackages = "com.wanlihong.multi_mybatis.mapper.cluster",sqlSessionTemplateRef = "clusterSqlSessionTemplate")
public class ClusterDataSourceConfig {

    /**
     * 创建数据源
     *@return DataSource
     */
    @Bean(name = "clusterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.cluster")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 创建工厂
     *@param dataSource
     *@throws Exception
     *@return SqlSessionFactory
     */
    @Bean(name = "clusterSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("clusterDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //cluster 数据源对应的mapper.xml文件
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/cluster/*.xml"));
        return bean.getObject();
    }

    /**
     * 创建事务
     *@param dataSource
     *@return DataSourceTransactionManager
     */
    @Bean(name = "clusterTransactionManager")
    public DataSourceTransactionManager masterDataSourceTransactionManager(@Qualifier("clusterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 创建模板
     *@param sqlSessionFactory
     *@return SqlSessionTemplate
     */
    @Bean(name = "clusterSqlSessionTemplate")
    public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("clusterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

两个Mapper接口类

package com.wanlihong.multi_mybatis.mapper.cluster;

import com.wanlihong.multi_mybatis.model.Stu;

import java.util.List;
import java.util.Map;

public interface ClusterStuMapper {
     void clusterrInsert(Stu stu);
     List<Stu>  clusterrList(Map<String,Object> map);
}
package com.wanlihong.multi_mybatis.mapper.master;

import com.wanlihong.multi_mybatis.model.Stu;

import java.util.List;
import java.util.Map;

public interface MasterStuMapper {
     void masterInsert(Stu stu);
     List<Stu> masterList(Map<String,Object> map);
}

注意,这两个接口在不同的包下面

两个mapper.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.wanlihong.multi_mybatis.mapper.cluster.ClusterStuMapper">

    <resultMap id="clusterrStuMap" type="com.wanlihong.multi_mybatis.model.Stu">
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="age" property="age" jdbcType="INTEGER" />
        <result column="sex" property="sex" jdbcType="VARCHAR" />
    </resultMap>



    <insert id="clusterrInsert" parameterType="com.wanlihong.multi_mybatis.model.Stu">
        insert into student (
        <trim prefixOverrides=",">
            <if test="name != null">,name</if>
            <if test="age != null">,age</if>
            <if test="sex != null">,sex</if>
        </trim>
        ) values (
        <trim prefixOverrides=",">
            <if test="name != null">,#{name}</if>
            <if test="age != null">,#{age}</if>
            <if test="sex != null">,#{sex}</if>
        </trim>
        )
    </insert>


    <select id="clusterrList" parameterType="java.util.Map" resultMap="clusterrStuMap">
        select
        name,age,sex
        from student
        <where>
            <if test="name != null">and name = #{name}</if>
            <if test="age != null">and age = #{age}</if>
            <if test="sex != null">and sex = #{sex}</if>
        </where>
    </select>
</mapper>
<?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.wanlihong.multi_mybatis.mapper.master.MasterStuMapper">

    <resultMap id="masterStuMap" type="com.wanlihong.multi_mybatis.model.Stu">
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="age" property="age" jdbcType="INTEGER" />
        <result column="sex" property="sex" jdbcType="VARCHAR" />
    </resultMap>



    <insert id="masterInsert" parameterType="com.wanlihong.multi_mybatis.model.Stu">
        insert into student (
        <trim prefixOverrides=",">
            <if test="name != null">,name</if>
            <if test="age != null">,age</if>
            <if test="sex != null">,sex</if>
        </trim>
        ) values (
        <trim prefixOverrides=",">
            <if test="name != null">,#{name}</if>
            <if test="age != null">,#{age}</if>
            <if test="sex != null">,#{sex}</if>
        </trim>
        )
    </insert>


    <select id="masterList" parameterType="java.util.Map" resultMap="masterStuMap">
        select
        name,age,sex
        from student
        <where>
            <if test="name != null">and name = #{name}</if>
            <if test="age != null">and age = #{age}</if>
            <if test="sex != null">and sex = #{sex}</if>
        </where>
    </select>
</mapper>

注意,这两个mapper.xml文件也在不同的目录下面,和不同数据源的配置类要对应起来

两个数据库的表结构和表名称都是相同的,实际开发中可能不同。

CREATE TABLE `student` (
  `name` varchar(100) character set latin1 default NULL,
  `age` int(11) default NULL,
  `sex` varchar(255) character set latin1 default NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Service:

public interface MyService {
    public void clusterrInsert(Stu stu);
    public List<Stu> clusterList(Map<String,Object> map);

    public void masterInsert(Stu stu);
    public List<Stu> masterList(Map<String,Object> map);
}
@Service
public  class MyServiceImpl implements MyService {
    @Resource
    private MasterStuMapper masterStuMapper;
    @Resource
    private ClusterStuMapper clusterStuMapper;

    @Override
    public void clusterrInsert(Stu stu) {
        clusterStuMapper.clusterrInsert(stu);
    }

    @Override
    public List<Stu> clusterList(Map<String, Object> map) {
        return clusterStuMapper.clusterrList(map);
    }

    @Override
    public void masterInsert(Stu stu) {
        masterStuMapper.masterInsert(stu);

    }

    @Override
    public List<Stu> masterList(Map<String, Object> map) {
        return masterStuMapper.masterList(map);
    }
}

Controller:

@RestController
public class FirstController {
    @Resource
    private MyService myService;

    @RequestMapping("/save/master")
    public  String testMaster(Stu stu){
        myService.masterInsert(stu);
        return "success_master";
    }

    @RequestMapping("/save/cluster")
    public  String testCluster(Stu stu){
        myService.clusterrInsert(stu);
        return "success_cluster";
    }

    @RequestMapping("/masterList")
    public List<Stu> getMasterList(){
        return myService.masterList(null);
    }

    @RequestMapping("/clusterList")
    public List<Stu> getClusterList(){
        return myService.clusterList(null);
    }
}

启动类:

@SpringBootApplication
@MapperScan("com.wanlihong.multi_mybatis.mapper")
public class MultiMybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(MultiMybatisApplication.class, args);
    }

}

使用Postman进行测试:

Spring boot实现多数据源的一种方式

 这只是Springboot 配置多数据源的方式之一,优点是简单易懂,缺点是缺少灵活性。