spring boot自动化配置多数据源

spring boot多数据源支持中讲了我在项目中做多数据源支持的配置方式,但是由于服务很多,每个项目都要写这些配置就重复性太多了,考虑到提出一个common的工具包给其他服务使用,又因为我们的spring boot的一大特点就是自动化配置,所以在看了一些源码和自动化配置的尝试后,尝试出了一种方式,经验有限希望不要误导大家,接下来的方式仅供参考。

明确目的

  1. 我要提供一个stater的jar包出来给其他服务使用。
  2. 我想要的想过是使用的服务导入jar包后不需要其他的操作,只通过配置就可以完成多数据源支持
  3. 配置的形式如下:
#连接池配置
spring:
  datasource:
    multi:  
	  #name的第一个为默认数据源
      name: db1,db2
      db1: 
        filters: stat
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db1
        username: root
        password: 123456
        mapperxmlpath: classpath:mapper/db1/*.xml
        mapperpath: com.example.demo.mapper.db1
        configlocation: classpath:public/mybatis-configuration.xml
      db2: 
        filters: stat
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db2
        username: root
        password: 123456
        mapperxmlpath: classpath:mapper/db2/*.xml
        mapperpath: com.example.demo.mapper.db2
        configlocation: classpath:public/mybatis-configuration.xml
注:配置中数据源的名字的第一个为默认数据源,这里的配置默认数据源就是db1

需要特别指出的是

  • mapperxmlpath 为spring boot多数据源支持中说到过的对应的数据源的mapper.xml文件路径

  • mapperpath为mapper.xml对应的java接口

  • configlocation为mybatis的配置文件

所以说最终希望的效果就是这样,导入stater的jar包,然后配置上面的配置就可以使用。下面详细来说一下我的实现过程。

原理思路

因为spring boot提供了自动化配置的方式,就是autoconfig,但是在spring boot version 1.5.10中尝试过使用autoconfig的方式去配置,事务支持一直没有成功。所以说这里我采用的是另外一种方式就是ApplicationContextInitializer + BeanDefinitionRegistryPostProcessor 的方式去配置的。

实现的原理其实就是在spring提供的自动化配置方案spring.factories中添加我们自己加载数据源的ApplicationContextInitializer,然后在ApplicationContextInitializer初始化的时候读取配置数据源信息,添加我们自己的BeanDefinitionRegistryPostProcessor,在spring boot启动的时候就会调用我们的BeanDefinitionRegistryPostProcessor完成我们的spring boot多数据源的自动化加载与配置。

创建一个springboot项目

  • springboot 1.5.14.RELEASE
  • jdk1.8

因为我们是做一个stater,所以我们没有使用springapplication,所以删掉启动类。 调整后的pom文件如下所示:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.multipledatasource</groupId>
  <artifactId>multipledatasource-spring-boot-starter</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <packaging>jar</packaging>
  
  <name>multiple-datasource</name>
  <description>Demo project for Spring Boot</description>
  
  <properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<druid.version>1.1.2</druid.version>
		<mybatis.version>1.3.2</mybatis.version>
		<spring-boot.version>1.5.14.RELEASE</spring-boot.version>
		<maven.compiler.source>1.8</maven.compiler.source>    
        <maven.compiler.target>1.8</maven.compiler.target>    
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
  </properties>
  
  <dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>${mybatis.version}</version>
		</dependency>
		
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>test</scope>
        </dependency>

		<!-- <dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>druid-spring-boot-starter</artifactId>
		    <version>${druid.version}</version>
		</dependency> -->
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		
		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
		
		<!-- 支持的数据库 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		
		
	</dependencies>

	<dependencyManagement>
        <dependencies>
            <dependency>
                <!-- Import dependency management from Spring Boot -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

创建配置数据源

1)创建spring.factories

springboot提供了一种自动化的配置方案就是在spring.factories中加入我们自己的initializer,如图所示

这里我们先定义好我们的initializer的路径,内容如下:

# 添加自定义bean的处理,通过initializer
org.springframework.context.ApplicationContextInitializer=\
com.multiplesource.config.MultiInitializer

2)创建MultiInitializer

public class MultiInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		Environment ev = applicationContext.getEnvironment();
	
	}
}

我们需要在MultiInitializer初始化的时候加载配置的数据源信息,并且添加到我们自己的BeanDefinitionRegistryPostProcessor中以供使用,所以说我们需要创建我们自己的javabean以供装载我们配置的多数据源信息,我这里提供了两个类MultiDataSourceProperties和SingleDataSourceProperties,SingleDataSourceProperties就是对应于每个属于的配置信息,MultiDataSourceProperties就是维护了配置的多个数据源的信息。这里就不在贴代码了,主要看下MultiInitializer的实现吧,MultiInitializer的完整代码如下所示:

public class MultiInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		Environment ev = applicationContext.getEnvironment();
		MultiDataSourceProperties pp = new MultiDataSourceProperties();
		initProperties(pp,ev);
		//添加我们自己定义的BeanDefinitionRegistryPostProcessor,以供创建数据源的时候使用
		applicationContext.addBeanFactoryPostProcessor(new MultiDataSourceProcessor(pp));	
	}
	
	/**
	 * 加载环境属性变量
	 * @param prop
	 * @param env
	 */
	private void initProperties(MultiDataSourceProperties prop,Environment env){
		String datasourcenames = env.getProperty(MultiDataSourceProperties.PREFIX+".name");
		if(StringUtils.isEmpty(datasourcenames)) {
			return ;
		}
		String[] sourcenames = datasourcenames.split(",");
		prop.setNames(sourcenames);
		Map<String,SingleDataSourceProperties> sps = new HashMap<String,SingleDataSourceProperties>();
		prop.setProperties(sps);
		Field[] fields = SingleDataSourceProperties.class.getDeclaredFields();
		for(int i=0;i<sourcenames.length;i++) {
			SingleDataSourceProperties sp = new SingleDataSourceProperties();
			sps.put(sourcenames[i], sp);
			StringBuffer realnamebuf = new StringBuffer(MultiDataSourceProperties.PREFIX);
			realnamebuf.append(".");
			realnamebuf.append(sourcenames[i]);
			realnamebuf.append(".");
			for(int k=0;k<fields.length;k++) {
				Field field = fields[k];
				String fieldname = field.getName();
				String realname = realnamebuf.toString()+fieldname;
				if(env.containsProperty(realname)) {
					try {
						String firstLetter = fieldname.substring(0,1).toUpperCase();
						String methodname_set = "set"+ firstLetter + fieldname.substring(1);
						Method setMethod = SingleDataSourceProperties.class.getMethod(methodname_set, new Class[]{field.getType()});
						setMethod.invoke(sp, new Object[]{env.getProperty(realname)});
					}catch (Exception e) {
					}
				}
			}
		}
	}

}

