亚洲欧美日韩综合系列在线_91精品人妻一区二区_欧美大肥婆一级特大AA片_九色91视频免费观看_亚洲综合国产精品_av中文字幕在线不卡_久久精品色综合网_看黄色视频的软件_无卡无码高清中文字幕码2024_亚洲欧美日韩天堂网

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

來源:catoop 發(fā)布時(shí)間:2018-11-14 11:36:19 閱讀量:1171

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

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


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


一配置二使用 

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

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

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


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


@SpringBootApplication

@Import({DynamicDataSourceRegister.class}) // 注冊動態(tài)多數(shù)據(jù)源

public class SpringBootSampleApplication {


    // 省略其他代碼

}

1

2

3

4

5

6

2、配置文件配置內(nèi)容為: 

(不包括項(xiàng)目中的其他配置,這里只是數(shù)據(jù)源相關(guān)的)


# 主數(shù)據(jù)源,默認(rèn)的

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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 {


    @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ù)源使用默認(rèn)數(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;

            }


        });

    }

}

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

要注意的是,在使用MyBatis時(shí),注解@TargetDataSource 不能直接在接口類Mapper上使用。 

按上面的代碼中StudentMapper為接口,代碼如下:


package org.springboot.sample.mapper;


import java.util.List;


import org.springboot.sample.entity.Student;


/**

 * StudentMapper,映射SQL語句的接口,無邏輯實(shí)現(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);


}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

請將下面幾個(gè)類放到Spring Boot項(xiàng)目中。 

DynamicDataSource.java 

DynamicDataSourceAspect.java 

DynamicDataSourceContextHolder.java 

DynamicDataSourceRegister.java 

TargetDataSource.java


package org.springboot.sample.datasource;


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


/**

 * 動態(tài)數(shù)據(jù)源

 *

 * @author   單紅宇(365384722)

 * @myblog  http://blog.csdn.net/catoop/

 * @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

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)

 * @myblog http://blog.csdn.net/catoop/

 * @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ù)源[{}]不存在,使用默認(rèn)數(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

35

36

37

38

39

40

41

42

43

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

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ù)源請?jiān)趩宇愔校ㄈ鏢pringBootSampleApplication)

 * 添加 @Import(DynamicDataSourceRegister.class)

 *

 * @author 單紅宇(365384722)

 * @myblog http://blog.csdn.net/catoop/

 * @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ù)源類型,使用該默認(rèn)值

    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;// 默認(rèn)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(",")) {// 多個(gè)數(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

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

187

188

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;


/**

 * 在方法上使用,用于指定使用哪個(gè)數(shù)據(jù)源

 *

 * @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();

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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

比如配置一個(gè):


spring.datasource.maximum-pool-size=80

1

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


補(bǔ)充: 

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


    <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

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


    <!-- 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>

1

2

3

4

5

6

7

8


--------------------- 


標(biāo)簽: 數(shù)據(jù)庫
分享:
評論:
你還沒有登錄,請先