日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Boot 动态数据源(Spring 注解数据源)

發布時間:2023/12/15 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Boot 动态数据源(Spring 注解数据源) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文實現案例場景:
某系統除了需要從自己的主要數據庫上讀取和管理數據外,還有一部分業務涉及到其他多個數據庫,要求可以在任何方法上可以靈活指定具體要操作的數據庫。

為了在開發中以最簡單的方法使用,本文基于注解和AOP的方法實現,在spring boot框架的項目中,添加本文實現的代碼類后,只需要配置好數據源就可以直接通過注解使用,簡單方便。

一配置二使用
1. 啟動類注冊動態數據源
2. 配置文件中配置多個數據源
3. 在需要的方法上使用注解指定數據源

1、在啟動類添加 @Import({DynamicDataSourceRegister.class, MProxyTransactionManagementConfiguration.class})

@SpringBootApplication @Import({DynamicDataSourceRegister.class}) // 注冊動態多數據源 public class SpringBootSampleApplication {// 省略其他代碼 }

2、配置文件配置內容為:
(不包括項目中的其他配置,這里只是數據源相關的)

# 主數據源,默認的 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=123456# 更多數據源 custom.datasource.names=ds1,ds2 custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1 custom.datasource.ds1.username=root custom.datasource.ds1.password=123456custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test2 custom.datasource.ds2.username=root custom.datasource.ds2.password=123456

3、使用方法

package org.springboot.sample.service;import java.sql.ResultSet; import java.sql.SQLException; import java.util.List;import org.springboot.sample.datasource.TargetDataSource; import org.springboot.sample.entity.Student; import org.springboot.sample.mapper.StudentMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service;/*** Student Service** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月12日*/ @Service public class StudentService {@Autowiredprivate JdbcTemplate jdbcTemplate;// MyBatis的Mapper方法定義接口@Autowiredprivate StudentMapper studentMapper;@TargetDataSource(name="ds2")public List<Student> likeName(String name){return studentMapper.likeName(name);}public List<Student> likeNameByDefaultDataSource(String name){return studentMapper.likeName(name);}/*** 不指定數據源使用默認數據源** @return* @author SHANHY* @create 2016年1月24日*/public List<Student> getList(){String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT";return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){@Overridepublic 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;}});}/*** 指定數據源** @return* @author SHANHY* @create 2016年1月24日*/@TargetDataSource(name="ds1")public List<Student> getListByDs1(){String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT";return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){@Overridepublic 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;}});}/*** 指定數據源** @return* @author SHANHY* @create 2016年1月24日*/@TargetDataSource(name="ds2")public List<Student> getListByDs2(){String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT";return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){@Overridepublic 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為接口,代碼如下:

package org.springboot.sample.mapper;import java.util.List;import org.springboot.sample.entity.Student;/*** StudentMapper,映射SQL語句的接口,無邏輯實現** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月20日*/ public interface StudentMapper {// 注解 @TargetDataSource 不可以在這里使用List<Student> likeName(String name);Student getById(int id);String getNameById(int id);}

請將下面幾個類放到Spring Boot項目中。
DynamicDataSource.java
DynamicDataSourceAspect.java
DynamicDataSourceContextHolder.java
DynamicDataSourceRegister.java
TargetDataSource.java
MProxyTransactionManagementConfiguration.java
MTransactionInterceptor.java

package org.springboot.sample.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** 動態數據源** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月22日*/ public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}} package org.springboot.sample.datasource;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;/*** 切換數據源Advice** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月23日*/ @Aspect @Order(-1)// 保證該AOP在@Transactional之前執行 @Component public 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();}} package org.springboot.sample.datasource;import java.util.ArrayList; import java.util.List;public class DynamicDataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();public static List<String> 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* @return* @author SHANHY* @create 2016年1月24日*/public static boolean containsDataSource(String dataSourceId){return dataSourceIds.contains(dataSourceId);} } package org.springboot.sample.datasource;import java.util.HashMap; import java.util.Map;import javax.sql.DataSource;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata;/*** 動態數據源注冊<br/>* 啟動動態數據源請在啟動類中(如SpringBootSampleApplication)* 添加 @Import(DynamicDataSourceRegister.class)** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月24日*/ public class DynamicDataSourceRegisterimplements 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<String, DataSource> customDataSources = new HashMap<>();@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Map<Object, Object> targetDataSources = new HashMap<Object, Object>();// 將主數據源添加到更多數據源中targetDataSources.put("dataSource", defaultDataSource);DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");// 添加更多數據源targetDataSources.putAll(customDataSources);for (String key : customDataSources.keySet()) {DynamicDataSourceContextHolder.dataSourceIds.add(key);}// 創建DynamicDataSourceGenericBeanDefinition 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* @author SHANHY* @create 2016年1月24日*/@SuppressWarnings("unchecked")public DataSource buildDataSource(Map<String, Object> dsMap) {try {Object type = dsMap.get("type");if (type == null)type = DATASOURCE_TYPE_DEFAULT;// 默認DataSourceClass<? extends DataSource> dataSourceType;dataSourceType = (Class<? extends DataSource>) 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;}/*** 加載多數據源配置*/@Overridepublic void setEnvironment(Environment env) {initDefaultDataSource(env);initCustomDataSources(env);}/*** 初始化主數據源** @author SHANHY* @create 2016年1月24日*/private void initDefaultDataSource(Environment env) {// 讀取主數據源RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");Map<String, Object> 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* @author SHANHY* @create 2016年1月25日*/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);//falsedataBinder.setIgnoreInvalidFields(false);//falsedataBinder.setIgnoreUnknownFields(true);//trueif(dataSourcePropertyValues == null){Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");Map<String, Object> 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);}/*** 初始化更多數據源** @author SHANHY* @create 2016年1月24日*/private void initCustomDataSources(Environment env) {// 讀取配置文件獲取更多數據源,也可以通過defaultDataSource讀取數據庫獲取更多數據源RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");String dsPrefixs = propertyResolver.getProperty("names");for (String dsPrefix : dsPrefixs.split(",")) {// 多個數據源Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");DataSource ds = buildDataSource(dsMap);customDataSources.put(dsPrefix, ds);dataBinder(ds, env);}}} package org.springboot.sample.datasource;import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** 在方法上使用,用于指定使用哪個數據源** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月23日*/ @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource {String name(); }

本文代碼博主是經過測試后沒有問題才發出來共享給大家的。對于連接池參數配置會應用到所有數據源上。
比如配置一個:

spring.datasource.maximum-pool-size=80

那么我們所有的數據源都會自動應用上。

總結

以上是生活随笔為你收集整理的Spring Boot 动态数据源(Spring 注解数据源)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。