springboot + mybatis配置多數(shù)據(jù)源示例
在實際開發(fā)中,我們一個項目可能會用到多個數(shù)據(jù)庫,通常一個數(shù)據(jù)庫對應(yīng)一個數(shù)據(jù)源。
代碼結(jié)構(gòu):

簡要原理:
1)DatabaseType列出所有的數(shù)據(jù)源的key---key
2)DatabaseContextHolder是一個線程安全的DatabaseType容器,并提供了向其中設(shè)置和獲取DatabaseType的方法
3)DynamicDataSource繼承AbstractRoutingDataSource并重寫其中的方法determineCurrentLookupKey(),在該方法中使用DatabaseContextHolder獲取當(dāng)前線程的DatabaseType
4)MyBatisConfig中生成2個數(shù)據(jù)源DataSource的bean---value
5)MyBatisConfig中將1)和4)組成的key-value對寫入到DynamicDataSource動態(tài)數(shù)據(jù)源的targetDataSources屬性(當(dāng)然,同時也會設(shè)置2個數(shù)據(jù)源其中的一個為DynamicDataSource的defaultTargetDataSource屬性中)
6)將DynamicDataSource作為primary數(shù)據(jù)源注入到SqlSessionFactory的dataSource屬性中去,并且該dataSource作為transactionManager的入?yún)順?gòu)造DataSourceTransactionManager
7)使用的時候,在dao層或service層先使用DatabaseContextHolder設(shè)置將要使用的數(shù)據(jù)源key,然后再調(diào)用mapper層進行相應(yīng)的操作,建議放在dao層去做(當(dāng)然也可以使用spring aop+自定注解去做)
注意:在mapper層進行操作的時候,會先調(diào)用determineCurrentLookupKey()方法獲取一個數(shù)據(jù)源(獲取數(shù)據(jù)源:先根據(jù)設(shè)置去targetDataSources中去找,若沒有,則選擇defaultTargetDataSource),之后在進行數(shù)據(jù)庫操作。
1、假設(shè)有兩個數(shù)據(jù)庫,配置如下
application.properties
#the first datasource jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://xxx:3306/mytestdb?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 jdbc.username = root jdbc.password = 123 #the second datasource jdbc2.driverClassName = com.mysql.jdbc.Driver jdbc2.url = jdbc:mysql://xxx:3306/mytestdb2?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 jdbc2.username = root jdbc2.password = 123
說明:在之前的配置的基礎(chǔ)上,只增加了上述的第二個數(shù)據(jù)源。
2、DatabaseType
package com.xxx.firstboot.common.datasource;
/**
* 列出所有的數(shù)據(jù)源key(常用數(shù)據(jù)庫名稱來命名)
* 注意:
* 1)這里數(shù)據(jù)源與數(shù)據(jù)庫是一對一的
* 2)DatabaseType中的變量名稱就是數(shù)據(jù)庫的名稱
*/
public enum DatabaseType {
mytestdb,mytestdb2
}
作用:列舉數(shù)據(jù)源的key。
3、DatabaseContextHolder
package com.xxx.firstboot.common.datasource;
/**
* 作用:
* 1、保存一個線程安全的DatabaseType容器
*/
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
public static void setDatabaseType(DatabaseType type){
contextHolder.set(type);
}
public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
}
作用:構(gòu)建一個DatabaseType容器,并提供了向其中設(shè)置和獲取DatabaseType的方法
4、DynamicDataSource
package com.xxx.firstboot.common.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 動態(tài)數(shù)據(jù)源(需要繼承AbstractRoutingDataSource)
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
作用:使用DatabaseContextHolder獲取當(dāng)前線程的DatabaseType
5、MyBatisConfig
package com.xxx.firstboot.common;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.common.datasource.DynamicDataSource;
/**
* springboot集成mybatis的基本入口 1)創(chuàng)建數(shù)據(jù)源(如果采用的是默認的tomcat-jdbc數(shù)據(jù)源,則不需要)
* 2)創(chuàng)建SqlSessionFactory 3)配置事務(wù)管理器,除非需要使用事務(wù),否則不用配置
*/
@Configuration // 該注解類似于spring配置文件
@MapperScan(basePackages = "com.xxx.firstboot.mapper")
public class MyBatisConfig {
@Autowired
private Environment env;
/**
* 創(chuàng)建數(shù)據(jù)源(數(shù)據(jù)源的名稱:方法名可以取為XXXDataSource(),XXX為數(shù)據(jù)庫名稱,該名稱也就是數(shù)據(jù)源的名稱)
*/
@Bean
public DataSource myTestDbDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
props.put("url", env.getProperty("jdbc.url"));
props.put("username", env.getProperty("jdbc.username"));
props.put("password", env.getProperty("jdbc.password"));
return DruidDataSourceFactory.createDataSource(props);
}
@Bean
public DataSource myTestDb2DataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("jdbc2.driverClassName"));
props.put("url", env.getProperty("jdbc2.url"));
props.put("username", env.getProperty("jdbc2.username"));
props.put("password", env.getProperty("jdbc2.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* @Primary 該注解表示在同一個接口有多個實現(xiàn)類可以注入的時候,默認選擇哪一個,而不是讓@autowire注解報錯
* @Qualifier 根據(jù)名稱進行注入,通常是在具有相同的多個類型的實例的一個注入(例如有多個DataSource類型的實例)
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("myTestDbDataSource") DataSource myTestDbDataSource,
@Qualifier("myTestDb2DataSource") DataSource myTestDb2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.mytestdb, myTestDbDataSource);
targetDataSources.put(DatabaseType.mytestdb2, myTestDb2DataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 該方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(myTestDbDataSource);// 默認的datasource設(shè)置為myTestDbDataSource
return dataSource;
}
/**
* 根據(jù)數(shù)據(jù)源創(chuàng)建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);// 指定數(shù)據(jù)源(這個必須有,否則報錯)
// 下邊兩句僅僅用于*.xml文件,如果整個持久層操作不需要使用到xml文件的話(只用注解就可以搞定),則不加
fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
fb.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//
return fb.getObject();
}
/**
* 配置事務(wù)管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
作用:
- 通過讀取application.properties文件生成兩個數(shù)據(jù)源(myTestDbDataSource、myTestDb2DataSource)
- 使用以上生成的兩個數(shù)據(jù)源構(gòu)造動態(tài)數(shù)據(jù)源dataSource
- @Primary:指定在同一個接口有多個實現(xiàn)類可以注入的時候,默認選擇哪一個,而不是讓@Autowire注解報錯(一般用于多數(shù)據(jù)源的情況下)
- @Qualifier:指定名稱的注入,當(dāng)一個接口有多個實現(xiàn)類的時候使用(在本例中,有兩個DataSource類型的實例,需要指定名稱注入)
- @Bean:生成的bean實例的名稱是方法名(例如上邊的@Qualifier注解中使用的名稱是前邊兩個數(shù)據(jù)源的方法名,而這兩個數(shù)據(jù)源也是使用@Bean注解進行注入的)
- 通過動態(tài)數(shù)據(jù)源構(gòu)造SqlSessionFactory和事務(wù)管理器(如果不需要事務(wù),后者可以去掉)
6、使用
ShopMapper:
package com.xxx.firstboot.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.xxx.firstboot.domain.Shop;
public interface ShopMapper {
@Select("SELECT * FROM t_shop WHERE id = #{id}")
@Results(value = { @Result(id = true, column = "id", property = "id"),
@Result(column = "shop_name", property = "shopName") })
public Shop getShop(@Param("id") int id);
}
ShopDao:
package com.xxx.firstboot.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.xxx.firstboot.common.datasource.DatabaseContextHolder;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.mapper.ShopMapper;
@Repository
public class ShopDao {
@Autowired
private ShopMapper mapper;
/**
* 獲取shop
*/
public Shop getShop(int id) {
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
return mapper.getShop(id);
}
}
注意:首先設(shè)置了數(shù)據(jù)源的key,然后調(diào)用mapper(在mapper中會首先根據(jù)該key從動態(tài)數(shù)據(jù)源中查詢出相應(yīng)的數(shù)據(jù)源,之后取出連接進行數(shù)據(jù)庫操作)
ShopService:
package com.xxx.firstboot.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xxx.firstboot.dao.ShopDao;
import com.xxx.firstboot.domain.Shop;
@Service
public class ShopService {
@Autowired
private ShopDao dao;
public Shop getShop(int id) {
return dao.getShop(id);
}
}
ShopController:
package com.xxx.firstboot.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.service.ShopService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/shop")
@Api("shopController相關(guān)api")
public class ShopController {
@Autowired
private ShopService service;
@ApiOperation("獲取shop信息,測試多數(shù)據(jù)源")
@RequestMapping(value = "/getShop", method = RequestMethod.GET)
public Shop getShop(@RequestParam("id") int id) {
return service.getShop(id);
}
}
補:其實DatabaseContextHolder和DynamicDataSource完全可以合為一個類
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- springboot下配置多數(shù)據(jù)源的方法
- SpringBoot項目配置postgresql數(shù)據(jù)庫完整步驟(配置多數(shù)據(jù)源)
- springboot 多數(shù)據(jù)源配置不生效遇到的坑及解決
- springboot-mongodb的多數(shù)據(jù)源配置的方法步驟
- 詳解SpringBoot和Mybatis配置多數(shù)據(jù)源
- Springboot如何設(shè)置多數(shù)據(jù)源,隨時切換
- springboot 多數(shù)據(jù)源的實現(xiàn)(最簡單的整合方式)
- springboot整合多數(shù)據(jù)源配置方式
- SpringBoot中Flowable多數(shù)據(jù)源配置及事務(wù)沖突的兩種方案
相關(guān)文章
Java GUI進階之流式布局管理器FlowLayout專項精講
FlowLayout-流式布局管理器,按水平方向依次排列放置組件,排滿一行,換下一行繼續(xù)排列。排列方向(左到右 或 右到左)取決于容器的componentOrientation屬性2022-04-04
Java中Scanner的常用方法總結(jié)(一次學(xué)懂)
這篇文章主要給大家介紹了關(guān)于Java中Scanner常用方法的相關(guān)資料,Java中的Scanner是一個用于讀取用戶輸入的類,它可以讀取各種類型的數(shù)據(jù),包括整數(shù)、浮點數(shù)、字符串等等,需要的朋友可以參考下2023-11-11
springboot中使用jpa下hibernate的ddl-auto方式
這篇文章主要介紹了springboot中使用jpa下hibernate的ddl-auto方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
spring security獲取用戶信息的實現(xiàn)代碼
這篇文章主要介紹了spring security獲取用戶信息的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Java成員變量與局部變量(動力節(jié)點Java學(xué)院整理)
這篇文章主要介紹了Java成員變量與局部變量的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-04-04

