博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot 动态数据源(yml配置,多数据源自动切换)
阅读量:6313 次
发布时间:2019-06-22

本文共 11311 字,大约阅读时间需要 37 分钟。

  hot3.png

1、在启动类添加注解,注册动态多数据源:

@SpringBootApplication@Import({DynamicDataSourceRegister.class}) // 注册动态多数据源public class OrangeplusApplication extends WebMvcConfigurerAdapter {    protected final static Logger logger = LoggerFactory.getLogger(OrangeplusApplication.class);	public static void main(String[] args) {		SpringApplication.run(OrangeplusApplication.class, args);        logger.info("Application is success!");	}}

2、yml配置文件中数据源配置内容为: 

spring:  profiles: dev  datasource:    url: jdbc:mysql://127.0.0.1:3306/orangeplus?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull    username: root    password: 123456    driver-class-name: com.mysql.jdbc.Driver# 更多数据源custom:  profiles: dev  datasource:    names: ds1,ds2    ds1:      driver-class-name: oracle.jdbc.driver.OracleDriver      url: jdbc:oracle:thin:@localhost:1521:oracle      username: root      password: orcl#ncu    ds2:      driver-class-name: com.mysql.jdbc.Driver      url: jdbc:mysql://localhost:3306/orangeplus2?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull      username: root      password: 123456

3、在需要的方法上使用注解指定数据源,简单代码示例:

@Servicepublic class StudentService {    @Autowired    private JdbcTemplate jdbcTemplate;    // MyBatis的Mapper方法定义接口    @Autowired    private StudentMapper studentMapper;    @TargetDataSource(name="ds2")    public List
likeName(String name){ return studentMapper.likeName(name); } public List
likeNameByDefaultDataSource(String name){ return studentMapper.likeName(name); } /** * 不指定数据源使用默认数据源 */ public List
getList(){ String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT"; return (List
) jdbcTemplate.query(sql, new RowMapper
(){ @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student stu = new Student(); stu.setId(rs.getInt("ID")); stu.setAge(rs.getInt("AGE")); stu.setName(rs.getString("NAME")); stu.setSumScore(rs.getString("SCORE_SUM")); stu.setAvgScore(rs.getString("SCORE_AVG")); return stu; } }); } /** * 指定数据源 */ @TargetDataSource(name="ds1") public List
getListByDs1(){ String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT"; return (List
) jdbcTemplate.query(sql, new RowMapper
(){ @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student stu = new Student(); stu.setId(rs.getInt("ID")); stu.setAge(rs.getInt("AGE")); stu.setName(rs.getString("NAME")); stu.setSumScore(rs.getString("SCORE_SUM")); stu.setAvgScore(rs.getString("SCORE_AVG")); return stu; } }); } /** * 指定数据源 */ @TargetDataSource(name="ds2") public List
getListByDs2(){ String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT"; return (List
) jdbcTemplate.query(sql, new RowMapper
(){ @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student stu = new Student(); stu.setId(rs.getInt("ID")); stu.setAge(rs.getInt("AGE")); stu.setName(rs.getString("NAME")); stu.setSumScore(rs.getString("SCORE_SUM")); stu.setAvgScore(rs.getString("SCORE_AVG")); return stu; } }); }}

要注意的是,在使用MyBatis时,注解@TargetDataSource 不能直接在接口类Mapper上使用。 

按上面的代码中StudentMapper为接口,实例代码如下:

public interface StudentMapper {    // 多数据源注解 @TargetDataSource 不可以在这里使用    List
likeName(String name); Student getById(int id); String getNameById(int id);}

请将下面几个类放到Spring Boot项目中:

DynamicDataSource.java 

public class DynamicDataSource extends AbstractRoutingDataSource {    @Override    protected Object determineCurrentLookupKey() {        return DynamicDataSourceContextHolder.getDataSourceType();    }}

DynamicDataSourceAspect.java 

@Aspect@Order(-1)// 保证该AOP在@Transactional之前执行@Componentpublic class DynamicDataSourceAspect {    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);    @Before("@annotation(ds)")    public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {        String dsId = ds.name();        if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {            logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());        } else {            logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());            DynamicDataSourceContextHolder.setDataSourceType(ds.name());        }    }    @After("@annotation(ds)")    public void restoreDataSource(JoinPoint point, TargetDataSource ds) {        logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());        DynamicDataSourceContextHolder.clearDataSourceType();    }}

DynamicDataSourceContextHolder.java 

public class DynamicDataSourceContextHolder {    private static final ThreadLocal
contextHolder = new ThreadLocal
(); public static List
dataSourceIds = new ArrayList<>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } /** * 判断指定DataSrouce当前是否存在 * * @param dataSourceId */ public static boolean containsDataSource(String dataSourceId){ return dataSourceIds.contains(dataSourceId); }}

DynamicDataSourceRegister.java 

