前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Boot2(四):使用Spring Boot实现多数据源过程

Spring Boot2(四):使用Spring Boot实现多数据源过程

原创
作者头像
鸟不拉屎
修改2019-07-04 11:07:36
1.1K0
修改2019-07-04 11:07:36
举报
文章被收录于专栏:Java成长计划Java成长计划

前言

实际业务场景中,不可能只有一个库,所以就有了分库分表,多数据源的出现。实现了读写分离,主库负责增改删,从库负责查询。这篇文章将实现Spring Boot如何实现多数据源,动态数据源切换,读写分离等操作。

代码部署

快速新建项目spring-boot项目

1、添加maven依赖

代码语言:javascript
复制
<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>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

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

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2、application配置多数据源读取配置

和之前教程一样,首先配置application.yml

代码语言:javascript
复制
#指定配置文件为test
spring:
  profiles:
    active: test

#配置Mybatis
mybatis:
  configuration:
    # 开启驼峰命名转换,如:Table(create_time) -> Entity(createTime)。不需要我们关心怎么进行字段匹配,mybatis会自动识别`大写字母与下划线`
    map-underscore-to-camel-case: true

#打印SQL日志
logging:
  level:
    com.niaobulashi.mapper.*: DEBUG

其中打印SQL日志这块,因为是多数据源,在mapper包下面区分不同的数据库来源xml文件,所以用*表示。

配置application-test.yml如下

代码语言:javascript
复制
spring:
  datasource:
    #主库
    master:
      jdbc-url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    #从库
    slave:
      jdbc-url: jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

从spring.datasource节点开始,区分主库master,从库slave。主库连接的数据库为test,从库连接的数据库为test2。

注意:这里需要注意的是,从Spring Boot2开始,在配置多数据源时有些配置发生了变化,网上许多教程使用的是spring.datasource.url。会出现jdbcUrl is required with driverClassName.的问题。

解决方法:配置多数据源时,将spring.datasource.url配置改为spring.datasource.jdbc-url

3、添加主库配置信息

依据知名博主:纯洁的微笑,写的博文我们来分析一波

首先看主库配置的代码:

代码语言:javascript
复制
@Configuration
@MapperScan(basePackages = "com.niaobulashi.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class DataSourceMasterConfig {

    /**
     * 是application-test.yml中的spring.datasource.master配置生效
     * @return
     */
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    @Primary
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 将配置信息注入到SqlSessionFactoryBean中
     * @param dataSource    数据库连接信息
     * @return
     * @throws Exception
     */
    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
        return bean.getObject();
    }

    /**
     * 事务管理器,在实例化时注入主库master
     * @param dataSource
     * @return
     */
    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * SqlSessionTemplate具有线程安全性
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "masterSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

问题:看这块masterSqlSessionFactorySqlSessionFactoryBean只获取了spring.datasource.master数据库连接信息,并没有获取多数据库的配置信息mybatis.configuration导致我们需要配置驼峰命名规则,配置信息并没有注入到SqlSessionFactoryBean。这样就导致在查询是,遇到下划线无法解析相应字段user_id,dept_id,create_time

解决方法:在配置中添加Configuration

同时,将配置信息注入到SqlSessionFactoryBean

代码语言:javascript
复制
/**
 * 将配置信息注入到SqlSessionFactoryBean中
 * @param dataSource    数据库连接信息
 * @return
 * @throws Exception
 */
@Bean(name = "slaveSqlSessionFactory")
public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
    org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
    configuration.setMapUnderscoreToCamelCase(true);
    bean.setConfiguration(configuration);
    bean.setDataSource(dataSource);
    bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
    return bean.getObject();
}

4、添加从库配置信息

和添加主库配置信息一样,只不过不同的是,不需要添加@Primary首选注解

代码如下

代码语言:javascript
复制
@Configuration
@MapperScan(basePackages = "com.niaobulashi.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate")
public class DataSourceSlaveConfig {

    /**
     * 是application-test.yml中的spring.datasource.master配置生效
     * @return
     */
    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 将配置信息注入到SqlSessionFactoryBean中
     * @param dataSource    数据库连接信息
     * @return
     * @throws Exception
     */
    @Bean(name = "slaveSqlSessionFactory")
    public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        bean.setConfiguration(configuration);
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
        return bean.getObject();
    }

    /**
     * 事务管理器,在实例化时注入主库master
     * @param dataSource
     * @return
     */
    @Bean(name = "slaveTransactionManager")
    public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * SqlSessionTemplate具有线程安全性
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "slaveSqlSessionTemplate")
    public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

5、扩展配置方法会报错

在网上还看到这样一种配置,单独通过@ConfigurationProperties注解配置Mybatis的配置信息如下

