0


【SpringBoot】多数据源N种实现详解

一、概述

1.1 业务场景

  1. 1,数据库的读写分类
  2. 2SAAS服务多数据源
  3. 3,由于数据较大,要分库分表

1.2 主要的实现方式

  1. 1)、使用分包方式,不同的数据源配置不同的MapperScanmapper文件
  2. 2)、使用AOP切片方式,实现动态数据源切换,AbstractRoutingDataSource
  3. 3)、使用数据库代理中间件,如MycatshardingJDBC

1.3 不同方式之间的区别

  1. 分包方式可以集合JTA(JAVA Transactional API)实现分布式事务,但是整个流程的实现相对来说比较复杂。
  2. AOP动态配置数据源方式缺点在于无法实现全局分布式事务,所以如果只是对接第三方数据源,不涉及到需要保证分布式事务的话,是可以作为一种选择。
  3. 使用数据库代理中间件方式是现在比较流行的一种方式,很多大厂也是使用这种方式,开发者不需要关注太多与业务无关的问题,把它们都交给数据库代理中间件去处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,比较常见的有Mycat、TDDL等。现在阿里出了100%自研的分布式数据库OceanBase,从最底层支持分布式,性能也非常强大,大家感兴趣的可以去了解下!

1.4 原理如下

一个spring和Mybatis的框架的项目中,我们在spring配置中往往是配置一个dataSource来连接数据库,然后绑定给sessionFactory,在dao层代码中再指定sessionFactory来进行数据库操作。

正如上图所示,每一块都是指定绑死的,如果是多个数据源,也只能是下图中那种方式。

可看出在Dao层代码中写死了两个SessionFactory,这样日后如果再多一个数据源,还要改代码添加一个SessionFactory,显然这并不符合开闭原则。

二、多种实现方式

2.1 【分包方式】实现简单的多数据源整合

主要是MyBatis框架分包方式实现

2.1.1 配置两个数据源配置

  1. # 项目启动端口
  2. server:
  3. port: 9090
  4. # 项目 名称
  5. spring:
  6. application:
  7. name: multi-datasource-instance
  8. datasource:
  9. # 主数据库
  10. master:
  11. # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
  12. jdbc-url: jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
  13. username: root
  14. password: 123456
  15. driver-class-name: com.mysql.cj.jdbc.Driver
  16. # 副数据库
  17. slave:
  18. # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
  19. jdbc-url: jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
  20. username: root
  21. password: 123456
  22. driver-class-name: com.mysql.cj.jdbc.Driver

2.1.2 编写主副数据库数据源配置

主数据源相关配置:主要是指定主数据源、扫描的mapper地址、事务管理器等信息。

  1. @Configuration
  2. // 指定主数据库扫描对应的Mapper文件,生成代理对象
  3. @MapperScan(basePackages ="com.diary.it.multi.datasource.mapper" ,sqlSessionFactoryRef = "masterSqlSessionFactory")
  4. public class MasterDataSourceConfig {
  5. // mapper.xml所在地址
  6. private static final String MAPPER_LOCATION = "classpath*:mapper/*.xml";
  7. /**
  8. * 主数据源,Primary注解必须增加,它表示该数据源为默认数据源
  9. * 项目中还可能存在其他的数据源,如获取时不指定名称,则默认获取这个数据源,如果不添加,则启动时候回报错
  10. */
  11. @Primary
  12. @Bean(name = "masterDataSource")
  13. // 读取spring.datasource.master前缀的配置文件映射成对应的配置对象
  14. @ConfigurationProperties(prefix = "spring.datasource.master")
  15. public DataSource dataSource() {
  16. DataSource build = DataSourceBuilder.create().build();
  17. return build;
  18. }
  19. /**
  20. * 事务管理器,Primary注解作用同上
  21. */
  22. @Bean(name = "masterTransactionManager")
  23. @Primary
  24. public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
  25. return new DataSourceTransactionManager(dataSource);
  26. }
  27. /**
  28. * session工厂,Primary注解作用同上
  29. */
  30. @Bean(name = "masterSqlSessionFactory")
  31. @Primary
  32. public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
  33. final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
  34. sessionFactoryBean.setDataSource(dataSource);
  35. sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));
  36. return sessionFactoryBean.getObject();
  37. }
  38. }

2.1.3 副数据源相关配置

主要是指定数据源、扫描的mapper地址、事务管理器等信息。

  1. @Configuration
  2. // 指定从数据库扫描对应的Mapper文件,生成代理对象
  3. @MapperScan(basePackages = "com.diary.it.multi.datasource.mapper2", sqlSessionFactoryRef = "slaveSqlSessionFactory")
  4. public class SlaveDataSourceConfig {
  5. // mapper.xml所在地址
  6. private static final String MAPPER_LOCATION = "classpath*:mapper2/*.xml";
  7. /**
  8. * 数据源
  9. */
  10. @Bean(name = "slaveDataSource")
  11. // 读取spring.datasource.slave前缀的配置文件映射成对应的配置对象
  12. @ConfigurationProperties(prefix = "spring.datasource.slave")
  13. public DataSource dataSource() {
  14. DataSource build = DataSourceBuilder.create().build();
  15. return build;
  16. }
  17. /**
  18. * 事务管理器
  19. */
  20. @Bean(name = "slaveTransactionManager")
  21. public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
  22. return new DataSourceTransactionManager(dataSource);
  23. }
  24. /**
  25. * session工厂
  26. */
  27. @Bean(name = "slaveSqlSessionFactory")
  28. public SqlSessionFactory sqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
  29. final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
  30. sessionFactoryBean.setDataSource(dataSource);
  31. sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(SlaveDataSourceConfig.MAPPER_LOCATION));
  32. return sessionFactoryBean.getObject();
  33. }
  34. }

2.1.4 UserController层

  1. /** *控制层 */
  2. @RestController
  3. public class UserController {
  4. @Autowired
  5. private UserService userService;
  6. /**
  7. * 通过用户名获取用户信息,以及从库的地址信息
  8. */
  9. @RequestMapping(value = "/user",method = RequestMethod.GET)
  10. public User findUserByName(@RequestParam(value = "name",required = true) String name){
  11. return userService.findByName(name);
  12. }
  13. }

2.1.5 Service层

照常注入主从两个Dao层

  1. @Service
  2. public class UserService {
  3. @Autowired
  4. private UserDao userDao;
  5. // 主数据源
  6. @Autowired
  7. private CityDao cityDao;
  8. // 从数据源
  9. public User findByName(String userName) {
  10. User user = userDao.findByName(userName);
  11. City city = cityDao.findByName("上海市");
  12. System.out.println(city);
  13. user.setDescription("从数据库->"+city.getDescription());
  14. user.setCity(city);
  15. return user;
  16. }
  17. }

2.2 AbstractRoutingDataSource AOP 方式实现

AbstrictRoutingDataSource的本质就是利用一个Map将数据源存储起来,然后通过Key来得到Value来修改数据源。

2.2.1 主要步骤

在 SpringBoot 项目中实现读写分离通常需要以下几步:

  1. 配置数据源:你需要为读操作和写操作分别配置一个数据源。
  2. 创建数据源路由逻辑:这通常通过扩展 Spring 的 AbstractRoutingDataSource 来实现。它允许你根据一定的逻辑来决定使用哪个数据源(读或写)。
  3. 配置事务管理器:这使得你能够在使用不同数据源时保持事务的一致性。
  4. 服务层或DAO层设计:确保在执行读操作时使用读数据源,在执行写操作时使用写数据源。
  5. 自定义切面,在切面中解析 @DataSource 注解。当一个方法或者类上面,有 @DataSource 注解的时候,将 @DataSource 注解所标记的数据源列出来存入到 ThreadLocal 中。

注意:这里使用ThreadLocal的原因是为了保证我们的线程安全。

2.2.2 配置文件类

  1. spring:
  2. datasource:
  3. local:
  4. username: root
  5. password: 123456
  6. driver-class-name: com.mysql.jdbc.Driver
  7. jdbc-url: jdbc:mysql://localhost:3306/demo?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
  8. remote:
  9. username: root
  10. password: 123456
  11. driver-class-name: com.mysql.jdbc.Driver
  12. jdbc-url: jdbc:mysql://192.168.85.111:3306/demo?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8

2.2.3 创建数据源枚举类

  1. package com.mashibing.mult;
  2. public enum DataSourceType {
  3. REMOTE,
  4. LOCAL
  5. }

2.2.4 数据源切换处理

  1. package com.mashibing.mult;
  2. public class DynamicDataSourceContextHolder {
  3. /**
  4. * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
  5. * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
  6. */
  7. private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
  8. /**
  9. * 设置数据源变量
  10. * @param dataSourceType
  11. */
  12. public static void setDataSourceType(String dataSourceType){
  13. System.out.printf("切换到{%s}数据源", dataSourceType);
  14. CONTEXT_HOLDER.set(dataSourceType);
  15. }
  16. /**
  17. * 获取数据源变量
  18. * @return
  19. */
  20. public static String getDataSourceType(){
  21. return CONTEXT_HOLDER.get();
  22. }
  23. /**
  24. * 清空数据源变量
  25. */
  26. public static void clearDataSourceType(){
  27. CONTEXT_HOLDER.remove();
  28. }
  29. }
  1. package com.mashibing.mult;
  2. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  3. import javax.sql.DataSource;
  4. import java.util.Map;
  5. public class DynamicDataSource extends AbstractRoutingDataSource {
  6. public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
  7. super.setDefaultTargetDataSource(defaultTargetDataSource);
  8. super.setTargetDataSources(targetDataSources);
  9. // afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
  10. //afterPropertiesSet 也可以重修此方法
  11. super.afterPropertiesSet();
  12. }
  13. /**
  14. * 根据Key获取数据源的信息
  15. *
  16. * @return
  17. */
  18. @Override
  19. protected Object determineCurrentLookupKey() {
  20. return DynamicDataSourceContextHolder.getDataSourceType();
  21. }
  22. }

2.2.5 注入数据源

  1. package com.mashibing.mult;
  2. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.boot.jdbc.DataSourceBuilder;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.context.annotation.Primary;
  8. import javax.sql.DataSource;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. @Configuration
  12. public class DataSourceConfig {
  13. @Bean
  14. @ConfigurationProperties("spring.datasource.remote")
  15. public DataSource remoteDataSource() {
  16. return DataSourceBuilder.create().build();
  17. }
  18. @Bean
  19. @ConfigurationProperties("spring.datasource.local")
  20. public DataSource localDataSource() {
  21. return DataSourceBuilder.create().build();
  22. }
  23. @Bean(name = "dynamicDataSource")
  24. @Primary
  25. public DynamicDataSource dataSource(DataSource remoteDataSource, DataSource localDataSource) {
  26. Map<Object, Object> targetDataSources = new HashMap<>();
  27. targetDataSources.put(DataSourceType.REMOTE.name(), remoteDataSource);
  28. targetDataSources.put(DataSourceType.LOCAL.name(), localDataSource);
  29. return new DynamicDataSource(remoteDataSource, targetDataSources);
  30. }
  31. }

2.2.6 自定义多数据源切换注解

  1. package com.mashibing.mult;
  2. import java.lang.annotation.*;
  3. @Target(ElementType.METHOD)
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Documented
  6. public @interface DataSource {
  7. /**
  8. * 切换数据源名称
  9. */
  10. DataSourceType value() default DataSourceType.REMOTE;
  11. }

2.2.7 AOP拦截类的实现

  1. package com.mashibing.mult;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.aspectj.lang.reflect.MethodSignature;
  7. import org.springframework.core.annotation.Order;
  8. import org.springframework.stereotype.Component;
  9. import java.lang.reflect.Method;
  10. @Aspect
  11. @Order(1)
  12. @Component
  13. public class DataSourceAspect {
  14. @Pointcut("@annotation(com.mashibing.mult.DataSource)")
  15. public void dsPointCut() {
  16. }
  17. @Around("dsPointCut()")
  18. public Object around(ProceedingJoinPoint point) throws Throwable {
  19. MethodSignature signature = (MethodSignature) point.getSignature();
  20. Method method = signature.getMethod();
  21. DataSource dataSource = method.getAnnotation(DataSource.class);
  22. if (dataSource != null) {
  23. DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
  24. }
  25. try {
  26. return point.proceed();
  27. } finally {
  28. // 销毁数据源 在执行方法之后
  29. DynamicDataSourceContextHolder.clearDataSourceType();
  30. }
  31. }
  32. }

2.2.8 使用切换数据源注解

  1. package com.mashibing.mult;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.jdbc.core.JdbcTemplate;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import java.util.List;
  7. import java.util.Map;
  8. @RestController
  9. public class EmpController {
  10. @Autowired
  11. JdbcTemplate jdbcTemplate;
  12. @GetMapping("/local")
  13. @DataSource(value = DataSourceType.LOCAL)
  14. public List<Map<String, Object>> local(){
  15. List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from emp");
  16. return maps;
  17. }
  18. @GetMapping("/remote")
  19. @DataSource(value = DataSourceType.REMOTE)
  20. public List<Map<String, Object>> remote(){
  21. List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from emp");
  22. return maps;
  23. }
  24. }

2.3 Dynamic-datasource

MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

使用MP提供的Dynamic-datasource多数据源框架,实现在不同数据源间切换,通过@DS注解实现对master和slave数据库的选择,展示了从配置、持久层到控制层的完整代码示例。

dynamic-datasource特性

  • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  • 支持数据库敏感配置信息 加密 ENC()。
  • 支持每个数据库独立初始化表结构schema和数据库database。
  • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
  • 支持 自定义注解 ,需继承DS(3.2.0+)。
  • 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
  • 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
  • 提供 自定义数据源来源 方案(如全从数据库加载)。
  • 提供项目启动后 动态增加移除数据源 方案。
  • 提供Mybatis环境下的 纯读写分离 方案。
  • 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
  • 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
  • 提供 基于seata的分布式事务方案。
  • 提供 本地多数据源事务方案。 附:不能和原生spring事务混用

2.3.1 引入依赖

  1. <!-- 多数据切换所需依赖 -->
  2. <dependency>
  3. <groupId>com.baomidou</groupId>
  4. <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  5. <version>3.0.0</version>
  6. </dependency>

2.3.2 配置文件

  1. sever:
  2. # 端口
  3. port: 8080
  4. # 配置数据源
  5. spring:
  6. datasource:
  7. dynamic:
  8. primary: master #设置默认的数据源或者数据源组,默认值即为master
  9. strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
  10. datasource:
  11. master:
  12. # 数据库路径jdbc:mysql://localhost:3306/mydb 的缩写,并配置时区
  13. url: jdbc:mysql:///mydb?serverTimezone=GMT%2B8
  14. username: root
  15. password: 123456
  16. driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
  17. slave:
  18. url: jdbc:mysql:///mydb2?serverTimezone=GMT%2B8
  19. username: root
  20. password: 123456
  21. driver-class-name: com.mysql.cj.jdbc.Driver
  22. # 打印MyBatis SQL 日志
  23. logging:
  24. level:
  25. com.guqueyue.test.dao: debug # 写接口的包名

注意:

这里数据源的名字可以自定义,什么master、slave都可以随意命名。
这里可以根据格式自行添加数据源的个数,并且支持多种数据库,如oracle、达梦等。此处为了演示方便,都使用了mysql数据库。

2.3.3 dao层

  1. package com.guqueyue.test.dao;
  2. import com.baomidou.dynamic.datasource.annotation.DS;
  3. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4. import com.guqueyue.test.entity.User;
  5. import org.apache.ibatis.annotations.Select;
  6. import java.util.List;
  7. /**
  8. * @Author: guqueyue
  9. * @Description: 映射接口UserMapper
  10. * @Date: 2023/12/19
  11. **/
  12. public interface UserMapper extends BaseMapper<User> {
  13. @DS("master") // 默认为主数据源,其实可以省略
  14. @Select("select * from users")
  15. List<User> selectUserList();
  16. @DS("slave") // 切换为slave数据源
  17. @Select("select * from users")
  18. List<User> selectUserListBySlave();
  19. }

注意:

  1. @DS注解不止可以用在持久层,可以用在任意的类和方法上。
  2. @DS注解作用在方法上的优先级 > 类。

也就是说可以在类上加一个

  1. @DS

注解默认一个该类的数据源,如

  1. @DS("master")


再在具体的方法上加一个

  1. @DS

注解做个性化指定,如

  1. @DS("slave")

,效果等同:

  1. /**
  2. * @Author: guqueyue
  3. * @Description: 映射接口UserMapper
  4. * @Date: 2023/12/19
  5. **/
  6. @DS("master") // 默认为主数据源,其实可以省略
  7. public interface UserMapper extends BaseMapper<User> {
  8. @Select("select * from users")
  9. List<User> selectUserList();
  10. @DS("slave") // 切换为slave数据源
  11. @Select("select * from users")
  12. List<User> selectUserListBySlave();
  13. }

2.3.4 创建service

  1. package com.guqueyue.test.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.guqueyue.test.entity.User;
  4. import java.util.List;
  5. /**
  6. * @Author: guqueyue
  7. * @Description: 用户service接口
  8. * @Date: 2023/12/19
  9. **/
  10. public interface IUserService extends IService<User> {
  11. List<User> selectUserList(String type);
  12. }

