springboot整合多数据源

1:整体项目结构
在这里插入图片描述
2:pom.xml
在这里插入图片描述
在这里插入图片描述
3:application.yml配置文件
server:
port: 8080
spring:
datasource:
master:
driverClassName: com.mysql.jdbc.Driver
username: root
password: 123456
jdbc-url: jdbc:mysql://127.0.0.1:3306/master?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
type: com.alibaba.druid.pool.DruidDataSource
slave:
driverClassName: com.mysql.jdbc.Driver
username: root
password: 123456
jdbc-url: jdbc:mysql://127.0.0.1:3306/slave1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:mapper/*.xml
在这里插入图片描述
4:controller层
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private IUserService userService;

// http://127.0.0.1:8080/users/1
@DataSource("master")
@GetMapping("/{id}")
public UserVO selectByPrimaryKey(@PathVariable("id") int id) {
	return userService.selectByPrimaryKey(id);
}

// http://127.0.0.1:8080/users
@DataSource("slave1")
@GetMapping("")
public List<UserVO> selectAll() {
	return userService.selectAll();
}

}
5:Service层
public interface IUserService {
UserVO selectByPrimaryKey(int id);

List<UserVO> selectAll();

}
@Service
public class UserService implements IUserService {

@Autowired
private UserDao userDao;

@Override
public UserVO selectByPrimaryKey(int id) {
	return userDao.selectByPrimaryKey(id);
}

@Override
public List<UserVO> selectAll() {
	return userDao.selectAll();
}

}
6:Dao层
@Mapper
public interface UserDao {
UserVO selectByPrimaryKey(int id);

List<UserVO> selectAll();

}
7 xml省略;
8:配置类:
a:注解类
@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default “master”; // 该值即key值
}
b:切面类
@Aspect
@Component
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);

@Before("@annotation(ds)")
public void changeDataSource(JoinPoint point, DataSource ds) throws Throwable {
	String dsId = ds.value();
	if (DynamicDataSourceContextHolder.dataSourceIds.contains(dsId)) {
		DynamicDataSourceContextHolder.setDataSourceRouterKey(dsId);
		logger.debug("Use DataSource :{} >", dsId, point.getSignature());
	} else {
		logger.info("数据源[{}]不存在,使用默认数据源 >{}", dsId, point.getSignature());
		DynamicDataSourceContextHolder.setDataSourceRouterKey(dsId);
	}
}

@After("@annotation(ds)")
public void restoreDataSource(JoinPoint point, DataSource ds) {
	logger.debug("Revert DataSource : " + ds.value() + " > " + point.getSignature());
	DynamicDataSourceContextHolder.removeDataSourceRouterKey();

}

}
c:动态数据源类
public class DynamicDataSourceContextHolder {
private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

/**
 * 存储已经注册的数据源的key
 */
public static List<String> dataSourceIds = new ArrayList<>();

/**
 * 线程级别的私有变量
 */
private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();

public static String getDataSourceRouterKey() {
	return HOLDER.get();
}

public static void setDataSourceRouterKey(String dataSourceRouterKey) {
	logger.info("切换至{}数据源", dataSourceRouterKey);
	HOLDER.set(dataSourceRouterKey);
}

/**
 * 设置数据源之前一定要先移除
 */
public static void removeDataSourceRouterKey() {
	HOLDER.remove();
}

/**
 * 判断指定DataSrouce当前是否存在
 *
 * @param dataSourceId
 * @return
 */
public static boolean containsDataSource(String dataSourceId) {
	return dataSourceIds.contains(dataSourceId);
}

}

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

private static Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);

@Override
protected Object determineCurrentLookupKey() {
	String dataSourceName = DynamicDataSourceContextHolder.getDataSourceRouterKey();
	logger.info("当前数据源是:{}", dataSourceName);
	return DynamicDataSourceContextHolder.getDataSourceRouterKey();
}

}

