Springboot實(shí)現(xiàn)多數(shù)據(jù)源切換詳情
1. 實(shí)現(xiàn)效果
1.1 controller
最終實(shí)現(xiàn)效果,在接口上標(biāo)記上 @Router 注解用來標(biāo)記當(dāng)前接口需要根據(jù)參數(shù)中的某個(gè)字段進(jìn)行數(shù)據(jù)的切換
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserMapper userMapper;
@GetMapping("/save")
@Router(routingFiled = "id")
@Transactional
public void saveUser(@RequestParam("id") String id){
User user = new User();
user.setAge("123");
user.setId(Long.valueOf(id));
user.setName("zs");
//設(shè)置表的后綴名稱,在mybatis執(zhí)行sql時(shí)可以獲取
user.setTableSuffix(MultiDataSourceHolder.getTableIndex());
userMapper.insert(user);
}
@GetMapping("/get")
@Router(routingFiled = "id")
public User getUser(@RequestParam("id") String id){
return userMapper.selectByPrimaryKey(Long.valueOf(id),MultiDataSourceHolder.getTableIndex());
}
}1.2 mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhj.multiple.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.zhj.multiple.entity.User">
<!--@mbg.generated-->
<id column="id" jdbcType="BIGINT" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="age" jdbcType="VARCHAR" property="age" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, `name`, age
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
<!--@mbg.generated-->
select
<include refid="Base_Column_List" />
<!--通過${}獲取出表名,為什么不是用#呢?因?yàn)楸砻窃趦?nèi)部進(jìn)行計(jì)算,不用擔(dān)心sql注入的問題,而且$進(jìn)行獲取是直接將表名進(jìn)行拼接上,不會(huì)使用預(yù)處理-->
from user${tableName}
where id = #{id,jdbcType=BIGINT}
</select>
<insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.zhj.multiple.entity.User" useGeneratedKeys="true">
<!--@mbg.generated-->
insert into user${tableSuffix} (id ,`name`, age)
values (#{id},#{name,jdbcType=VARCHAR}, #{age,jdbcType=VARCHAR})
</insert>
</mapper>1.3 application.yml
#showSql
logging:
level:
com:
zhj:
mapper : debug
server:
port: 8081
# 配置路由分庫分表的策略
datasource:
stragegy:
dataSourceNum: 2 #庫的個(gè)數(shù)
tableNum: 2 #表的個(gè)數(shù)
routingFiled: 'userId' #根據(jù)哪個(gè)字段來進(jìn)行分庫分表
tableSuffixStyle: '%04d' #表的索引值 4位補(bǔ)齊 例如:_0003
tableSuffixConnect: '_' #表的連接風(fēng)格 order_
routingStategy: 'ROUTING_DS_TABLE_STATEGY' #表的策略,啟動(dòng)時(shí)會(huì)根據(jù)表的策略進(jìn)行驗(yàn)證
#配置數(shù)據(jù)源
multiple:
data0:
user: root
password: root
url: jdbc:mysql://192.168.60.46:3306/multiple-0?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver: com.mysql.jdbc.Driver
data1:
user: root
password: root
url: jdbc:mysql://192.168.60.46:3306/multiple-1?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver: com.mysql.jdbc.Driver1.4 啟動(dòng)類
@SpringBootApplication
@EnableAspectJAutoProxy
@MapperScan("com.zhj.multiple.mapper")
@EnableTransactionManagement
public class MultipleApplication {
public static void main(String[] args) {
SpringApplication.run(MultipleApplication.class, args);
}
}2. 注解
2.1 @Router
/**
* 路由注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Router {
/**
* 路由字段
*
* @return 默認(rèn)路由字段是參數(shù)中的哪一個(gè)
*/
String routingFiled() default MultipleConstant.DEFAULT_ROUTING_FIELD;
}3. 分庫策略
3.1 MultipleConstant
目前有三種分庫的策略:
- 多庫多表
- 多庫單表
- 單庫多表
@Data
public class MultipleConstant {
/**
* 多庫多表策略
*/
public static final String ROUTING_DS_TABLE_STATEGY = "ROUTING_DS_TABLE_STATEGY";
/**
* 多庫單表策略
*/
public static final String ROUTGING_DS_STATEGY = "ROUTGING_DS_STATEGY";
/**
* 單庫多表策略
*/
public static final String ROUTGIN_TABLE_STATEGY = "ROUTGIN_TABLE_STATEGY";
/**
* 默認(rèn)的路由字段
*/
public static final String DEFAULT_ROUTING_FIELD = "accountId";
}3.2 IRoutingInterface
路由的頂級(jí)接口,用于定義一些通用的方法
public interface IRoutingInterface {
/**
* 根據(jù)字段key計(jì)算出數(shù)據(jù)庫
* @param routingFiled
* @return
*/
String calDataSourceKey(String routingFiled);
/**
* 獲取路由字段的hashCode
* @param routingFiled
* @return
*/
Integer getRoutingFileHashCode(String routingFiled);
/**
* 計(jì)算出表名
* @param routingFiled
* @return
*/
String calTableKey(String routingFiled);
/**
* 計(jì)算出表的前綴
* @param tableIndex
* @return
*/
String getFormatTableSuffix(Integer tableIndex);
}3.3 AbstractRouting
@EnableConfigurationProperties({MultipleStategyProperties.class})
@Data
@Slf4j
public abstract class AbstractRouting implements IRoutingInterface, InitializingBean {
@Resource
private MultipleStategyProperties multipleStategyProperties;
@Override
public Integer getRoutingFileHashCode(String routingFiled){
return Math.abs(routingFiled.hashCode());
}
/**
* 獲取表的后綴
* @param tableIndex 表索引
*/
@Override
public String getFormatTableSuffix(Integer tableIndex){
//獲取連接符
String tableSuffixConnect = multipleStategyProperties.getTableSuffixConnect();
//根據(jù)配置風(fēng)格格式化表后綴名稱
String format = String.format(multipleStategyProperties.getTableSuffixStyle(), tableIndex);
return tableSuffixConnect + format;
}
/**
* 工程啟動(dòng)時(shí),檢驗(yàn)配置的數(shù)據(jù)源是否跟策略相似,實(shí)現(xiàn)了 InitializingBean 初始化后會(huì)執(zhí)行當(dāng)前方法
*/
@Override
public void afterPropertiesSet(){
switch (multipleStategyProperties.getRoutingStategy()) {
case MultipleConstant.ROUTING_DS_TABLE_STATEGY:
checkRoutingDsTableStategyConfig();
break;
case MultipleConstant.ROUTGING_DS_STATEGY:
checkRoutingDsStategyConfig();
break;
default:
checkRoutingTableStategyConfig();
break;
}
}
/**
* 檢查多庫 多表配置
*/
private void checkRoutingDsTableStategyConfig() {
if(multipleStategyProperties.getTableNum()<=1 ||multipleStategyProperties.getDataSourceNum()<=1){
log.error("你的配置項(xiàng)routingStategy:{}是多庫多表配置,數(shù)據(jù)庫個(gè)數(shù)>1," +
"每一個(gè)庫中表的個(gè)數(shù)必須>1,您的配置:數(shù)據(jù)庫個(gè)數(shù):{},表的個(gè)數(shù):{}",multipleStategyProperties.getRoutingStategy(),
multipleStategyProperties.getDataSourceNum(),multipleStategyProperties.getTableNum());
throw new RuntimeException();
}
}
/**
* 檢查多庫一表的路由配置項(xiàng)
*/
private void checkRoutingDsStategyConfig() {
if(multipleStategyProperties.getTableNum()!=1 ||multipleStategyProperties.getDataSourceNum()<=1){
log.error("你的配置項(xiàng)routingStategy:{}是多庫一表配置,數(shù)據(jù)庫個(gè)數(shù)>1," +
"每一個(gè)庫中表的個(gè)數(shù)必須=1,您的配置:數(shù)據(jù)庫個(gè)數(shù):{},表的個(gè)數(shù):{}",multipleStategyProperties.getRoutingStategy(),
multipleStategyProperties.getDataSourceNum(),multipleStategyProperties.getTableNum());
throw new RuntimeException();
}
}
/**
* 檢查一庫多表的路由配置項(xiàng)
*/
private void checkRoutingTableStategyConfig() {
if(multipleStategyProperties.getTableNum()<=1 ||multipleStategyProperties.getDataSourceNum()!=1){
log.error("你的配置項(xiàng)routingStategy:{}是一庫多表配置,數(shù)據(jù)庫個(gè)數(shù)=1," +
"每一個(gè)庫中表的個(gè)數(shù)必須>1,您的配置:數(shù)據(jù)庫個(gè)數(shù):{},表的個(gè)數(shù):{}",multipleStategyProperties.getRoutingStategy(),
multipleStategyProperties.getDataSourceNum(),multipleStategyProperties.getTableNum());
throw new RuntimeException();
}
}
}3.4 RoutingDsAndTbStrategy
目前實(shí)現(xiàn)了一個(gè)多庫多表的策略進(jìn)行配置,其余兩個(gè)分庫算法可以自行實(shí)現(xiàn)
@Slf4j
public class RoutingDsAndTbStrategy extends AbstractRouting {
/**
* 確定數(shù)據(jù)源的key
* @param routingFiled
* @return
*/
@Override
public String calDataSourceKey(String routingFiled) {
//計(jì)算hash值
Integer routingFileHashCode = getRoutingFileHashCode(routingFiled);
//定位數(shù)據(jù)源
int dsIndex = routingFileHashCode % getMultipleStategyProperties().getDataSourceNum();
String dataSourceKey = getMultipleStategyProperties().getDataSourceKeysMapping().get(dsIndex);
//將數(shù)據(jù)源key放入持有器當(dāng)中
MultiDataSourceHolder.setDataSourceHolder(dataSourceKey);
log.info("根據(jù)路由字段:{},值:{},計(jì)算出數(shù)據(jù)庫索引值:{},數(shù)據(jù)源key的值:{}",getMultipleStategyProperties().getRoutingFiled(),routingFiled,dsIndex,dataSourceKey);
return dataSourceKey;
}
/**
* 計(jì)算表的key
* @param routingFiled
* @return
*/
@Override
public String calTableKey(String routingFiled) {
//獲取到當(dāng)前key的hash
Integer routingFileHashCode = getRoutingFileHashCode(routingFiled);
//通過hash值取模,獲取到對(duì)應(yīng)的索引值
int tbIndex = routingFileHashCode % getMultipleStategyProperties().getTableNum();
//獲取表后綴
String formatTableSuffix = getFormatTableSuffix(tbIndex);
//將表名設(shè)置到上下文中,方便后續(xù)同線程獲取到對(duì)應(yīng)的表名
MultiDataSourceHolder.setTableIndexHolder(formatTableSuffix);
return formatTableSuffix;
}
}3.5 RoutingDsStrategy
多庫單表:
public class RoutingDsStrategy extends AbstractRouting {
@Override
public String calDataSourceKey(String routingFiled) {
return null;
}
@Override
public String calTableKey(String routingFiled) {
return null;
}
}3.6 RoutingTbStrategy
單庫多表策略:
public class RoutingTbStrategy extends AbstractRouting {
@Override
public String calDataSourceKey(String routingFiled) {
return null;
}
@Override
public String calTableKey(String routingFiled) {
return null;
}
}4. 配置類
以下兩個(gè)配置:
MultipleStategyProperties:用于配置數(shù)據(jù)庫策略,有多少庫,多少表,以及表名
4.1 MultipleStategyProperties
@ConfigurationProperties(prefix = "datasource.stragegy")
@Data
public class MultipleStategyProperties {
/**
* 默認(rèn)是一個(gè)數(shù)據(jù)庫 默認(rèn)一個(gè)
*/
private Integer dataSourceNum = 1;
/**
* 每一個(gè)庫對(duì)應(yīng)表的個(gè)數(shù) 默認(rèn)是一個(gè)
*/
private Integer tableNum = 1;
/**
* 路由字段 必須在配置文件中配置(不配置會(huì)拋出異常)
*/
private String routingFiled;
/**
* 數(shù)據(jù)庫的映射關(guān)系
*/
private Map<Integer,String> dataSourceKeysMapping;
/**
* 表的后綴連接風(fēng)格 比如order_
*/
private String tableSuffixConnect="_";
/**
* 表的索引值 格式化為四位 不足左補(bǔ)零 1->0001 然后在根據(jù)tableSuffixConnect屬性拼接成
* 成一個(gè)完整的表名 比如 order表 所以為1 那么數(shù)據(jù)庫表明為 order_0001
*/
private String tableSuffixStyle= "%04d";
/**
* 默認(rèn)的多庫多表策略
*/
private String routingStategy = MultipleConstant.ROUTING_DS_TABLE_STATEGY;
}4.2 MultipleStategyProperties
@Configuration
public class MultipleDataSourceStrategyConfig {
/**
* 當(dāng)配置文件里面包含某個(gè)配置,并且值是多少時(shí)生效
*
* @return routing interface
* @since 1.0.0
*/
@Bean
@ConditionalOnProperty(prefix = "datasource.stragegy",name = "routingStategy",havingValue = "ROUTING_DS_TABLE_STATEGY")
public IRoutingInterface routingDsAndTbStrategy(){
return new RoutingDsAndTbStrategy();
}
/**
* Routing ds strategy
*
* @return the routing interface
* @since 1.0.0
*/
@Bean
@ConditionalOnProperty(prefix = "datasource.stragegy",name = "routingStategy",havingValue = "ROUTGING_DS_STATEGY")
public IRoutingInterface routingDsStrategy(){
return new RoutingDsStrategy();
}
/**
* Routing tb strategy
*
* @return the routing interface
* @since 1.0.0
*/
@Bean
@ConditionalOnProperty(prefix = "datasource.stragegy",name = "routingStategy",havingValue = "ROUTGIN_TABLE_STATEGY")
public IRoutingInterface routingTbStrategy(){
return new RoutingTbStrategy();
}
}4.3 MultipleDataSourceStrategyConfig
根據(jù)對(duì)應(yīng)的配置創(chuàng)建不同的分庫策略
@Configuration
public class MultipleDataSourceStrategyConfig {
/**
* 當(dāng)配置文件里面包含某個(gè)配置,并且值是多少時(shí)生效
*
* @return routing interface
* @since 1.0.0
*/
@Bean
@ConditionalOnProperty(prefix = "datasource.stragegy",name = "routingStategy",havingValue = "ROUTING_DS_TABLE_STATEGY")
public IRoutingInterface routingDsAndTbStrategy(){
return new RoutingDsAndTbStrategy();
}
/**
* Routing ds strategy
*
* @return the routing interface
* @since 1.0.0
*/
@Bean
@ConditionalOnProperty(prefix = "datasource.stragegy",name = "routingStategy",havingValue = "ROUTGING_DS_STATEGY")
public IRoutingInterface routingDsStrategy(){
return new RoutingDsStrategy();
}
/**
* Routing tb strategy
*
* @return the routing interface
* @since 1.0.0
*/
@Bean
@ConditionalOnProperty(prefix = "datasource.stragegy",name = "routingStategy",havingValue = "ROUTGIN_TABLE_STATEGY")
public IRoutingInterface routingTbStrategy(){
return new RoutingTbStrategy();
}
}4.4 MultipleDataSourceConfig
多數(shù)據(jù)源自動(dòng)裝配類,其中創(chuàng)建了多個(gè)數(shù)據(jù)源,通過 spring提供的 AbstractRoutingDataSource 類進(jìn)行數(shù)據(jù)源的切換
@Configuration
//開啟數(shù)據(jù)源以及數(shù)據(jù)分庫策略配置
@EnableConfigurationProperties({MultipleDataSourceProperties.class, MultipleStategyProperties.class})
public class MultipleDataSourceConfig {
@Resource
private MultipleDataSourceProperties multipleDataSourceProperties;
@Resource
private MultipleStategyProperties multipleStategyProperties;
/**
* 配置數(shù)據(jù)源
* @return
*/
@Bean("data0")
public DataSource dataSource0(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUsername(multipleDataSourceProperties.getData0().getUser());
druidDataSource.setPassword(multipleDataSourceProperties.getData0().getPassword());
druidDataSource.setUrl(multipleDataSourceProperties.getData0().getUrl());
druidDataSource.setDriverClassName(multipleDataSourceProperties.getData0().getDriver());
return druidDataSource;
}
@Bean("data1")
public DataSource dataSource1(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUsername(multipleDataSourceProperties.getData1().getUser());
druidDataSource.setPassword(multipleDataSourceProperties.getData1().getPassword());
druidDataSource.setUrl(multipleDataSourceProperties.getData1().getUrl());
druidDataSource.setDriverClassName(multipleDataSourceProperties.getData1().getDriver());
return druidDataSource;
}
/**
* 設(shè)置多數(shù)據(jù)源
* @param data0
* @param data1
* @return
*/
@Bean
public MultiDataSource multiDataSource(DataSource data0,DataSource data1){
//將多個(gè)數(shù)據(jù)與數(shù)據(jù)源關(guān)聯(lián)起來
MultiDataSource multiDataSource = new MultiDataSource();
HashMap<Object, Object> multiMap = new HashMap<>();
multiMap.put("data0",data0);
multiMap.put("data1",data1);
//設(shè)置目標(biāo)數(shù)據(jù)源
multiDataSource.setTargetDataSources(multiMap);
//設(shè)置默認(rèn)的數(shù)據(jù)源
multiDataSource.setDefaultTargetDataSource(data0);
//設(shè)置數(shù)據(jù)源名稱的映射
Map<Integer, String> multiMappings = new HashMap<>();
multiMappings.put(0,"data0");
multiMappings.put(1,"data1");
multipleStategyProperties.setDataSourceKeysMapping(multiMappings);
return multiDataSource;
}
/**
* 將多數(shù)據(jù)源設(shè)置進(jìn)mybatis的工廠類中
* @param multiDataSource
* @return
*/
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("multiDataSource") MultiDataSource multiDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(multiDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/mappers/*.xml"));
sqlSessionFactoryBean.setTypeAliasesPackage("com.zhj.multiple.entity");
return sqlSessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* 將多數(shù)據(jù)源設(shè)置到事務(wù)管理器中
*
* @param multiDataSource
* @return
*/
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("multiDataSource") MultiDataSource multiDataSource){
return new DataSourceTransactionManager(multiDataSource);
}
}4.5 MultiDataSource
覆寫 AbstractRoutingDataSource.determineCurrentLookupKey() 的方法,在mybatis中通過 Datasource.getConnection() 會(huì)調(diào)用 determineCurrentLookupKey() 獲取到對(duì)應(yīng)的數(shù)據(jù)源,然后通過數(shù)據(jù)源獲取到連接,其中內(nèi)部維護(hù)了一個(gè) Map 來保存數(shù)據(jù)源的映射關(guān)系
public class MultiDataSource extends AbstractRoutingDataSource {
/**
* 獲取到指定的數(shù)據(jù)源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return MultiDataSourceHolder.getDataSourceKey();
}
}5. 全局上下文
用于保存數(shù)據(jù)庫、表名,方便后續(xù)使用
5.1 MultiDataSourceHolder
@Data
public class MultiDataSourceHolder {
/**
* 存儲(chǔ)數(shù)據(jù)源
*/
private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>();
/**
* 存儲(chǔ)表的索引
*/
private static final ThreadLocal<String> tableIndexHolder = new ThreadLocal<>();
/**
* Get data source key
*
* @return the string
* @since 1.0.0
*/
public static String getDataSourceKey(){
return dataSourceHolder.get();
}
/**
* Get table index
*
* @return the string
* @since 1.0.0
*/
public static String getTableIndex(){
return tableIndexHolder.get();
}
/**
* Clear data source key
*
* @since 1.0.0
*/
public static void clearDataSourceKey(){
dataSourceHolder.remove();
}
/**
* Clear table index
*
* @since 1.0.0
*/
public static void clearTableIndex(){
tableIndexHolder.remove();
}
/**
* Set data source holder
*
* @param key key
* @since 1.0.0
*/
public static void setDataSourceHolder(String key){
dataSourceHolder.set(key);
}
/**
* Set table index holder
*
* @param key key
* @since 1.0.0
*/
public static void setTableIndexHolder(String key){
tableIndexHolder.set(key);
}
}6. 切面
6.1 RoutingAspect
通過路由切面進(jìn)行 @Router 注解的處理,提前將數(shù)據(jù)庫的key以及表名的后綴獲取出來進(jìn)行存儲(chǔ)
@Aspect
@Component
@Slf4j
public class RoutingAspect {
@Resource
private IRoutingInterface iRoutingInterface;
@Pointcut("@annotation(com.zhj.multiple.annotations.Router)")
public void pointCut(){};
@Before("pointCut()")
public void before(JoinPoint joinPoint) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
long beginTime = System.currentTimeMillis();
//獲取方法調(diào)用名稱
Method method = getInvokeMethod(joinPoint);
//獲取方法指定的注解
Router router = method.getAnnotation(Router.class);
//獲取指定的路由key
String routingFiled = router.routingFiled();
if(Objects.nonNull(router)){
boolean havingRoutingField = false;
//獲取到http請(qǐng)求
HttpServletRequest requestAttributes = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
//優(yōu)先獲取@ReqeustParam注解中的路由字段
String routingFieldValue = requestAttributes.getParameter(routingFiled);
if(!StringUtils.isEmpty(routingFieldValue)){
//計(jì)算數(shù)據(jù)庫key
String dbKey = iRoutingInterface.calDataSourceKey(routingFieldValue);
//計(jì)算表索引
String tableIndex = iRoutingInterface.calTableKey(routingFieldValue);
log.info("選擇的dbkey是:{},tableKey是:{}",dbKey,tableIndex);
}else {
//獲取方法入?yún)?
Object[] args = joinPoint.getArgs();
if(args != null && args.length > 0) {
for(int index = 0; index < args.length; index++) {
//找到參數(shù)當(dāng)中路由字段的值
routingFieldValue = BeanUtils.getProperty(args[index],routingFiled);
if(!StringUtils.isEmpty(routingFieldValue)) {
//計(jì)算數(shù)據(jù)庫key
String dbKey = iRoutingInterface.calDataSourceKey(routingFieldValue);
//計(jì)算表索引
String tableIndex = iRoutingInterface.calTableKey(routingFieldValue);
log.info("選擇的dbkey是:{},tableKey是:{}",dbKey,tableIndex);
havingRoutingField = true;
break;
}
}
//判斷入?yún)⒅袥]有路由字段
if(!havingRoutingField) {
log.warn("入?yún)}中沒有包含路由字段:{}",args,routingFiled);
throw new RuntimeException();
}
}
}
}
}
private Method getInvokeMethod(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method targetMethod = methodSignature.getMethod();
return targetMethod;
}
/**
* 清除線程緩存
* @param joinPoint
*/
@After("pointCut()")
public void methodAfter(JoinPoint joinPoint){
MultiDataSourceHolder.clearDataSourceKey();
MultiDataSourceHolder.clearTableIndex();
}
}到此這篇關(guān)于Springboot實(shí)現(xiàn)多數(shù)據(jù)源切換詳情的文章就介紹到這了,更多相關(guān)Springboot多數(shù)據(jù)源切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 在SpringBoot項(xiàng)目中動(dòng)態(tài)切換數(shù)據(jù)源和數(shù)據(jù)庫的詳細(xì)步驟
- SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢
- SpringBoot項(xiàng)目中如何動(dòng)態(tài)切換數(shù)據(jù)源、數(shù)據(jù)庫
- SpringBoot實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的項(xiàng)目實(shí)踐
- SpringBoot實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的方法總結(jié)
- 使用SpringBoot動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)方式
- SpringBoot多數(shù)據(jù)源切換實(shí)現(xiàn)代碼(Mybaitis)
- SpringBoot實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源的示例代碼
相關(guān)文章
深入學(xué)習(xí)SpringCloud之SpringCloud簡介
Spring Cloud是一個(gè)一站式的開發(fā)分布式系統(tǒng)的框架,為開發(fā)者提供了一系列的構(gòu)建分布式系統(tǒng)的工具集,本文給大家介紹springcloud的相關(guān)知識(shí),感興趣的朋友跟隨一起看看吧2021-04-04
Java?Stream實(shí)現(xiàn)多字段分組groupingBy操作詳解
Stream是Java8的一個(gè)新特性,主要用戶集合數(shù)據(jù)的處理,如排序、過濾、去重等等功能,本文就來講講如何利用Stream實(shí)現(xiàn)比較優(yōu)雅的按多字段進(jìn)行分組groupingBy吧2023-06-06
SpringBoot使用Jackson配置全局時(shí)間日期格式
本文主要介紹了SpringBoot使用Jackson配置全局時(shí)間日期格式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
Java中使用StackWalker和Stream API進(jìn)行堆棧遍歷
StackWalking API是添加到Java中最酷的(并且對(duì)大多數(shù)開發(fā)人員來說完全不切實(shí)際,一般不會(huì)用,除非深層跟蹤調(diào)優(yōu))的功能之一。在這篇簡短的文章中,我們將看到它是什么以及使用它有多么容易,很快的認(rèn)識(shí)它2018-09-09
Java設(shè)計(jì)模式七大原則之里氏替換原則詳解
在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,里氏替換原則(Liskov Substitution principle)是對(duì)子類型的特別定義。本文將為大家詳細(xì)介紹Java設(shè)計(jì)模式七大原則之一的里氏替換原則,需要的可以參考一下2022-02-02
SpringBoot參數(shù)校驗(yàn)Validator框架詳解
Validator框架就是為了解決開發(fā)人員在開發(fā)的時(shí)候少寫代碼,提升開發(fā)效率,Validator專門用來進(jìn)行接口參數(shù)校驗(yàn),今天通過本文給大家介紹SpringBoot參數(shù)校驗(yàn)Validator框架,感興趣的朋友一起看看吧2022-06-06
SpringBoot之controller參數(shù)校驗(yàn)詳解
介紹了Java中使用@Validated和@Valid進(jìn)行參數(shù)校驗(yàn)的方法,包括不同標(biāo)簽的使用場景、基本屬性和一些常用的注解類型,同時(shí),還討論了如何在控制器中使用這些校驗(yàn)標(biāo)簽,以及如何處理校驗(yàn)結(jié)果和自定義錯(cuò)誤消息,最后,還介紹了如何實(shí)現(xiàn)分組校驗(yàn)和嵌套校驗(yàn),并提供了一些示例代碼2024-11-11
利用java和sqlserver建立簡易圖書管理系統(tǒng)的完整步驟
圖書館管理系統(tǒng)是圖書館管理工作中不可缺少的部分,它對(duì)于圖書館的管理者和使用者都非常重要,下面這篇文章主要給大家介紹了關(guān)于利用java和sqlserver建立簡易圖書管理系統(tǒng)的完整步驟,需要的朋友可以參考下2022-06-06

