好无奈,csdn不知道怎么了,我上传了两次代码都没了。我只好传到博客园去了
笔者在最近的学习过程中,又整理了下springboot数据源的问题。平常我们用原生jdbc,或者是使用spring的jdbc,或者是jndi的jdbc对于多数据源的配置还是比较简单的。但是springboot的多数据源,因为比较陌生,所以就有点难了。
此次分享的多数据源是springboot集成了mybatis框架的基础上进行的。
**
1、单个数据源
**
平常接触比较多的还是单一数据源,单一数据源的配置比较简单。
@Configuration //该注解类似于spring配置文件
@MapperScan(basePackages="com.neo.mapper")
public class MyBatisConfig {
@Autowired
private Environment env;//注入了Environment实例,使用该实例可以去读取类路径下application.properties文件中的内容
/**
* 创建数据源
* 配置 dataSource
相当于 xml
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}" />
<property name="password" value="${password}"/>
</bean>
* @return
*/
@Bean
public DataSource myTestDb2DataSource() 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);
}
/**
* 根据数据源创建SqlSessionFactory
* 类似xml配置
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.study.bean"/>
</bean>
* @return
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception{
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);//指定数据源(这个必须有,否则报错)这边的ds其实就是上面的myTestDb2DataSource()(采用了@Bean的注解方式)
//下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));//指定基包
fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//指定xml文件位置
return fb.getObject();
}
}
单一数据源的配置到此结束,使用的时候,这个MyBatisConfig都不会被主动调用。
2.多数据源
做过很多国企的项目,都是涉及到多库多表的情况。所以这边也讨论下多数据库的情况
package com.neo.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.neo.common.datasourse.DatabaseType;
import com.neo.common.datasourse.DynamicDataSource;
/**
* springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)
* 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置
*/
@Configuration //该注解类似于spring配置文件
@MapperScan(basePackages="com.neo.mapper")
public class MyBatisConfig {
@Autowired
private Environment env;
/**
* 创建数据源
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* 配置 dataSource
*/
@Bean
public DataSource myTestDb2DataSource() 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 myTestDbDataSource() 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);
}
@Bean
@Primary
public DatabaseContextHolder dataSource(@Qualifier("myTestDbDataSource") DataSource myTestDbDataSource,@Qualifier("myTestDb2DataSource") DataSource myTestDb2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<Object,Object>();
targetDataSources.put(DatabaseType.mytestdb, myTestDbDataSource);
targetDataSources.put(DatabaseType.mytestdb2, myTestDb2DataSource);
DatabaseContextHolder dataSource = new DatabaseContextHolder();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(myTestDbDataSource);// 默认的datasource设置为myTestDbDataSource
return dataSource;
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DatabaseContextHolder ds) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
// 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
fb.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//
return fb.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DatabaseContextHolder dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
package com.neo.common.datasourse;
/**
* 列出所有的数据源key(常用数据库名称来命名)
* 注意:
* 1)这里数据源与数据库是一对一的
* 2)DatabaseType中的变量名称就是数据库的名称
*/
public enum DatabaseType {
mytestdb,mytestdb2
}
package com.neo.common.datasourse;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 作用:构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
* 1、保存一个线程安全的DatabaseType容器
*/
public class DatabaseContextHolder extends AbstractRoutingDataSource {
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<DatabaseType>();
public static void setDatabaseType(DatabaseType type){
contextHolder.set(type);
}
public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
//动态数据源(需要继承AbstractRoutingDataSource)
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
多数据源情况下,因为有默认的数据源,所以要是不想切换数据源的情况下,不用做任何操作。只有当要切换数据源的时候才要做切换数据源的操作
切换数据源的方式有两种:
2.1.springboot方法内设置数据源
//注意:首先设置了数据源的key,然后调用mapper(在mapper中会首先根据该key从动态数据源中查询出相应的数据源,之后取出连接进行数据库操作)
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);//DatabaseType里面配置了所有的数据源的名称,同步要配置的就是MybatisConfig.java里头,要增加对应的所有的数据源的配置
userMapper.insertUser(users.get(0).getUsername(), users.get(0).getPassword());
2.2.springboot集成aop进行数据源的切换
这种方式,其实有点业务和开发模式的思想的在里头。因为正常情况下,我们会把同一个数据库的操作放在一个dao里头。所以我们就可以利用aop把不同的dao都归类多对应的数据源里头就好了。、
增加一个切面类:
package com.neo.common.datasourse.dbaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.neo.common.datasourse.DatabaseContextHolder;
import com.neo.common.datasourse.DatabaseType;
import com.neo.dao.AopDao;
@Aspect
@Component
public class DataSourceAspect {
/**
* 使用空方法定义切点表达式
*/
@Pointcut("execution(* com.neo.dao.AopDao.*(..))")
public void declareJointPointExpression() {
}
/**
* 使用定义切点表达式的方法进行切点表达式的引入
*/
@Before("declareJointPointExpression()")
public void setDataSourceKey(JoinPoint point) {
//根据连接点的实例调用不同的数据源
// 连接点所属的类实例是AopDao
if (point.getTarget() instanceof AopDao) {
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb);
} else {// 连接点所属的类实例是UserDao(当然,这一步也可以不写,因为defaultTargertDataSource就是该类所用的mytestdb)
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb);
}
}
}
调用:
mapper.insertUser("testaop1", "testaop2");//显然,调用的方式跟单个数据源的情况一样,不做任何显式调用。aop帮我们做完一切
over 附上代码:下载
多数据源的原理图如下: