激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

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

服務(wù)器之家 - 編程語言 - Java教程 - Spring Boot 動態(tài)數(shù)據(jù)源示例(多數(shù)據(jù)源自動切換)

Spring Boot 動態(tài)數(shù)據(jù)源示例(多數(shù)據(jù)源自動切換)

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

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

本文實現(xiàn)案例場景:

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

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

一配置二使用

1. 啟動類注冊動態(tài)數(shù)據(jù)源

2. 配置文件中配置多個數(shù)據(jù)源

3. 在需要的方法上使用注解指定數(shù)據(jù)源

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

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

2、配置文件配置內(nèi)容為: (不包括項目中的其他配置,這里只是數(shù)據(jù)源相關(guān)的)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 主數(shù)據(jù)源,默認的
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
 
# 更多數(shù)據(jù)源
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);
  }
 
  /**
   * 不指定數(shù)據(jù)源使用默認數(shù)據(jù)源
   *
   * @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;
      }
 
    });
  }
 
  /**
   * 指定數(shù)據(jù)源
   *
   * @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;
      }
 
    });
  }
 
  /**
   * 指定數(shù)據(jù)源
   *
   * @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語句的接口,無邏輯實現(xiàn)
 *
 * @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;
 
/**
 * 動態(tài)數(shù)據(jù)源
 *
 * @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;
 
/**
 * 切換數(shù)據(jù)源Advice
 *
 * @author 單紅宇(365384722)
 * @create 2016年1月23日
 */
@Aspect
@Order(-1)// 保證該AOP在@Transactional之前執(zhí)行
@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("數(shù)據(jù)源[{}]不存在,使用默認數(shù)據(jù)源 > {}", 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當(dāng)前是否存在
   *
   * @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;
 
/**
 * 動態(tài)數(shù)據(jù)源注冊<br/>
 * 啟動動態(tài)數(shù)據(jù)源請在啟動類中(如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;
 
  // 如配置文件中未指定數(shù)據(jù)源類型,使用該默認值
  private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
  // private static final Object DATASOURCE_TYPE_DEFAULT =
  // "com.zaxxer.hikari.HikariDataSource";
 
  // 數(shù)據(jù)源
  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>();
    // 將主數(shù)據(jù)源添加到更多數(shù)據(jù)源中
    targetDataSources.put("dataSource", defaultDataSource);
    DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
    // 添加更多數(shù)據(jù)源
    targetDataSources.putAll(customDataSources);
    for (String key : customDataSources.keySet()) {
      DynamicDataSourceContextHolder.dataSourceIds.add(key);
    }
 
    // 創(chuàng)建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");
  }
 
  /**
   * 創(chuàng)建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;
  }
 
  /**
   * 加載多數(shù)據(jù)源配置
   */
  @Override
  public void setEnvironment(Environment env) {
    initDefaultDataSource(env);
    initCustomDataSources(env);
  }
 
  /**
   * 初始化主數(shù)據(jù)源
   *
   * @author SHANHY
   * @create 2016年1月24日
   */
  private void initDefaultDataSource(Environment env) {
    // 讀取主數(shù)據(jù)源
    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綁定更多數(shù)據(jù)
   *
   * @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);
      // 排除已經(jīng)設(shè)置的屬性
      values.remove("type");
      values.remove("driver-class-name");
      values.remove("url");
      values.remove("username");
      values.remove("password");
      dataSourcePropertyValues = new MutablePropertyValues(values);
    }
    dataBinder.bind(dataSourcePropertyValues);
  }
 
  /**
   * 初始化更多數(shù)據(jù)源
   *
   * @author SHANHY
   * @create 2016年1月24日
   */
  private void initCustomDataSources(Environment env) {
    // 讀取配置文件獲取更多數(shù)據(jù)源,也可以通過defaultDataSource讀取數(shù)據(jù)庫獲取更多數(shù)據(jù)源
    RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
    String dsPrefixs = propertyResolver.getProperty("names");
    for (String dsPrefix : dsPrefixs.split(",")) {// 多個數(shù)據(jù)源
      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;
 
/**
 * 在方法上使用,用于指定使用哪個數(shù)據(jù)源
 *
 * @author  單紅宇(365384722)
 * @create  2016年1月23日
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
  String name();
}

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

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

那么我們所有的數(shù)據(jù)源都會自動應(yīng)用上。

補充:

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

?
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式方法級權(quán)限檢查 -->
 <!-- 不要使用 DefaultAdvisorAutoProxyCreator 會出現(xiàn)二次代理的問題,這里不詳述。 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>

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 黄色成人小视频 | 暖暖免费观看高清完整版电影 | 日本不卡视频在线观看 | 成码无人av片在线观看网站 | 成人在线免费观看网址 | 免费永久看羞羞片网站入口 | 欧美日本91精品久久久久 | 日韩视频中文 | 国产做爰 | 亚洲国产精久久久久久久 | 黄视频免费在线 | 国产美女视频一区二区三区 | 毛片成人| 国产二三区 | 亚洲特黄 | 深夜福利视频免费观看 | 久久精品视频网址 | 在线成人免费视频 | 在线播放免费视频 | 一区二区三区日韩在线观看 | 久久97超碰| 日本a v免费观看 | 毛片韩国 | 日韩视频在线观看免费 | 黄色一级视频 | 欧美成人一区二区三区电影 | 一区二区久久精品66国产精品 | 国产一区二区三区四区五区精品 | 国产亚洲精品久久久久久久 | 精品呦女 | 久久中文一区 | 男人的天堂色偷偷 | 亚洲男人一区 | 午夜小影院 | 亚洲免费网站 | 中文字幕在线播放视频 | 亚洲一区 国产精品 | 国产一级午夜 | 在线播放av网址 | 欧美日韩在线中文字幕 | 亚洲成人国产综合 |