接下来就是我们最重要核心的一步了,创建我们自己的BeanDefinitionRegistryPostProcessor,并维护一个MultiDataSourceProperties对象

3)创建MultiDataSourceProcessor 这个对象主要实现的接口是BeanDefinitionRegistryPostProcessor,通过重写postProcessBeanFactory和postProcessBeanDefinitionRegistry,这样springboot在启动的时候初始化bean的期间就会自动调用完成datasource和mapper的创建和初始化工作。所以这个类所处理的事情有:

  • 创建每一个数据源对象(并初始化,给出默认数据源标记为isPrimary=true)
  • 创建事务并绑定对应的数据源
  • 创建mapper扫码路径和配置文件所在的路径

下面给出MultiDataSourceProcessor的具体实现

package com.multiplesource.config;

import java.io.IOException;
import java.sql.SQLException;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
import org.springframework.context.annotation.ScopeMetadata;
import org.springframework.context.annotation.ScopeMetadataResolver;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.StringUtils;

/**
 * 根据数据配置,动态创建bean交给spring管理
 * 并给定扫描对于的数据源的mapper
 * 
 * @author zhuzhoutong
 *
 */
public class MultiDataSourceProcessor implements BeanDefinitionRegistryPostProcessor{
	
	/**
	 * 数据源bean的前缀
	 * 数据源的bean名称就是dataSource_+(spring.datasource.multi的name属性值)
	 */
	public static final String DATASOURCPREFIX = "dataSource_";
	
