java 动态 数据源_java springboot动态数据源配置

听说爱点赞的人都月瘦十斤,月入十万哟!

自从接触了JAVA,真的是每天都学到新知识,很具有挑战性,今天开发过程中,springboot项目需要连接mysql数据库和sql server数据库.通过查资料,终于完成了接口测试,记录一下,以便帮助更多的人.

通过百度查询 关键字:springboot 多数据源,给出的例子基本都是多套源策略,什么是多套源呢,看下图:

多套源策略1.png

这种策略虽然简单、直接、好理解,也符合开闭原则(再添加数据库,原来的数据库信息不用修改,只添加即可)

但是资源浪费,代码冗余,缺乏灵活。需要针对每一个数据源写一套操作,不推荐使用。

今天我来介绍使用AOP切面进行动态数据源配置。用户可以根据实际业务需要,统一操作逻辑,只需要在切换数据源的地方进行切换即可,超级方便。流程图如下:

动态数据源2.png

今天举例连接mysql数据库和sql server数据库

1,包结构说明

|--common

|--annotation //自定义注解

|--aop //切面

|--context //自定义

|--config //数据源配置

|--controller //访问接口

|--entity //实体类

|--mapper //数据库操作

|--service //服务类

|--impl //实现类

2,pom.xml 引入相关包。

其中sqlserver引入jar包比较特殊,不能直接maven引入,需要自己下载,并添加到本地maven默认地址,sqljdbc4:jar:4.0问题解决方案

com.baomidou

mybatis-plus-boot-starter

3.2.0

mysql

mysql-connector-java

runtime

5.1.47

com.microsoft.sqlserver

sqljdbc4

4.0

org.springframework.boot

spring-boot-starter-aop

3,接下来就是配置数据库信息了,在application.yml中添加

注:这里可以看出mysql和sqlserver的配置是不同的。数据库名引入不同。

mysql是端口号/book_test,

sqlserver是端口号;DatabaseName=project

spring:值对应

datasource:

master1:

driver-class-name: com.mysql.jdbc.Driver

jdbc-url: jdbc:mysql://localhost:3306/book_test?useUnicode=true&useSSL=false&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull

username: ***

password: ***

master2:

driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver

jdbc-url: jdbc:sqlserver://****:1433;DatabaseName=project

username: **

password: **

4,前期准备工作已完成,接下来进行动态数据源配置

4.1数据源配置:根据连接信息把数据源注入到spring中.config/DynamicDataSourceConfig.java

package com.springboot.test.config;

/**

* @date: Created in 2020/8/12 13:36

* @description: 动态数据源配置

* @version: 1.0

*/

//import me.mason.demo.dynamicdatasource.constants.DataSourceConstants;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.boot.jdbc.DataSourceBuilder;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

import java.util.HashMap;

import java.util.Map;

// 添加此配置,否则 报`The dependencies of some of the beans in the application context form a cycle`

@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })

@Configuration

@PropertySource("classpath:application.yml")

@MapperScan(basePackages = "com.springboot.test.mapper")

public class DynamicDataSourceConfig {

@Bean("master1")

@ConfigurationProperties(prefix = "spring.datasource.master1")

public DataSource master1DataSource() {

return DataSourceBuilder.create().build();

}

@Bean("master2")

@ConfigurationProperties(prefix = "spring.datasource.master2")

public DataSource master2DataSource() {

return DataSourceBuilder.create().build();

}

//设置动态数据源为主数据源

@Bean

@Primary

public DataSource dynamicDataSource() {

Map dataSourceMap = new HashMap<>(2);

dataSourceMap.put("master1", master1DataSource());

dataSourceMap.put("master2", master2DataSource());

//设置动态数据源

DynamicDataSource dynamicDataSource = new DynamicDataSource();

dynamicDataSource.setTargetDataSources(dataSourceMap);

dynamicDataSource.setDefaultTargetDataSource(master1DataSource());

return dynamicDataSource;

}

}

4.2 添加动态数据源类.config/DynamicDataSource.java

package com.springboot.test.config;

import com.springboot.test.common.context.DynamicDataSourceContextHolder;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**

