当前位置: 首页 > article >正文

SpringBoot实现mysql多数据源配置(Springboot+Mybatis)

最近在学习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种解决方案
最后编辑于:2024-10-04 14:46:28


喜欢的朋友记得点赞、收藏、关注哦!!!


http://www.kler.cn/news/365305.html

相关文章:

  • Docker:容器化的革命
  • 【MySQL数据库】MySQL高级语句(SQL语句进阶版)
  • 深度学习调参大法
  • SLAM|1. 相机投影及相机畸变
  • C#探索之路基础夯实篇(6):C#在Unity中的自定义特性
  • SIP 业务举例之 Call Forwarding - No Answer(无应答呼叫转移)
  • 模拟信号采集显示器+GPS同步信号发生器制作全过程(焊接、问题、代码、电路)
  • Java调用大模型 - Spring AI 初体验
  • [ 钓鱼实战系列-基础篇-8 ] 一篇文章教会你选择适合的钓鱼项目并设计钓鱼页面
  • 富格林:曝光阻止欺诈套路攻略
  • 利用移动式三维扫描技术创建考古文物的彩色纹理网格【上海沪敖3D】
  • Java基础第二天(实训学习整理资料(二))
  • 【纯血鸿蒙】HarmonyOS Emulator(实操亲测版)
  • java 17天 TreeSet以及Collections
  • 昇思MindSpore进阶教程--安装常见问题(上)
  • 炸了!改进Transformer!Transformer-BiGRU多变量回归预测(Matlab)
  • 机器学习与神经网络的当下与未来
  • LoadBalancer 类型的 Service工作期间,kube-proxy做了什么?
  • ctfshow(262,264)--反序列化漏洞--字符串逃逸
  • LeetCode Hot 100:图论
  • 昇思MindSpore进阶教程--三方硬件对接
  • Windchill性能优化篇之分页查询
  • 操作系统笔记(二)进程,系统调用,I/O设备
  • 使用LangGraph构建多Agent系统架构!
  • C++20中头文件syncstream的使用
  • JavaScript 有哪些学习资源