	public static final String TRANSACTIONPREFIX = "transAction_";
	
	public static final String SQLSESSIONFACTORYPREFIX = "sqlSessionFactory_";
	
	public MultiDataSourceProperties datasourceproperties;
	
    private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
    private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

    public MultiDataSourceProcessor(MultiDataSourceProperties datasourceproperties) {
    	this.datasourceproperties = datasourceproperties;
	}
    
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 这里可以设置属性
		initProperties(beanFactory);
    }

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    	registerDataSourceBeans(registry);
        
    }
    
    private void registerDataSourceBeans(BeanDefinitionRegistry registry) {
    	String[] names = datasourceproperties.getNames();
    	if(names==null) {
    		return;
    	}
    	for(int i=0;i<names.length;i++) {
    		String name = names[i];
    		boolean isPrimary = false;
    		String datasourcename = DATASOURCPREFIX+name;
    		String transactionname = TRANSACTIONPREFIX+name;
    		String sqlsessionFactoryname = SQLSESSIONFACTORYPREFIX+name;
    		if(i==0) {
    			isPrimary = true;
    		}
    		//数据源
        	registerBean(registry, datasourcename, DruidDataSource.class,isPrimary);
        	//tansaction
        	registerBean(registry, transactionname, DataSourceTransactionManager.class,isPrimary);
        	//sessionfactory
        	registerBean(registry, sqlsessionFactoryname, SqlSessionFactoryBean.class,isPrimary);
        	//mapperscan
        	registerMapperInfo(registry,sqlsessionFactoryname,datasourceproperties.getProperties().get(name));
    	}
    }

    private void registerMapperInfo(BeanDefinitionRegistry registry,String sqlseesionfactoryname,SingleDataSourceProperties prop) {
    	ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

        scanner.setSqlSessionFactoryBeanName(sqlseesionfactoryname);
        String[] basepackages = prop.getMapperpath().split(",");
        scanner.registerFilters();
        scanner.doScan(basepackages);
	}

	private void registerBean(BeanDefinitionRegistry registry, String name, Class<?> beanClass,boolean isPrimary){
    	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        abd.setPrimary(isPrimary);
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
        // 可以自动生成name
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, registry));
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }

    private void initProperties(ConfigurableListableBeanFactory beanFactory) {
    	String[] names = datasourceproperties.getNames();
    	if(names==null) {
    		return ;
    	}
    	for(int i=0;i<names.length;i++) {
    		String name = names[i];
    		SingleDataSourceProperties datasourceprop = datasourceproperties.getProperties().get(name);
    		String datasourcename = DATASOURCPREFIX+name;
    		String transactionname = TRANSACTIONPREFIX+name;
    		String sessionfactoryname = SQLSESSIONFACTORYPREFIX+name;
    		
    		//datasource
    		initDataSource(datasourceprop,datasourcename,(DruidDataSource)beanFactory.getBean(datasourcename));
    		
    		//transaction
    		BeanDefinition transactionbd = beanFactory.getBeanDefinition(transactionname); 
    		MutablePropertyValues transactionbdpv =  transactionbd.getPropertyValues();  
    		transactionbdpv.addPropertyValue("dataSource", beanFactory.getBean(datasourcename));

    		//sqlsessionfactory
    		BeanDefinition bdd = beanFactory.getBeanDefinition(sessionfactoryname);
            MutablePropertyValues mpvd =  bdd.getPropertyValues();  
            mpvd.addPropertyValue("dataSource", beanFactory.getBean(datasourcename));
            try {
    			mpvd.addPropertyValue("mapperLocations", new PathMatchingResourcePatternResolver().getResources(datasourceprop.getMapperxmlpath()));
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
            if(!StringUtils.isEmpty(datasourceprop.getConfiglocation())) {
            	mpvd.addPropertyValue("configLocation", new DefaultResourceLoader().getResource(datasourceprop.getConfiglocation()));
            }
    	}
    }


    /**
     * 初始化数据源
     * @param properties
     * @param datasourcename
     * @param dataSource
     */
    private void initDataSource(SingleDataSourceProperties properties,String datasourcename,DruidDataSource dataSource) {
    	dataSource.setName(datasourcename);
        if (properties.getUrl() != null) {
            dataSource.setUrl(properties.getUrl());
        }
        if (properties.getUsername() != null) {
            dataSource.setUsername(properties.getUsername());
        }
        if (properties.getPassword() != null) {
            dataSource.setPassword(properties.getPassword());
        }
        if (properties.getDriverClassName() != null) {
            dataSource.setDriverClassName(properties.getDriverClassName());
        }
        if (properties.getInitialSize() != null) {
            dataSource.setInitialSize(properties.getInitialSize());
        }
        if (properties.getMaxActive() != null) {
            dataSource.setMaxActive(properties.getMaxActive());
        }
        if (properties.getMinIdle() != null) {
            dataSource.setMinIdle(properties.getMinIdle());
        }
        if (properties.getMaxWait() != null) {
            dataSource.setMaxWait(properties.getMaxWait());
        }
        if (properties.getPoolPreparedStatements() != null) {
            dataSource.setPoolPreparedStatements(properties.getPoolPreparedStatements());
        }
        if (properties.getMaxOpenPreparedStatements() != null) {
            dataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements());
        }
        if (properties.getMaxPoolPreparedStatementPerConnectionSize() != null) {
            dataSource.setMaxPoolPreparedStatementPerConnectionSize(properties.getMaxPoolPreparedStatementPerConnectionSize());
        }
        if (properties.getValidationQuery() != null) {
            dataSource.setValidationQuery(properties.getValidationQuery());
        }
        if (properties.getValidationQueryTimeout() != null) {
            dataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout());
        }
        if (properties.getTestWhileIdle() != null) {
            dataSource.setTestWhileIdle(properties.getTestWhileIdle());
        }
        if (properties.getTestOnBorrow() != null) {
            dataSource.setTestOnBorrow(properties.getTestOnBorrow());
        }
        if (properties.getTestOnReturn() != null) {
            dataSource.setTestOnReturn(properties.getTestOnReturn());
        }
        if (properties.getTimeBetweenEvictionRunsMillis() != null) {
            dataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
        }
        if (properties.getMinEvictableIdleTimeMillis() != null) {
            dataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
        }
        if (properties.getMaxEvictableIdleTimeMillis() != null) {
            dataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis());
        }
        try {
            if (properties.getFilters() != null) {
                dataSource.setFilters(properties.getFilters());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
	}

}

