一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring Boot 動態數據源示例(多數據源自動切換)

Spring Boot 動態數據源示例(多數據源自動切換)

2020-08-18 11:34catoop Java教程

本篇文章主要介紹了Spring Boot 動態數據源示例(多數據源自動切換),小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

本文實現案例場景:

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

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

一配置二使用

1. 啟動類注冊動態數據源

2. 配置文件中配置多個數據源

3. 在需要的方法上使用注解指定數據源

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

?
1
2
3
4
5
6
@SpringBootApplication
@Import({DynamicDataSourceRegister.class}) // 注冊動態多數據源
public class SpringBootSampleApplication {
 
  // 省略其他代碼
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 主數據源,默認的
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=123456
 
custom.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、使用方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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 {
 
  @Autowired
  private JdbcTemplate jdbcTemplate;
 
  // MyBatis的Mapper方法定義接口
  @Autowired
  private 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>(){
 
      @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;
      }
 
    });
  }
 
  /**
   * 指定數據源
   *
   * @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>(){
 
      @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;
      }
 
    });
  }
 
  /**
   * 指定數據源
   *
   * @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>(){
 
      @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為接口,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.springboot.sample.datasource;
 
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
/**
 * 動態數據源
 *
 * @author  單紅宇(365384722)
 * @create  2016年1月22日
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
 
  @Override
  protected Object determineCurrentLookupKey() {
    return DynamicDataSourceContextHolder.getDataSourceType();
  }
 
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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)
 * @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();
  }
 
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
  }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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)
 * @create 2016年1月24日
 */
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<String, DataSource> customDataSources = new HashMap<>();
 
  @Override
  public 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);
    }
 
    // 創建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
   * @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;// 默認DataSource
 
      Class<? 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;
  }
 
  /**
   * 加載多數據源配置
   */
  @Override
  public 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);//false
    dataBinder.setIgnoreInvalidFields(false);//false
    dataBinder.setIgnoreUnknownFields(true);//true
    if(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);
    }
  }
 
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)
 * @create  2016年1月23日
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
  String name();
}

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

?
1
spring.datasource.maximum-pool-size=80

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

補充:

如果你使用的是SpringMVC,并集成了Shiro,一般按網上的配置你可能是:

?
1
2
3
4
5
6
7
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
   <property name="proxyTargetClass" value="true" />
 </bean>
 
 <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
   <property name="securityManager" ref="securityManager"/>
 </bean>

那么你請不要這樣做,請按下面方法配置:

?
1
2
3
4
5
6
7
8
<!-- AOP式方法級權限檢查 -->
 <!-- 不要使用 DefaultAdvisorAutoProxyCreator 會出現二次代理的問題,這里不詳述。 mark by shanhy 2016-05-15 -->
 <aop:config proxy-target-class="true"/>
 <!-- 或者你使用了 <aop:aspectj-autoproxy proxy-target-class="true" /> 也可以。 -->
 
 <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
   <property name="securityManager" ref="securityManager"/>
 </bean>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://blog.csdn.net/catoop/article/details/50575038

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美成黑人性猛交xxoo | 亚洲天堂影视 | 亚洲 国产精品 日韩 | 亚洲国产天堂久久综合网站 | 边打电话边操 | 9420高清完整版在线观看国语 | 性趣味商品推荐 | 91精品啪在线观看国产91九色 | 狠狠狠地啪香蕉 | 亚洲欧美日韩一区成人 | 亚洲 日韩 国产 制服 在线 | 熟睡中的麻麻大白屁股小说 | 欧美亚洲国产综合在线 | 91国语精品自产拍在线观看一 | 九九精品视频一区二区三区 | 波多野结衣小说 | 亚洲zooz人禽交xxxx | 嫩草影院永久一二三入口 | 四虎最新永久在线精品免费 | 亚洲国产区中文在线观看 | 久久毛片网站 | 日本wwxx护士 | 边摸边吃奶又黄激烈视频韩国 | 天色综合 | 日韩免费一级毛片 | 扒开大腿狠狠挺进视频 | 欧美精品亚洲精品日韩专区va | 羞羞漫画免费漫画页面在线看漫画秋蝉 | 色综合97天天综合网 | 国产精品久久久久影院色老大 | 俄罗斯引擎首页进入 | 免费视频精品一区二区三区 | 999国产精品亚洲77777 | 亚洲 欧美 国产 综合久久 | 国产一精品一av一免费爽爽 | yy111111免费观看 | 性鸥美| 国产一卡2卡3卡四卡高清 | 扒开老女人 | 欧美久久久久久久一区二区三区 | 日本视频在线观看 |