跳至主要內容

Spring-多数据源

holic-x...大约 5 分钟JAVA框架

Spring-多数据源

学习核心

  • 多数据源概念和核心
  • Spring多数据源整合的两种方式

学习资料

多数据源概念

多数据源概念&引用场景

​ 多数据源概念顾名思义即在一个系统中引用到了不同的数据源。

​ 【场景1】第三方数据交互

​ 常见与外部数据对接方式是通过接口交互的方式来实现功能交互,但是一些场景中有些与第三方对接时,有些合作方并不会为了某些需求而单独开发一个功能,而是通过提供给一个可以访问数据源的只读账号,需要获取什么数据则自行获取并进行业务逻辑处理,这时候就避免不了需要进行多数据源整合了

​ 【场景二】数据整合(数据的分库分表)

​ 当业务数据达到了一个量级,使用单一数据库存储达到了一个瓶颈,需要进行分库分表等操作进行数据管理,在操作数据时,不可避免的涉及到多数据源问题

多数据源整合的方式

【1】使用分包方式,不同的数据源配置不同的MapperScan和mapper文件

【2】使用AOP切片方式,实现动态数据源切换

【3】使用数据库代理中间件,如Mycat等

不同方式的区别

【1】分包方式可以集合JTA(JAVA Transactional API)实现分布式事务,但是整个流程的实现相对来说比较复杂

【2】AOP动态配置数据源方式缺点在于无法实现全局分布式事务,所以如果只是对接第三方数据源,不涉及到需要保证分布式事务的话,可以作为一种选择

【3】使用数据库代理中间件方式是现在比较流行的一种方式,很多公司都是采用这种方式,开发者不需要关注太多与业务无关的问题,把它们都交给数据库代理中间件去处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,比较常见的有MycatTDDL等,还有阿里出的100%自研的分布式数据库OceanBase,从最底层支持分布式,性能也非常强大

基于分包方式的多数据源

image-20240608221513934

项目构建 & 配置

​ 新建Springboot项目,引入相关maven依赖配置:

  • springboot 版本2.7.6
  • spring-boot-starter-web
  • mybatis-spring-boot-starter 2.3.0
  • mysql-connector-java
  • lombok
  • hutool-all 5.4.5

application.yml配置主从数据源

# 项目启动窗口
server:
  port: 8080

# 项目名称
spring:
  profiles:
    active: dev
  application:
    name: springboot-mybatis-multiDatasource
  datasource:
    # 主数据库
    master:
      # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
      jdbc-url: jdbc:mysql://localhost:3306/db_noob_demo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
      username: db_noob_demo
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    # 副数据库
    slave:
      # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
      jdbc-url: jdbc:mysql://localhost:3306/db_noob_demo_bak?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
      username: db_noob_demo_bak
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

主从数据源配置(config)

@Configuration
// 指定主数据库扫描对应的Mapper文件,生成代理对象
@MapperScan(basePackages ="com.noob.framework.mapper.master" ,sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {

    // mapper.xml所在地址
    private static final String MAPPER_LOCATION = "classpath*:mapper/master/*.xml";


    /**
     * 主数据源,Primary注解必须增加,它表示该数据源为默认数据源
     * 项目中还可能存在其他的数据源,如获取时不指定名称,则默认获取这个数据源,如果不添加,则启动时候报错
     */
    @Primary
    @Bean(name = "masterDataSource")
    // 读取spring.datasource.master前缀的配置文件映射成对应的配置对象
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource dataSource() {
        DataSource build = DataSourceBuilder.create().build();
        return build;
    }

    /**
     * 事务管理器,Primary注解作用同上
     */
    @Primary
    @Bean(name = "masterTransactionManager")
    public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * session工厂,Primary注解作用同上
     */
    @Primary
    @Bean(name = "masterSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));
        return sessionFactoryBean.getObject();
    }

}
@Configuration
// 指定从数据库扫描对应的Mapper文件,生成代理对象
@MapperScan(basePackages = "com.noob.framework.mapper.salve", sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class SlaveDataSourceConfig {
    // mapper.xml所在地址
    private static final String MAPPER_LOCATION = "classpath*:mapper/salve/*.xml";

    /**
     * 数据源
     */
    @Bean(name = "slaveDataSource")
    // 读取spring.datasource.slave前缀的配置文件映射成对应的配置对象
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource dataSource() {
        DataSource build = DataSourceBuilder.create().build();
        return build;
    }


    /**
     * 事务管理器
     */
    @Bean(name = "slaveTransactionManager")
    public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * session工厂
     */

    @Bean(name = "slaveSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(SlaveDataSourceConfig.MAPPER_LOCATION));
        return sessionFactoryBean.getObject();
    }

}

mapper层构建(其中mapper层分包存储)

// 主Mapper
package com.noob.framework.mapper.master;
@Mapper
public interface MasterUserMapper {
    public List<User> getAllUser();
}

// 主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.noob.framework.mapper.master.MasterUserMapper">

    <select id="getAllUser" resultType="com.noob.framework.model.User">
        select * from user
    </select>

</mapper>
// 从Mapper
package com.noob.framework.mapper.salve;
@Mapper
public interface SalveUserMapper {
    public List<User> getAllUser();
}

// 从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.noob.framework.mapper.salve.SalveUserMapper">

    <select id="getAllUser" resultType="com.noob.framework.model.User">
        select * from user
    </select>

</mapper>

service层构建(此处为了简化,只构建一个service用于测试)

public interface UserService {

    public List<User> getMasterUserList();

    public List<User> getSalveUserList();

}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private MasterUserMapper masterUserMapper;

    @Autowired
    private SalveUserMapper salveUserMapper;

    @Override
    public List<User> getMasterUserList() {
        return masterUserMapper.getAllUser();
    }

    @Override
    public List<User> getSalveUserList() {
        return salveUserMapper.getAllUser();
    }
}

controller层构建

@RequestMapping("/user")
@Controller
public class UserController {

    private static final Logger log = LoggerFactory.getLogger(UserController.class);
    @Autowired
    private UserService userService;

    @RequestMapping("/list")
    public void getAllUser(){
        // 分别调用不同数据源获取信息
        List<User> masterUserList = userService.getMasterUserList();
        log.info("主数据源:{}" , masterUserList.toString());
        List<User> salveUserList = userService.getSalveUserList();
        log.info("副数据源:{}" , salveUserList.toString());
    }

}

启动访问测试

访问URL
http://localhost:8080/user/list

// output  查看控制台
[nio-8080-exec-4] c.n.framework.controller.UserController  : 主数据源:[User(userId=44, userName=小龙)]
[nio-8080-exec-4] c.n.framework.controller.UserController  : 副数据源:[User(userId=44, userName=夏红), User(userId=45, userName=张宝)]
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3