* @date: Created in 2020/8/12 14:07

* @description: 动态数据源

* @version: 1.0

*/

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

return DynamicDataSourceContextHolder.getContextKey();

}

}

4.3动态返回数据源。contxt/DynamicDataSourceContextHolder.java

package com.springboot.test.common.context;

/**

* @date: Created in 2020/8/12 14:02

* @description: TODO

* @version: 1.0

*/

public class DynamicDataSourceContextHolder {

/**

* 动态数据源名称上下文

*/

private static final ThreadLocal DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();

/**

* 设置数据源

* @param key

*/

public static void setContextKey(String key){

System.out.println("切换数据源"+key);

DATASOURCE_CONTEXT_KEY_HOLDER.set(key);

}

/**

* 获取数据源名称

* @return

*/

public static String getContextKey(){

String key = DATASOURCE_CONTEXT_KEY_HOLDER.get();

return key == null?"master1":key;

}

/**

* 删除当前数据源名称

*/

public static void removeContextKey(){

DATASOURCE_CONTEXT_KEY_HOLDER.remove();

}

}

4.3 定义数据源注解,这里很关键哟,定义了注解,之后再server层就可以直接@注解使用了.common/annotation/DS.java。不要问为什么把注解定义为java,因为大家都这样做,DS也是DataSource的简称。

package com.springboot.test.common.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* @date: Created in 2020/8/12 13:55

* @description: TODO

* @version: 1.0

*/

@Target({ElementType.METHOD,ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface DS {

/**

* 数据源名称

* @return

*/

String value() default "master1";

}

4.4 定义切面

package com.springboot.test.common.aop;

import com.springboot.test.common.annotation.DS;

import com.springboot.test.common.context.DynamicDataSourceContextHolder;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.aspectj.lang.reflect.MethodSignature;

import org.springframework.stereotype.Component;

import java.util.Objects;

/**

* @date: Created in 2020/8/12 14:00

* @description: TODO

* @version: 1.0

*/

@Aspect

@Component

public class DynamicDataSourceAspect {

@Pointcut("@annotation(com.springboot.test.common.annotation.DS)")

public void dataSourcePointCut(){

}

@Around("dataSourcePointCut()")

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

String dsKey = getDSAnnotation(joinPoint).value();

DynamicDataSourceContextHolder.setContextKey(dsKey);

try{

return joinPoint.proceed();

}finally {

DynamicDataSourceContextHolder.removeContextKey();

}

}

/**

* 根据类或方法获取数据源注解

* @param joinPoint

* @return

*/

private DS getDSAnnotation(ProceedingJoinPoint joinPoint){

Class> targetClass = joinPoint.getTarget().getClass();

DS dsAnnotation = targetClass.getAnnotation(DS.class);

// 先判断类的注解,再判断方法注解

if(Objects.nonNull(dsAnnotation)){

return dsAnnotation;

}else{

MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();

return methodSignature.getMethod().getAnnotation(DS.class);

}

}

}

5,接下来就是service层使用AOP进行数据源切换了。

在service/impl/TestUserServiceImpl.java页面

/**

* 查询master1库User

* @return

*/

@DS("master1") //这个是自定义注解

public List getMasterUser(){

QueryWrapper queryWrapper = new QueryWrapper<>();

return testUserMapper.selectAll(queryWrapper.isNotNull("name"));

}

/**

* 查询master2库User

* @return

*/

@DS("master2") //这个是自定义注解

public List getSlaveUser(){

return testUserMapper.selectList(null);

}

controller层

/**

* 查询全部

*/

@GetMapping("/listall")

public Object listAll() {

int initSize = 2;

Map result = new HashMap<>(initSize);

//默认master数据源查询

List masterUser = testUserService.getMasterUser();

result.put("master1", masterUser);

//从slave数据源查询

List slaveUser = testUserService.getSlaveUser();

result.put("master2", slaveUser);

//返回数据

return ResponseResult.success(result);

}

看,是不是超级简单?没有数据库切换代码,只需要关注业务逻辑即可,省心好多。你学会了吗?

你可能感兴趣的:(java,动态,数据源)