代码语言:javascript
复制
/**
 * 试application.yml中的mybatis.configuration配置生效,如果不主动配置,由于@Order配置顺序不同,讲导致配置不能及时生效
 * 使配置信息加载到类中,再注入到SqlSessionFactoryBean
 * @return
 */
@Bean
@ConfigurationProperties(prefix = "mybatis.configuration")
public org.apache.ibatis.session.Configuration configuration() {
    return new org.apache.ibatis.session.Configuration();
}

其中prefix,在主库和从库中的id是一样的,必须保持不同,否则idea就会提示报错Duplicate prefix

导致只有主库可以执行Mybatis的配置,从库无效。

代码语言:javascript
复制
@Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource, org.apache.ibatis.session.Configuration configuration) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
        bean.setConfiguration(configuration);
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
        return bean.getObject();
    }

这块验证只有主库有效,从库的驼峰方法解析无效。后续再来研究下。。。

6、数据层代码

代码结构如下

其中SysUserMasterDao代码

代码语言:javascript
复制
public interface SysUserMasterDao {
	
	/**
	 * 根据userId查询用户信息
	 * @param userId  用户ID
	 */
	List<SysUserEntity> queryUserInfo(Long userId);

	/**
	 * 查询所有用户信息
	 */
	List<SysUserEntity> queryUserAll();

	/**
	 * 根据userId更新用户的邮箱和手机号
	 * @return
	 */
	int updateUserInfo(SysUserEntity user);

}

7、resource下数据执行语句

SysCodeMasterDao.xml

代码语言:javascript
复制
<mapper namespace="com.niaobulashi.mapper.master.SysUserMasterDao">

    <!--查询所有用户信息-->
    <select id="queryUserAll" resultType="com.niaobulashi.entity.SysUserEntity">
        SELECT
            ur.*
        FROM
            sys_user ur
        WHERE
            1 = 1
    </select>

    <!--根据用户userId查询用户信息-->
    <select id="queryUserInfo" resultType="com.niaobulashi.entity.SysUserEntity">
        SELECT
            ur.*
        FROM
            sys_user ur
        WHERE
            1 = 1
          AND ur.user_id = #{userId}
    </select>

    <!-- 根据UserId,更新邮箱和手机号 -->
    <update id="updateUserInfo" parameterType="com.niaobulashi.entity.SysUserEntity">
        UPDATE sys_user u
        <set>
            <if test="email != null">
                u.email = #{email},
            </if>
            <if test="mobile != null">
                u.mobile = #{mobile},
            </if>
        </set>
        WHERE
        u.user_id = #{userId}
    </update>

</mapper>

8、Controller层测试

代码语言:javascript
复制
@RestController
public class SysUserController {

    @Autowired
    private SysUserMasterDao sysUserMasterDao;

    @Autowired
    private SysUserSlaveDao sysUserSlaveDao;

    /**
     * 查询所有用户信息Master
     * @return
     */
    @RequestMapping("/getUserMasterAll")
    private List<SysUserEntity> getUserMaster() {
        System.out.println("查询主库");
        List<SysUserEntity> userList = sysUserMasterDao.queryUserAll();
        return userList;
    }

    /**
     * 查询所有用户信息Slave
     * @return
     */
    @RequestMapping("/getUserSlaveAll")
    private List<SysUserEntity> getUserSlave() {
        System.out.println("查询从库");
        List<SysUserEntity> userList = sysUserSlaveDao.queryUserAll();
        return userList;
    }

    /**
     * 根据userId查询用户信息Master
     * @return
     */
    @RequestMapping("/getUserMasterById")
    private List<SysUserEntity> getUserMasterById(@RequestParam(value = "userId", required = false) Long userId) {
        List<SysUserEntity> userList = sysUserMasterDao.queryUserInfo(userId);
        return userList;
    }

    /**
     * 根据userId查询用户信息Slave
     * @return
     */
    @RequestMapping("/getUserSlaveById")
    private List<SysUserEntity> getUserSlaveById(@RequestParam(value = "userId", required = false) Long userId) {
        List<SysUserEntity> userList = sysUserSlaveDao.queryUserInfo(userId);
        return userList;
    }

}

发送查询所有用户接口

主库:http://localhost:8080/getUserMasterAll

从库:http://localhost:8080/getUserSlaveAll

总结

1、通过多数据源方式实现数据库层面的读写分离

2、多数据源链接数据库是,使用spring.datasource.jdbc-url

3、多数据源的mybatis.configuration配置注意需要手动注入SqlSessionFactory

示例代码-github

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 代码部署
    • 1、添加maven依赖
      • 2、application配置多数据源读取配置
        • 3、添加主库配置信息
          • 4、添加从库配置信息
            • 5、扩展配置方法会报错
              • 6、数据层代码
                • 7、resource下数据执行语句
                  • 发送查询所有用户接口
                  • 总结
                  • 示例代码-github
                  相关产品与服务
                  数据库
                  云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档