/** * 动态数据源注册
* 启动动态数据源请在启动类中(如SpringBootSampleApplication) * 添加 @Import(DynamicDataSourceRegister.class) * */public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class); private ConversionService conversionService = new DefaultConversionService(); private PropertyValues dataSourcePropertyValues; // 如配置文件中未指定数据源类型,使用该默认值 private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource"; // private static final Object DATASOURCE_TYPE_DEFAULT = // "com.zaxxer.hikari.HikariDataSource"; // 数据源 private DataSource defaultDataSource; private Map
customDataSources = new HashMap<>(); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Map
targetDataSources = new HashMap
(); // 将主数据源添加到更多数据源中 targetDataSources.put("dataSource", defaultDataSource); DynamicDataSourceContextHolder.dataSourceIds.add("dataSource"); // 添加更多数据源 targetDataSources.putAll(customDataSources); for (String key : customDataSources.keySet()) { DynamicDataSourceContextHolder.dataSourceIds.add(key); } // 创建DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); mpv.addPropertyValue("targetDataSources", targetDataSources); registry.registerBeanDefinition("dataSource", beanDefinition); logger.info("Dynamic DataSource Registry"); } /** * 创建DataSource * * @param type * @param driverClassName * @param url * @param username * @param password * @return */ @SuppressWarnings("unchecked") public DataSource buildDataSource(Map
dsMap) { try { Object type = dsMap.get("type"); if (type == null) type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource Class
dataSourceType; dataSourceType = (Class
) Class.forName((String) type); String driverClassName = dsMap.get("driver-class-name").toString(); String url = dsMap.get("url").toString(); String username = dsMap.get("username").toString(); String password = dsMap.get("password").toString(); DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url) .username(username).password(password).type(dataSourceType); return factory.build(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 加载多数据源配置 */ @Override public void setEnvironment(Environment env) { initDefaultDataSource(env); initCustomDataSources(env); } /** * 初始化主数据源 * */ private void initDefaultDataSource(Environment env) { // 读取主数据源 RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource."); Map
dsMap = new HashMap<>(); dsMap.put("type", propertyResolver.getProperty("type")); dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name")); dsMap.put("url", propertyResolver.getProperty("url")); dsMap.put("username", propertyResolver.getProperty("username")); dsMap.put("password", propertyResolver.getProperty("password")); defaultDataSource = buildDataSource(dsMap); dataBinder(defaultDataSource, env); } /** * 为DataSource绑定更多数据 * * @param dataSource * @param env */ private void dataBinder(DataSource dataSource, Environment env){ RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource); //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext)); dataBinder.setConversionService(conversionService); dataBinder.setIgnoreNestedProperties(false);//false dataBinder.setIgnoreInvalidFields(false);//false dataBinder.setIgnoreUnknownFields(true);//true if(dataSourcePropertyValues == null){ Map
rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties("."); Map
values = new HashMap<>(rpr); // 排除已经设置的属性 values.remove("type"); values.remove("driver-class-name"); values.remove("url"); values.remove("username"); values.remove("password"); dataSourcePropertyValues = new MutablePropertyValues(values); } dataBinder.bind(dataSourcePropertyValues); } /** * 初始化更多数据源 * */ private void initCustomDataSources(Environment env) { // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源 RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource."); String dsPrefixs = propertyResolver.getProperty("names"); for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源 Map
dsMap = propertyResolver.getSubProperties(dsPrefix + "."); DataSource ds = buildDataSource(dsMap); customDataSources.put(dsPrefix, ds); dataBinder(ds, env); } }}

TargetDataSource.java

/** * 在方法上使用,用于指定使用哪个数据源 * */@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface TargetDataSource {    String name();}

 

转载于:https://my.oschina.net/ljc94/blog/1547474

你可能感兴趣的文章
打造一个上传图片到图床利器的插件(Mac版 开源)
查看>>
iOS横竖屏
查看>>
thinkphp判断更新是否成功
查看>>
Do While ... Loop 与 Do Until ... Loop 的区别
查看>>
【Linux】查询某个字符串出现次数
查看>>
高效使用jquery之一:请使用'On'函数
查看>>
冲刺第一周第三天
查看>>
ERP环境检测工具设计与实现 Environment Detection
查看>>
不要在构造中做太多事情,不然有时候会出现有意思的代码~
查看>>
IIS 发布网站遇到的问题
查看>>
NuGet学习笔记(2)——使用图形化界面打包自己的类库
查看>>
xcode中没有autoSizing的设置
查看>>
字符编码
查看>>
企业应用:应用层查询接口设计
查看>>
浅谈Excel开发:十 Excel 开发中与线程相关的若干问题
查看>>
nfd指令的详细说明
查看>>
安装VisualSvn Server时遇到的问题
查看>>
不用Visual Studio,5分钟轻松实现一张报表
查看>>
人脸识别 开放书籍 下载地址
查看>>
Notepad++配置Python开发环境
查看>>