数据源的名字默认为"dataSource_"+配置的数据源名字,如:dataSource_db1,dataSource_db2 默认的事务名称为"transAction_"+配置的数据源名字 到此为止我们的stater就写完了,下面我们来测试一下多数据源的支持和事务的支持。

测试

1)创建一个demo项目,并且导入我们的stater。

<!-- 自动配置多数据源starter -->
<dependency>
	<groupId>com.multipledatasource</groupId>
	<artifactId>multipledatasource-spring-boot-starter</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>

2)配置我们的配置文件

spring:
  datasource:
    multi:  
      #name的第一个为默认数据源
      name: db1,db2
      db1: 
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db1
        username: root
        password: 123456
        mapperxmlpath: classpath:mapper/db1/*.xml
        mapperpath: com.example.demo.mapper.db1
        configlocation: classpath:public/mybatis-configuration.xml
      db2: 
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db2
        username: root
        password: 123456
        mapperxmlpath: classpath:mapper/db2/*.xml
        mapperpath: com.example.demo.mapper.db2
        configlocation: classpath:public/mybatis-configuration.xml

3)编写我们的controller和service,mapper和xml

Db1Service.java

@Component
public class Db1Service {
	
	@Autowired
	public UserMapper_db1 db1;
	
	/**
	 * 查询demo
	 * @return
	 */
	public User selectuser() {
		return db1.selectuser();
	}

	/**
	 * 添加demo
	 * @param user
	 * @return
	 */
	//默认事物可以不用写名称
	//直接使用的是@Transactional
	@Transactional
	public Map<String,Object> insert(User user) {
		 db1.insert(user);
		 if("zhu".equals(user.getName())) {
			throw new RuntimeException("回滚操作");
		 }
		 Map<String,Object> res = new HashMap<String,Object>();
		 res.put("status", 0);
		 res.put("message", "添加成功");
		 return res;
	}
	
}

