Skip to content

Commit dcc32df

Browse files
tomleetomlee
tomlee
authored and
tomlee
committedJun 14, 2020
Spring Boot 整合 Druid 多数据源(MyBaties)
1 parent c967598 commit dcc32df

22 files changed

+777
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>cn.lijunkui</groupId>
7+
<artifactId>spring-boot-2.x-mybaties-multipleDataSource</artifactId>
8+
<version>0.0.1-SNAPSHOT</version>
9+
<packaging>jar</packaging>
10+
11+
<name>spring-boot-2.x-mybaties-multipleDataSource</name>
12+
<description>Demo project for Spring Boot</description>
13+
14+
<parent>
15+
<groupId>org.springframework.boot</groupId>
16+
<artifactId>spring-boot-starter-parent</artifactId>
17+
<version>2.1.0.RELEASE</version>
18+
<relativePath/>
19+
</parent>
20+
21+
<properties>
22+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
24+
<java.version>1.8</java.version>
25+
</properties>
26+
27+
<dependencies>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter-web</artifactId>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.mybatis.spring.boot</groupId>
34+
<artifactId>mybatis-spring-boot-starter</artifactId>
35+
<version>1.1.1</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>mysql</groupId>
39+
<artifactId>mysql-connector-java</artifactId>
40+
</dependency>
41+
<dependency>
42+
<groupId>com.alibaba</groupId>
43+
<artifactId>druid-spring-boot-starter</artifactId>
44+
<version>1.1.10</version>
45+
</dependency>
46+
47+
<!-- <dependency>
48+
<groupId>com.alibaba</groupId>
49+
<artifactId>druid</artifactId>
50+
<version>1.1.0</version>
51+
</dependency> -->
52+
<dependency>
53+
<groupId>com.google.guava</groupId>
54+
<artifactId>guava</artifactId>
55+
<version>18.0</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.springframework.boot</groupId>
59+
<artifactId>spring-boot-starter-test</artifactId>
60+
<scope>test</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.springframework.boot</groupId>
64+
<artifactId>spring-boot-starter-aop</artifactId>
65+
</dependency>
66+
67+
</dependencies>
68+
69+
<build>
70+
<plugins>
71+
<plugin>
72+
<groupId>org.springframework.boot</groupId>
73+
<artifactId>spring-boot-maven-plugin</artifactId>
74+
</plugin>
75+
</plugins>
76+
</build>
77+
78+
79+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package cn.lijunkui;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class SpringbootexamplesApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(SpringbootexamplesApplication.class, args);
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package cn.lijunkui.config;
2+
3+
4+
import cn.lijunkui.enums.DataSourceTypeEnum;
5+
import org.aspectj.lang.JoinPoint;
6+
import org.aspectj.lang.annotation.Aspect;
7+
import org.aspectj.lang.annotation.Before;
8+
import org.aspectj.lang.annotation.Pointcut;
9+
import org.aspectj.lang.reflect.MethodSignature;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
import org.springframework.stereotype.Component;
13+
14+
/**
15+
* 通过AOP 动态切换数据源切面类
16+
* @Author jkli
17+
* @Date 2020/6/14 2:57 下午
18+
**/
19+
@Aspect
20+
@Component
21+
public class DataSourceAop {
22+
23+
Logger log = LoggerFactory.getLogger(DataSourceAop.class);
24+
25+
@Pointcut("execution( * cn.lijunkui.dao.*.*(..))")
26+
public void daoAspect() {
27+
}
28+
@Before(value="daoAspect()")
29+
public void switchDataSource(JoinPoint joinPoint) throws NoSuchMethodException {
30+
log.info("开始切换数据源");
31+
32+
//获取HotelMapper 获取 ProductMapper 类上声明的TargetDataSource的数据源注解的值
33+
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
34+
Class<?> declaringClass = methodSignature.getMethod().getDeclaringClass();
35+
TargetDataSource annotation = declaringClass.getAnnotation(TargetDataSource.class);
36+
DataSourceTypeEnum value = annotation.value();
37+
log.info("数据源为:{}",value);
38+
39+
//根据TargetDataSource的value设置要切换的数据源
40+
DynamicDataSource.setDataBaseType(value);
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package cn.lijunkui.config;
2+
3+
import cn.lijunkui.enums.DataSourceTypeEnum;
4+
import com.alibaba.druid.pool.DruidDataSource;
5+
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
6+
import org.apache.ibatis.session.SqlSessionFactory;
7+
import org.mybatis.spring.SqlSessionFactoryBean;
8+
import org.springframework.beans.factory.annotation.Qualifier;
9+
import org.springframework.boot.context.properties.ConfigurationProperties;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
13+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
14+
import org.springframework.transaction.PlatformTransactionManager;
15+
16+
import javax.sql.DataSource;
17+
import java.util.HashMap;
18+
import java.util.Map;
19+
20+
/**
21+
* https://cloud.tencent.com/developer/article/1480366
22+
* 多数据库源配置类
23+
* @Author jkli
24+
* @Date 2020/6/14 2:57 下午
25+
**/
26+
@Configuration
27+
public class DataSourceConfig {
28+
29+
/**
30+
* 商品库的数据源
31+
* @return
32+
*/
33+
@Bean(name = "dataSourceForProduct")
34+
@ConfigurationProperties(prefix="spring.datasource.druid.product")
35+
public DruidDataSource dataSourceForProduct() {
36+
return DruidDataSourceBuilder.create().build();
37+
}
38+
39+
/**
40+
* 旅馆库的数据源
41+
* @return
42+
*/
43+
@Bean(name = "dataSourceForHotel")
44+
@ConfigurationProperties(prefix="spring.datasource.druid.hotel")
45+
public DruidDataSource dataSourceForHotel() {
46+
return DruidDataSourceBuilder.create().build();
47+
}
48+
49+
/**
50+
* 动态切换的数据源
51+
* @return
52+
*/
53+
@Bean(name = "dynamicDataSource")
54+
public DataSource dynamicDataSource() {
55+
56+
Map<Object, Object> targetDataSource = new HashMap<>();
57+
targetDataSource.put(DataSourceTypeEnum.PRODUCT, dataSourceForProduct());
58+
targetDataSource.put(DataSourceTypeEnum.HOTEL, dataSourceForHotel());
59+
//设置默认的数据源和以及多数据源的Map信息
60+
DynamicDataSourceRouting dataSource = new DynamicDataSourceRouting();
61+
dataSource.setTargetDataSources(targetDataSource);
62+
dataSource.setDefaultTargetDataSource(dataSourceForProduct());
63+
return dataSource;
64+
}
65+
66+
/**
67+
* 多数据源的 SqlSessionFactory
68+
* @param dynamicDataSource
69+
* @return
70+
* @throws Exception
71+
*/
72+
@Bean(name = "sqlSessionFactory")
73+
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
74+
throws Exception {
75+
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
76+
bean.setDataSource(dynamicDataSource);
77+
//设置数据数据源的Mapper.xml路径
78+
bean.setMapperLocations(
79+
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
80+
//设置Mybaties查询数据自动以驼峰式命名进行设值
81+
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session
82+
.Configuration();
83+
configuration.setMapUnderscoreToCamelCase(true);
84+
bean.setConfiguration(configuration);
85+
86+
return bean.getObject();
87+
}
88+
89+
/**
90+
* 注入 DataSourceTransactionManager 用于事务管理
91+
*/
92+
@Bean
93+
public PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
94+
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dynamicDataSource);
95+
return new DataSourceTransactionManager(dynamicDataSource);
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package cn.lijunkui.config;
2+
3+
import cn.lijunkui.enums.DataSourceTypeEnum;
4+
5+
/**
6+
* @Author jkli
7+
* @Date 2020/6/14 2:57 下午
8+
**/
9+
public class DynamicDataSource {
10+
11+
12+
// 使用ThreadLocal保证线程安全
13+
private static final ThreadLocal<DataSourceTypeEnum> TYPE = new ThreadLocal<DataSourceTypeEnum>();
14+
15+
// 往当前线程里设置数据源类型
16+
public static void setDataBaseType(DataSourceTypeEnum dataBaseType) {
17+
if (dataBaseType == null) {
18+
throw new NullPointerException();
19+
}
20+
System.err.println("[将当前数据源改为]:" + dataBaseType);
21+
TYPE.set(dataBaseType);
22+
}
23+
24+
// 获取数据源类型
25+
public static DataSourceTypeEnum getDataBaseType() {
26+
DataSourceTypeEnum dataBaseType = TYPE.get() == null ? DataSourceTypeEnum.PRODUCT : TYPE.get();
27+
System.err.println("[获取当前数据源的类型为]:" + dataBaseType);
28+
return dataBaseType;
29+
}
30+
31+
// 清空数据类型
32+
public static void clearDataBaseType() {
33+
TYPE.remove();
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package cn.lijunkui.config;
2+
3+
import cn.lijunkui.enums.DataSourceTypeEnum;
4+
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
5+
6+
/**
7+
* @Author jkli
8+
* @Date 2020/6/14 2:57 下午
9+
**/
10+
public class DynamicDataSourceRouting extends AbstractRoutingDataSource {
11+
@Override
12+
protected Object determineCurrentLookupKey() {
13+
DataSourceTypeEnum dataBaseType = DynamicDataSource.getDataBaseType();
14+
return dataBaseType;
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package cn.lijunkui.config;
2+
3+
import cn.lijunkui.enums.DataSourceTypeEnum;
4+
5+
import java.lang.annotation.*;
6+
7+
/**
8+
* @Author jkli
9+
* @Date 2020/6/14 2:57 下午
10+
**/
11+
@Target({ElementType.METHOD, ElementType.TYPE})
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Documented
14+
public @interface TargetDataSource {
15+
DataSourceTypeEnum value() default DataSourceTypeEnum.HOTEL;
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package cn.lijunkui.config;
2+
3+
import org.aspectj.lang.annotation.Aspect;
4+
import org.springframework.aop.Advisor;
5+
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
6+
import org.springframework.aop.support.DefaultPointcutAdvisor;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.transaction.PlatformTransactionManager;
11+
import org.springframework.transaction.TransactionDefinition;
12+
import org.springframework.transaction.interceptor.*;
13+
14+
import java.util.Collections;
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
18+
/**
19+
* @Author jkli
20+
* @Date 2020/6/14 2:57 下午
21+
**/
22+
@Aspect
23+
@Configuration
24+
public class TransactionConfiguration {
25+
private static final int TX_METHOD_TIMEOUT = 5;
26+
private static final String AOP_POINTCUT_EXPRESSION = "execution( * cn.lijunkui.service.*.*(..))";
27+
28+
@Autowired
29+
private PlatformTransactionManager transactionManager;
30+
31+
@Bean
32+
public TransactionInterceptor txAdvice() {
33+
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
34+
/* 只读事务,不做更新操作 */
35+
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
36+
readOnlyTx.setReadOnly(true);
37+
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
38+
/* 当前存在事务就使用当前事务,当前不存在事务就创建一个新的事务 */
39+
RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
40+
requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
41+
requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
42+
//requiredTx.setTimeout(TX_METHOD_TIMEOUT);
43+
Map<String, TransactionAttribute> txMap = new HashMap<String, TransactionAttribute>();
44+
txMap.put("add*", requiredTx);
45+
txMap.put("save*", requiredTx);
46+
txMap.put("insert*", requiredTx);
47+
txMap.put("update*", requiredTx);
48+
txMap.put("delete*", requiredTx);
49+
txMap.put("get*", readOnlyTx);
50+
txMap.put("find*", readOnlyTx);
51+
txMap.put("query*", readOnlyTx);
52+
txMap.put("*", requiredTx);
53+
source.setNameMap(txMap);
54+
TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
55+
return txAdvice;
56+
}
57+
58+
@Bean
59+
public Advisor txAdviceAdvisor() {
60+
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
61+
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
62+
return new DefaultPointcutAdvisor(pointcut, txAdvice());
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package cn.lijunkui.controller;
2+
3+
import cn.lijunkui.domain.Hotel;
4+
import cn.lijunkui.service.HotelService;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.RestController;
8+
9+
import java.util.List;
10+
/**
11+
* 旅馆 Controller
12+
* @Author jkli
13+
* @Date 2020/6/14 2:57 下午
14+
**/
15+
@RestController
16+
public class HotelController {
17+
18+
@Autowired
19+
private HotelService hotelService;
20+
21+
/**
22+
* 查询所有的旅馆信息
23+
* @return
24+
*/
25+
@GetMapping("/hotel")
26+
public List<Hotel> findAll(){
27+
List<Hotel> hotelList = hotelService.findAll();
28+
return hotelList;
29+
}
30+
}

0 commit comments

Comments
 (0)
Failed to load comments.