/**

  • 动态数据源注册 实现 ImportBeanDefinitionRegistrar 实现数据源注册 实现 EnvironmentAware

  • 用于读取application.yml配置
    */
    public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);

    public static DataSource defaultDataSource;

    /**

    • 存储我们注册的数据源
      */
      private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();

    /**

    • ImportBeanDefinitionRegistrar接口的实现方法,通过该方法可以按照自己的方式注册bean
    • @param annotationMetadata
    • @param beanDefinitionRegistry
      */
      @Override
      public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,
      BeanDefinitionRegistry beanDefinitionRegistry) {
      DynamicDataSourceContextHolder.dataSourceIds.add(“master”);
      logger.info(“注册默认数据源成功”);
      // 获取其他数据源配置
      Set keySet = customDataSources.keySet();
      for (String key : keySet) {
      DynamicDataSourceContextHolder.dataSourceIds.add(key);
      logger.info(“注册数据源{}成功”, key);
      }
      // bean定义类
      GenericBeanDefinition define = new GenericBeanDefinition();
      // 设置bean的类型,此处DynamicRoutingDataSource是继承AbstractRoutingDataSource的实现类
      define.setBeanClass(DynamicRoutingDataSource.class);
      // 需要注入的参数
      MutablePropertyValues mpv = define.getPropertyValues();
      // 添加默认数据源,避免key不存在的情况没有数据源可用
      mpv.add(“defaultTargetDataSource”, defaultDataSource);
      // 添加其他数据源
      mpv.add(“targetDataSources”, customDataSources);
      // 将该bean注册为datasource,不使用springboot自动生成的datasource
      beanDefinitionRegistry.registerBeanDefinition(“datasource”, define);
      logger.info(“注册数据源成功,一共注册{}个数据源”, customDataSources.keySet().size() + 1);
      }

    /**

    • EnvironmentAware接口的实现方法,通过aware的方式注入,此处是environment对象

    • @param environment
      */
      @Override
      public void setEnvironment(Environment environment) {
      logger.info(“开始注册数据源”);
      try {
      RelaxedPropertyResolver re = new RelaxedPropertyResolver(environment, “spring.datasource.master.”);
      Map<String, String> map = new HashMap<>();
      map.put(“url”, re.getProperty(“jdbc-url”));
      map.put(“username”, re.getProperty(“username”));
      map.put(“password”, re.getProperty(“password”));
      map.put(“type”, re.getProperty(“type”));
      map.put(“driverClassName”, re.getProperty(“driver-class-name”));
      defaultDataSource = buildDataSource(map);

       re = new RelaxedPropertyResolver(environment, "spring.datasource.slave.");
       map.put("url", re.getProperty("jdbc-url"));
       map.put("username", re.getProperty("username"));
       map.put("password", re.getProperty("password"));
       map.put("type", re.getProperty("type"));
       map.put("driverClassName", re.getProperty("driver-class-name"));
       DataSource slave1DataSource = buildDataSource(map);
       customDataSources.put("slave1", slave1DataSource);
      

      } catch (ClassNotFoundException e) {
      e.printStackTrace();
      }
      }

    private DataSource buildDataSource(Map<String, String> map) throws ClassNotFoundException {
    String url = map.get(“url”).toString();
    String username = map.get(“username”).toString();
    String password = map.get(“password”).toString();
    String driverClassName = map.get(“driverClassName”).toString();
    String type = map.get(“type”).toString();
    DataSourceBuilder sourceBuilder = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
    .username(username).password(password).type((Class<? extends DataSource>) Class.forName(type));
    return sourceBuilder.build();

    }
    }

至此,启动测试:
在这里插入图片描述
数据库中在这里插入图片描述
跟controller中
@DataSource(“master”)
@GetMapping("/{id}")
public UserVO selectByPrimaryKey(@PathVariable(“id”) int id) {
return userService.selectByPrimaryKey(id);
}
的确是请求到了master库;

在这里插入图片描述
在这里插入图片描述
@DataSource(“slave1”)
@GetMapping("")
public List selectAll() {
return userService.selectAll();
}
的确是请求到了slave1库;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值