创建service实现类,并调用持久层接口,根据传入的参数切换不同的方法:

  1. package com.guqueyue.test.service.impl;
  2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  3. import com.guqueyue.test.dao.UserMapper;
  4. import com.guqueyue.test.entity.User;
  5. import com.guqueyue.test.service.IUserService;
  6. import org.springframework.stereotype.Service;
  7. import javax.annotation.Resource;
  8. import java.util.List;
  9. /**
  10. * @Author: guqueyue
  11. * @Description: 用户实现类
  12. * @Date: 2023/12/19
  13. **/
  14. @Service
  15. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
  16. @Resource
  17. private UserMapper userMapper;
  18. @Override
  19. public List<User> selectUserList(String type) {
  20. // do something: 此处可根据实际情况进行业务选择,如根据当前登录的用户信息来选择不同的数据库
  21. // 此处为了方便演示,直接用前端传入的type来判断
  22. return "slave".equals(type) ? userMapper.selectUserListBySlave()
  23. : userMapper.selectUserList();
  24. }
  25. }

2.3.5 .编写控制层代码

  1. package com.guqueyue.test.controller;
  2. import com.guqueyue.test.entity.User;
  3. import com.guqueyue.test.service.IUserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import java.util.List;
  8. /**
  9. * @Author: guqueyue
  10. * @Description: 用户控制层
  11. * @Date: 2023/12/19
  12. **/
  13. @RestController
  14. @RequestMapping("/user")
  15. public class UserController {
  16. @Autowired
  17. private IUserService userService;
  18. /**
  19. * 查询用户列表
  20. * @return
  21. */
  22. @RequestMapping("/list")
  23. public List<User> userList(String type) {
  24. System.out.println("接收到的数据源类型为:" + type);
  25. return userService.selectUserList(type);
  26. }
  27. }

2.4 JdbcTemplate

jdbcTemplate连接数据库就是用jdbcTemplate对象去调用它的query、udate、insert等方法操作数据库。
jdbcTemplate必须有dao层实现类,因为jdbcTemplate是在dao层用jdbcTemplate对应的方法操作sql语句的

2.4.1 配置文件

  1. application.properties

  1. application.yml

中配置两个数据源的基本属性:

  1. # 数据源1配置
  2. spring.datasource1.url=jdbc:mysql://localhost:3306/db1
  3. spring.datasource1.username=user1
  4. spring.datasource1.password=pass1
  5. spring.datasource1.driver-class-name=com.mysql.cj.jdbc.Driver
  6. # 数据源2配置
  7. spring.datasource2.url=jdbc:mysql://localhost:3306/db2
  8. spring.datasource2.username=user2
  9. spring.datasource2.password=pass2
  10. spring.datasource2.driver-class-name=com.mysql.cj.jdbc.Driver

创建配置类来定义两个数据源的Bean和对应的

  1. JdbcTemplate

  1. import org.springframework.boot.context.properties.ConfigurationProperties;
  2. import org.springframework.boot.jdbc.DataSourceBuilder;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.context.annotation.Primary;
  6. import org.springframework.jdbc.core.JdbcTemplate;
  7. import javax.sql.DataSource;
  8. @Configuration
  9. public class DataSourceConfig {
  10. @Primary
  11. @Bean(name = "primaryDataSource")
  12. @ConfigurationProperties(prefix = "spring.datasource1")
  13. public DataSource primaryDataSource() {
  14. return DataSourceBuilder.create().build();
  15. }
  16. @Bean(name = "secondaryDataSource")
  17. @ConfigurationProperties(prefix = "spring.datasource2")
  18. public DataSource secondaryDataSource() {
  19. return DataSourceBuilder.create().build();
  20. }
  21. @Bean(name = "primaryJdbcTemplate")
  22. public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) {
  23. return new JdbcTemplate(dataSource);
  24. }
  25. @Bean(name = "secondaryJdbcTemplate")
  26. public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) {
  27. return new JdbcTemplate(dataSource);
  28. }
  29. }

