本文將介紹使用Spring Boot集成Mybatis并實現主從庫分離的實現(同樣適用于多數據源)。延續之前的Spring Boot 集成MyBatis。項目還將集成分頁插件PageHelper、通用Mapper以及Druid。
新建一個Maven項目,最終項目結構如下:
多數據源注入到sqlSessionFactory
POM增加如下依賴:
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
|
<!--JSON--> < dependency > < groupId >com.fasterxml.jackson.core</ groupId > < artifactId >jackson-core</ artifactId > </ dependency > < dependency > < groupId >com.fasterxml.jackson.core</ groupId > < artifactId >jackson-databind</ artifactId > </ dependency > < dependency > < groupId >com.fasterxml.jackson.datatype</ groupId > < artifactId >jackson-datatype-joda</ artifactId > </ dependency > < dependency > < groupId >com.fasterxml.jackson.module</ groupId > < artifactId >jackson-module-parameter-names</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-jdbc</ artifactId > </ dependency > < dependency > < groupId >mysql</ groupId > < artifactId >mysql-connector-java</ artifactId > </ dependency > < dependency > < groupId >com.alibaba</ groupId > < artifactId >druid</ artifactId > < version >1.0.11</ version > </ dependency > <!--mybatis--> < dependency > < groupId >org.mybatis.spring.boot</ groupId > < artifactId >mybatis-spring-boot-starter</ artifactId > < version >1.1.1</ version > </ dependency > <!--mapper--> < dependency > < groupId >tk.mybatis</ groupId > < artifactId >mapper-spring-boot-starter</ artifactId > < version >1.1.0</ version > </ dependency > <!--pagehelper--> < dependency > < groupId >com.github.pagehelper</ groupId > < artifactId >pagehelper-spring-boot-starter</ artifactId > < version >1.1.0</ version > < exclusions > < exclusion > < artifactId >mybatis-spring-boot-starter</ artifactId > < groupId >org.mybatis.spring.boot</ groupId > </ exclusion > </ exclusions > </ dependency > |
這里需要注意的是:項目是通過擴展mybatis-spring-boot-starter的org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration來實現多數據源注入的。在mybatis-spring-boot-starter:1.2.0中,該類取消了默認構造函數,因此本項目依舊使用1.1.0版本。需要關注后續版本是否會重新把擴展開放處理。
之所以依舊使用舊方案,是我個人認為開放擴展是合理的,相信在未來的版本中會回歸。
如果你需要其他方案可參考傳送門
增加主從庫配置(application.yml)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
druid: type: com.alibaba.druid.pool.DruidDataSource master: url: jdbc:mysql://192.168.249.128:3307/db-test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true driver-class-name: com.mysql.jdbc.Driver username: root password: root initial-size: 5 min-idle: 1 max-active: 100 test-on-borrow: true slave: url: jdbc:mysql://192.168.249.128:3317/db-test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.jdbc.Driver username: root password: root initial-size: 5 min-idle: 1 max-active: 100 test-on-borrow: true |
創建數據源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Configuration @EnableTransactionManagement public class DataSourceConfiguration { @Value ( "${druid.type}" ) private Class<? extends DataSource> dataSourceType; @Bean (name = "masterDataSource" ) @Primary @ConfigurationProperties (prefix = "druid.master" ) public DataSource masterDataSource(){ return DataSourceBuilder.create().type(dataSourceType).build(); } @Bean (name = "slaveDataSource" ) @ConfigurationProperties (prefix = "druid.slave" ) public DataSource slaveDataSource1(){ return DataSourceBuilder.create().type(dataSourceType).build(); } } |
將多數據源注入到sqlSessionFactory中
前面提到了這里通過擴展mybatis-spring-boot-starter的org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration來實現多數據源注入的
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
|
@Configuration @AutoConfigureAfter ({DataSourceConfiguration. class }) public class MybatisConfiguration extends MybatisAutoConfiguration { private static Log logger = LogFactory.getLog(MybatisConfiguration. class ); @Resource (name = "masterDataSource" ) private DataSource masterDataSource; @Resource (name = "slaveDataSource" ) private DataSource slaveDataSource; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { return super .sqlSessionFactory(roundRobinDataSouceProxy()); } public AbstractRoutingDataSource roundRobinDataSouceProxy(){ ReadWriteSplitRoutingDataSource proxy = new ReadWriteSplitRoutingDataSource(); Map<Object,Object> targetDataResources = new ClassLoaderRepository.SoftHashMap(); targetDataResources.put(DbContextHolder.DbType.MASTER,masterDataSource); targetDataResources.put(DbContextHolder.DbType.SLAVE,slaveDataSource); proxy.setDefaultTargetDataSource(masterDataSource); //默認源 proxy.setTargetDataSources(targetDataResources); return proxy; } } |
實現讀寫分離(多數據源分離)
這里主要思路如下:
1-將不同的數據源標識記錄在ThreadLocal中
2-通過注解標識出當前的service方法使用哪個庫
3-通過Spring AOP實現攔截注解并注入不同的標識到threadlocal中
4-獲取源的時候通過threadlocal中不同的標識給出不同的sqlSession
標識存放ThreadLocal的實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class DbContextHolder { public enum DbType{ MASTER,SLAVE } private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<>(); public static void setDbType(DbType dbType){ if (dbType== null ) throw new NullPointerException(); contextHolder.set(dbType); } public static DbType getDbType(){ return contextHolder.get()== null ?DbType.MASTER:contextHolder.get(); } public static void clearDbType(){ contextHolder.remove(); } } |
注解實現
1
2
3
4
5
6
7
8
|
/** * 該注解注釋在service方法上,標注為鏈接slaves庫 * Created by Jason on 2017/3/6. */ @Target ({ElementType.METHOD,ElementType.TYPE}) @Retention (RetentionPolicy.RUNTIME) public @interface ReadOnlyConnection { } |
Spring AOP對注解的攔截
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
|
@Aspect @Component public class ReadOnlyConnectionInterceptor implements Ordered { public static final Logger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor. class ); @Around ( "@annotation(readOnlyConnection)" ) public Object proceed(ProceedingJoinPoint proceedingJoinPoint,ReadOnlyConnection readOnlyConnection) throws Throwable { try { logger.info( "set database connection to read only" ); DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE); Object result = proceedingJoinPoint.proceed(); return result; } finally { DbContextHolder.clearDbType(); logger.info( "restore database connection" ); } } @Override public int getOrder() { return 0 ; } } |
根據標識獲取不同源
這里我們通過擴展AbstractRoutingDataSource來獲取不同的源。它是Spring提供的一個可以根據用戶發起的不同請求去轉換不同的數據源,比如根據用戶的不同地區語言選擇不同的數據庫。通過查看源碼可以發現,它是通過determineCurrentLookupKey()返回的不同key到sqlSessionFactory中獲取不同源(前面已經展示了如何在sqlSessionFactory中注入多個源)
1
2
3
4
5
6
7
|
public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DbContextHolder.getDbType(); } } |
以上就完成了讀寫分離(多數據源)的配置方案。下面是一個具體的實例
使用方式
Entity
1
2
3
4
5
6
7
8
9
10
11
|
@Table (name = "t_sys_dic_type" ) public class DicType extends BaseEntity{ String code; String name; Integer status; ... } |
Mapper
1
2
|
public interface DicTypeMapper extends BaseMapper<DicType> { } |
Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Service public class DicTypeService { @Autowired private DicTypeMapper dicTypeMapper; @ReadOnlyConnection public List<DicType> getAll(DicType dicType){ if (dicType.getPage() != null && dicType.getRows() != null ) { PageHelper.startPage(dicType.getPage(), dicType.getRows()); } return dicTypeMapper.selectAll(); } } |
注意這里的@ReadOnlyConnection注解
Controller
1
2
3
4
5
6
7
8
9
10
11
12
|
@RestController @RequestMapping ( "/dictype" ) public class DicTypeController { @Autowired private DicTypeService dicTypeService; @RequestMapping (value = "/all" ) public PageInfo<DicType> getALL(DicType dicType){ List<DicType> dicTypeList = dicTypeService.getAll(dicType); return new PageInfo<>(dicTypeList); } } |
通過mvn spring-boot:run啟動后,即可通過http://localhost:9090/dictype/all 獲取到數據
后臺打印出
1
|
c.a.d.m.ReadOnlyConnectionInterceptor : set database connection to read only |
說明使用了從庫的鏈接獲取數據
備注:如何保證多源事務呢?
1-在讀寫分離場景中不會考慮主從庫事務,在純讀的上下文上使用@ReadOnlyConnection標簽。其他則默認使用主庫。
2-在多源場景中,Spring的@Transaction是可以保證多源的事務性的。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.jianshu.com/p/8813ec02926a