Db2Service.java

@Component
public class Db2Service {
	
	@Autowired
	public UserMapper_db2 db2;

	/**
	 * 查询demo
	 * @return
	 */
	public User selectuser() {
		return db2.selectuser();
	}

	/**
	 * 添加demo
	 * @param user
	 * @return
	 */
	//默认事物可以不用写名称
	//直接使用的是@Transactional
	@Transactional("transAction_db2")
	public Map<String,Object> insert(User user) {
		 db2.insert(user);
		 if("zhu".equals(user.getName())) {
			throw new RuntimeException("回滚操作");
		 }
		 Map<String,Object> res = new HashMap<String,Object>();
		 res.put("status", 0);
		 res.put("message", "添加成功");
		 return res;
	}
}
注:默认数据源可以不用明确指出,其他数据源需要指出,如Db2Service中的@Transactional("transAction_db2")

UserMapper_db1.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.example.demo.mapper.db1.UserMapper_db1">

	<!-- 查询用户信息 -->
	<select id="selectuser" resultType="com.example.demo.entity.User">
		select * from usertable where id = '1111'
	</select>
	
	<insert id="insert" parameterType="com.example.demo.entity.User">
		INSERT into usertable (id,name) values(#{id},#{name})
	</insert>
</mapper>

UserMapper_db2.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.example.demo.mapper.db2.UserMapper_db2">

	<!-- 查询用户信息 -->
	<select id="selectuser" resultType="com.example.demo.entity.User">
		select * from usertable where id = '1111'
	</select>
	
	<insert id="insert" parameterType="com.example.demo.entity.User">
		INSERT into usertable (id,name) values(#{id},#{name})
	</insert>
</mapper>

最后看看我们的controller

@RestController
public class Contorller {

	@Autowired
	public Db1Service db1service;
	
	@Autowired
	public Db2Service db2service;
	
	@RequestMapping("/hello1")
	public User selectUser1() {
		return db1service.selectuser();
	}
	
	@RequestMapping("/add1")
	public Map<String,Object> add1(User user) {
		return db1service.insert(user);
	}
	
	@RequestMapping("/hello2")
	public User selectUser2() {
		return db2service.selectuser();
	}
	
	@RequestMapping("/add2")
	public Map<String,Object> add2(User user) {
		return db2service.insert(user);
	}
}

4)调用测试

db1查询:

db1正常添加:

db1事务回滚测试:

可以看到第二次的添加被回滚了,没有添加成功,db2也是一样的操作过程,这里就不在贴图了,至此多数据源的自动化配置以及完成。

另外如果有兴趣或者发现有问题的地方还请多多指正,代码已上传至码云
地址:[数据源自动化配置](https://gitee.com/qingyitianxia/multidatasource)

转载于:https://my.oschina.net/u/3721891/blog/1831564

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值