2.4.2 定义数据库表对应的实体类

  1. package com.qf.entity;
  2. import java.util.Date;
  3. public class Users {
  4. private Integer userId;
  5. private String username;
  6. private Date birthday;
  7. private Integer age;
  8. public String getUsername() {
  9. return username;
  10. }
  11. public void setUsername(String username) {
  12. this.username = username;
  13. }
  14. public Date getBirthday() {
  15. return birthday;
  16. }
  17. public void setBirthday(Date birthday) {
  18. this.birthday = birthday;
  19. }
  20. public Integer getUserId() {
  21. return userId;
  22. }
  23. public void setUserId(Integer userId) {
  24. this.userId = userId;
  25. }
  26. public Integer getAge() {
  27. return age;
  28. }
  29. public void setAge(Integer age) {
  30. this.age = age;
  31. }
  32. @Override
  33. public String toString() {
  34. return "Users{" +
  35. "userId=" + userId +
  36. ", username='" + username + '\'' +
  37. ", birthday=" + birthday +
  38. ", age=" + age +
  39. '}';
  40. }
  41. }

2.4.3 dao层

  1. package com.qf.dao;
  2. import com.qf.entity.Users;
  3. import java.util.List;
  4. //mybatis框架操作数据库有两种方式,一种是配置文件方式,一种是注解方式
  5. public interface UsersDao {
  6. public List<Users> getall();
  7. }
  1. package com.qf.dao.impl;
  2. import com.qf.dao.UsersDao;
  3. import com.qf.entity.Users;
  4. import org.springframework.jdbc.core.JdbcTemplate;
  5. import org.springframework.jdbc.core.RowMapper;
  6. import org.springframework.stereotype.Repository;
  7. import javax.annotation.Resource;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. import java.util.List;
  11. @Repository//Repository给当前类创建对象
  12. public class UsersDaoImpl implements UsersDao {
  13. @Resource(name = "onetemplate")
  14. private JdbcTemplate jdbcTemplate;
  15. //@Autowired
  16. //@Qualifier("db2JdbcTemplate")
  17. //private JdbcTemplate jdbcTemplate;
  18. @Override
  19. public List<Users> getall() {
  20. List<Users> userList = jdbcTemplate.query("SELECT * FROM users", new RowMapper<Users>() {
  21. @Override
  22. public Users mapRow(ResultSet resultSet, int i) throws SQLException {
  23. Users users = new Users();
  24. users.setAge(resultSet.getInt("age"));//第二个age是实体类Users的age属性对应的表的字段名。反正第二个age是字段名,第一个age是实体类属性
  25. users.setBirthday(resultSet.getDate("birthday"));
  26. users.setUsername(resultSet.getString("username"));
  27. users.setUserId(resultSet.getInt("userId"));
  28. return users;
  29. }
  30. });
  31. return userList;
  32. }
  33. }

2.4.4 service层

  1. package com.qf.service;
  2. import com.qf.entity.Users;
  3. import java.util.List;
  4. public interface UsersService {
  5. public List<Users> getall();
  6. }
  1. package com.qf.service.impl;
  2. import com.qf.dao.UsersDao;
  3. import com.qf.entity.Users;
  4. import com.qf.service.UsersService;
  5. import org.springframework.stereotype.Service;
  6. import javax.annotation.Resource;
  7. import java.util.List;
  8. @Service
  9. public class UsersServiceImpl implements UsersService{
  10. @Resource
  11. private UsersDao usersDao;
  12. @Override
  13. public List<Users> getall() {
  14. return usersDao.getall();
  15. }
  16. }

2.4.5 控制曾代码

  1. package com.qf.controller;
  2. import com.github.pagehelper.PageHelper;
  3. import com.github.pagehelper.PageInfo;
  4. import com.qf.entity.Users;
  5. import com.qf.service.UsersService;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.ui.ModelMap;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RequestParam;
  10. import javax.annotation.Resource;
  11. import java.util.List;
  12. @Controller
  13. public class TestController {
  14. @Resource
  15. private UsersService usersService;
  16. @RequestMapping("/test")//访问此请求的地址是localhost:8080/test
  17. public String test( ModelMap map){
  18. System.out.println("testjsp-----------");
  19. List<Users> usersList = usersService.getall();
  20. System.out.println("userList:"+usersList);
  21. map.addAttribute("usersList",usersList);
  22. return "show";//此时实际跳去的页面时/show.jsp.
  23. }
  24. }

本文转载自: https://blog.csdn.net/zhyooo123/article/details/143417345
版权归原作者 常生果 所有, 如有侵权,请联系我们删除。

“【SpringBoot】多数据源N种实现详解”的评论:

还没有评论