美文网首页
SpringBoot实现mysql多数据源配置(Springbo

SpringBoot实现mysql多数据源配置(Springbo

作者: 毛福林 | 来源:发表于2020-05-08 18:42 被阅读0次

    最近在学习SpringBoot的一些知识,主要是参考了纯洁的微笑的一些博客作为练手,想着他已经把入门的教程写的很详细了,如果再简单的把他的教程拷贝到自己的简书,这是是赤裸裸的剽窃,很不地道,作为一个技术人员,应该有一定的技术底线。所以,就把纯洁微笑的博客链接放在参考链接中的第一条,( ̄▽ ̄)"

    目的

    多数据源是指在同一个项目中写入/读取相同类型数据源的不同库的情况,比如在项目中需要访问mysql两个不同的数据库,常见于主从模式、或者数据库的分库的场景。
    多数据源不包含不同类型数据源的情况,例如:一个项目中可能引入mysql数据库,同时也引入redis缓存数据库,这的确是两个数据源,但是类型不同,可以直接使用SpringBoot的相关配置即可完成。

    开始动手干活吧

    话不多说,直接开整

    开发环境

    • IDE:idea
    • 项目构建:maven
    • 测试工具:postman

    创建SpringBoot的项目

    在这里说一下纯洁的微笑的项目结构,首先,创建了一个spring-boot-example的maven工程,该工程的打包类型为pom,每一个功能作为一个子module 项目,使用IDEA有一个比较高尴尬的事情,使用Spring 脚手架(spring initializr) 创建的module 在父项目的pom.xml文件中没有增加<module>标签。也有可能是因为我用的姿势不正确~
    使用spring initalizr,在创建的时候,可以勾选所需要的依赖,避免手工填写相关的依赖,所选依赖如下图所示:

    使用spring initalizr创建module

    在spring-boot-example项目里创建一个新的module,名称为mybatis-mutidbsource。在父项目的pom.xml文件,手工增加 <module>mybatis-mutidbsource</module> ,如果不加的话,应该也没问题,这个没尝试过。迫于强迫症的习惯,还是手工加上了。

    引入相关的依赖

    <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
      <mybatis.version>2.1.2</mybatis.version>
    </properties>
    <dependencies>
            <!-- web -->
            <!-- web 模块 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- mybatis -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <!-- mysql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <!-- 测试相关 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    

    配置application.properties

    在配置数据库信息时,需要注意一点,单数据源的数据库连接使用的参数为:spring.datasource.url ,具体示例如下:

    spring.datasource.url = jdbc:mysql://localhost:3306/spring_boot_test?useSSL=true&serverTimezone=UTC
    

    在使用多数据源的场景下,要将url修改为jdbc-url,对于多数据源参数的前缀信息也没有强制要求,但为了更方便的配置参数,建议相同数据源使用相同的前缀信息,修改后的配置参数如下:

    spring.datasource.test1.jdbc-url = jdbc:mysql://localhost:3306/spring_boot_test1?useSSL=true&serverTimezone=UTC
    
    

    其他的配置参数没有特别说明,完整的配置信息如下:

    # 数据库相关
    ## 配置多数据源 test1
    spring.datasource.test1.username=root
    spring.datasource.test1.password=1qaz@WSX
    spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver
    #此处url 修改为jdbc-url
    spring.datasource.test1.jdbc-url = jdbc:mysql://localhost:3306/spring_boot_test1?useSSL=true&serverTimezone=UTC
    
    ## 配置多数据源 test2
    spring.datasource.test2.username=root
    spring.datasource.test2.password=1qaz@WSX
    spring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver
    #此处url 修改为jdbc-url
    spring.datasource.test2.jdbc-url = jdbc:mysql://localhost:3306/spring_boot_test2?useSSL=true&serverTimezone=UTC
    
    
    # mybatis
    mybatis.type-aliases-package=com.fulin.example.bootexample.mybatismutidbsource.model
    

    最最最最重要的数据源配置

    第一个数据源

    @Configuration
    @MapperScan(basePackages = "com.fulin.example.bootexample.mybatismutidbsource.mapper.test1",sqlSessionTemplateRef = "test1SqlSessionTemplate")
    public class DataSource1Config {
    
        @Bean(name = "test1DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.test1")
        @Primary
        public DataSource testDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "test1SqlSessionFactory")
        @Primary
        public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                    .getResources("classpath:mybatis/mapper/test1/*.xml"));
            return bean.getObject();
        }
    
        @Bean(name = "test1SqlSessionTemplate")
        @Primary
        public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    
        @Bean(name = "test1TransactionManager")
        @Primary
        public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    
    
    }
    

    第二个数据源

    @Configuration
    @MapperScan(basePackages = "com.fulin.example.bootexample.mybatismutidbsource.mapper.test2",sqlSessionTemplateRef = "test2SqlSessionTemplate")
    public class DataSource2Config {
    
        @Bean(name = "test2DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.test2")
        public DataSource testDataSource(){
            return DataSourceBuilder.create().build();
        }
    
    
        @Bean(name = "test2SqlSessionFactory")
        public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                    .getResources("classpath:mybatis/mapper/test2/*.xml"));
            return bean.getObject();
        }
    
        @Bean(name = "test2TransactionManager")
        public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource){
            return new DataSourceTransactionManager(dataSource);
        }
    
        @Bean(name = "test2SqlSessionTemplate")
        public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }
    

    比对两个数据源配置文件

    使用文本比较软件,比对两个数据源配置文件,比对结果如下:


    数据源配置文件差异比对

    两个配置文件的主要差异有两点:

    1. 第一个数据源需要增加@Primary标签,用于区分是否为主库
    2. 两个数据源的bean 类型完全相同,应该使用名称进行区分

    验证功能

    为了能够验证配置的数据源,使用用户信息查询的简单demo验证配置是否正确。可以使用SpringBoot的Test类进行测试,也可以使用Restful接口的方式进行验证,在这里使用了后面的Restful接口的方式进行验证

    创建model类

    为了能够更加清晰的演示,针对不同的数据源,创建不同的model类,只是类名不同,具体字段等内容完全一致。使用了lombok的@Data标签可以省略getter和setter方法的编码。为了减少篇幅大小,在这里只显示了第一个数据源的model类。

    @Data
    public class User1Entity implements Serializable {
    
        private static final long serialVersionUID = 1L;
        public long id;
        public String userName;
        public String passWord;
        public UserSexEnum userSex;
        public String nickName;
    
    
        public User1Entity() {
            super();
        }
    
        public User1Entity(String userName, String passWord, UserSexEnum userSex) {
            super();
            this.passWord = passWord;
            this.userName = userName;
            this.userSex = userSex;
        }
    
        @Override
        public String toString() {
            return "userName: " + this.userName + ", pasword :" + this.passWord + ", sex :" + userSex.name();
        }
    }
    

    创建Mapper类

    创建Mapper类,主要写了一些增删改查的方法,便于后面的测试。

    @Mapper
    @Repository
    public interface User1Mapper {
    
        List<User1Entity> getAll();
        User1Entity getOne(Long id);
        void inserUser(User1Entity userEntity);
    
        void updateUser(User1Entity userEntity);
    
        void deleteById(Long id);
    
        User1Entity getOneByUserName(String userName);
    
        void deleteAll();
    }
    

    创建mapper.xml

    创建mybatis的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.fulin.example.bootexample.mybatismutidbsource.mapper.test1.User1Mapper" >
        <resultMap id="BaseResultMap" type="com.fulin.example.bootexample.mybatismutidbsource.model.test1.User1Entity">
            <id column="id" property="id" jdbcType="BIGINT" />
            <result column="userName" property="userName" jdbcType="VARCHAR" />
            <result column="passWord" property="passWord" jdbcType="VARCHAR" />
            <result column="user_sex" property="userSex" javaType="com.fulin.example.bootexample.mybatismutidbsource.enums.UserSexEnum" />
            <result column="nick_name" property="nickName" jdbcType="VARCHAR" />
        </resultMap>
    
        <sql id="Base_Column_List">
            id , userName,passWord,user_sex,nick_name
        </sql>
        <select id="getAll" resultMap="BaseResultMap">
            select
            <include refid="Base_Column_List"/>
            from users
        </select>
    
        <select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap">
            select
            <include refid="Base_Column_List" />
            from users
            where id = #{id}
        </select>
        <select id="getOneByUserName" parameterType="java.lang.String" resultMap="BaseResultMap">
            select
            <include refid="Base_Column_List" />
            from users
            where userName = #{userName}
        </select>
    
        <insert id="inserUser" parameterType="com.fulin.example.bootexample.mybatismutidbsource.model.test1.User1Entity">
            insert into users (userName,passWord,nick_name,user_sex)
            values(#{userName},#{passWord},#{nickName},#{userSex})
        </insert>
        <select id="updateUser" parameterType="com.fulin.example.bootexample.mybatismutidbsource.model.test1.User1Entity">
            update users
            set
            <if test="userName != null"> userName = #{userName} , </if>
            <if test="passWord != null"> passWord = #{passWord} , </if>
            nick_name=#{nickName}
        </select>
    
        <delete id="deleteById" parameterType="java.lang.Long">
            delete from users
            where id = #{id}
        </delete>
    
        <delete id="deleteAll" >
            delete from users
        </delete>
    </mapper>
    
    

    controller

    为了简单的测试相关的接口,没有编写service 层,直接在controller层调用dao层相关的接口。

    @RestController
    public class UserController {
    
        @Autowired
        private User1Mapper user1Mapper;
    
        @Autowired
        private User2Mapper user2Mapper;
    
        @GetMapping("/user1/getAllUser")
        public List<User1Entity> getAllUser1(){
            return user1Mapper.getAll();
        }
    
        @GetMapping("/user2/getAllUser")
        public List<User2Entity> getAllUser2(){
            return user2Mapper.getAll();
        }
    
        @GetMapping("/user1/{id}")
        public User1Entity getOneUser1(@PathVariable(value = "id") Long id){
            return user1Mapper.getOne(id);
        }
    
        @GetMapping("/user2/{id}")
        public User2Entity getOneUser2(@PathVariable(value = "id") Long id){
            return user2Mapper.getOne(id);
        }
    
        @PostMapping("/user1/inserUser1")
        public String insertUser1(@RequestParam(value = "userName") String userName,
                                  @RequestParam(value = "passWord") String passWord,
                                  @RequestParam(value = "userSex") String userSex){
    
            UserSexEnum  userSexEnum = UserSexEnum.valueOf(userSex);
            User1Entity user1Entity = new User1Entity( userName,  passWord,  userSexEnum);
            user1Mapper.inserUser(user1Entity);
            return "insert user ["+user1Entity+"] OK !";
        }
    
        @PostMapping("/user2/inserUser2")
        public String insertUser2(@RequestParam(value = "userName") String userName,
                                  @RequestParam(value = "passWord") String passWord,
                                  @RequestParam(value = "userSex") String userSex){
    
            UserSexEnum  userSexEnum = UserSexEnum.valueOf(userSex);
            User2Entity user2Entity = new User2Entity( userName,  passWord,  userSexEnum);
            user2Mapper.inserUser(user2Entity);
            return "insert user ["+user2Entity+"] OK !";
        }
    
    }
    

    项目结构

    最后的项目结构:


    项目结构

    最后的测试

    使用postman进行接口测试,在这里只展示insertUser的测试界面

    postman调用一个数据源的insertUser接口 第一个数据库查询结果 postman调用一个数据源的insertUser接口 第二个数据库查询结果

    问题列表

    1. 程序正常启动,调用系统接口时,提示“java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName”,具体错误信息如下:


      jdbcUrl is required with driverClassName 错误截图

      解决方法:在配置多数据源时,数据库连接串的关键字应该改为jdbc-url

    2. 在controller层引入mapper时,会提示"Could not autowire. No beans of 'User1Mapper' type found. "错误信息

      Could not autowire. No beans of 'User1Mapper' type found. 错误截图
      解决方法:在User1Mapper增加@Repository注解,这是一个相对比较简单的方法,其他修改方法可以参考文献3

    参考链接

    1. Spring Boot(七):Mybatis 多数据源最简解决方案
    2. 纯洁的微笑源码 spring-boot-example
    3. Intellij IDEA中Mybatis Mapper自动注入警告的6种解决方案

    相关文章

      网友评论

          本文标题:SpringBoot实现mysql多数据源配置(Springbo

          本文链接:https://www.haomeiwen.com/subject/lxjtnhtx.html