之前项目需要使用多数据库的功能,所以研究了下多数据库的配置,并且一并研究了多数据源的实现,但是因为多数据源网上资料较多,所以本不打算发文出来的。直到有位朋友需要使用多数据源功能,但是说实在的,网上能直接拿来照着代码拷贝下来直接用的文章还真不多,大多模棱两可或者干脆就是不知从哪抄了一半,看起来比较痛苦,所以在这里我就发一版自己亲测可用的完整版实现,方便初学者观看,也方便以后自己需要用的时候查阅。
说明:本文的部分代码来源于网络,但是因为查看了大量文章,所以已经不记得具体是引用了哪个文章了,若原作者发现本文,可与我联系。
首先注意的是,除了springboot和mybatis的基础引用,使用本文的方法来实现多数据源,需要引用druid线程池,在maven里加入以下引用即可。
<!-- druid线程池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
之后,在application.properties文件中增加数据源配置,如下:
d7.datasource.url=jdbc:dm://localhost:5236
d7.datasource.username=XUJN
d7.datasource.password=shihunshu
d7.datasource.driver-class-name=dm.jdbc.driver.DmDriver
mysql.datasource.url=jdbc:mysql://localhost:3306/demo
mysql.datasource.username=root
mysql.datasource.password=root
mysql.datasource.driver-class-name=com.mysql.jdbc.Driver
改配置中,我们可以通过命名很明确的区分开我们不同配置对应的数据库,以便于维护。
之后,我们需要一些辅助类来帮助实现多数据源功能,如下:
/**
* 作用:
* 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();
}
}
/**
* 列出所有的数据源key(常用数据库名称来命名)
* 注意:
* 1)这里数据源与数据库是一对一的
* 2)DatabaseType中的变量名称就是数据库的名称
*/
public enum DatabaseType {
d7,mysql
}
/**
* 动态数据源(需要继承AbstractRoutingDataSource)
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
之后,就是最关键的config类了,类中的一些关键配置会在注释中说明,直接看源码吧:
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.jdbc.datasource.DataSourceTransactionManager;
import com.alibaba.druid.pool.DruidDataSourceFactory;
/**
* 多数据源配置类
*/
@Configuration
@MapperScan(basePackages = "com.freddy")
public class MyBatisConfig {
@Autowired
private Environment env;
/**
* 达梦数据库数据源创建方法
* @return 创建好的数据源
* @throws Exception
*/
@Bean
public DataSource d7DataSource() throws Exception {
//注意,下方env.getProperty的参数,就是你在application.properties中配置的key
Properties props = new Properties();
props.put("driverClassName", env.getProperty("d7.datasource.driver-class-name"));
props.put("url", env.getProperty("d7.datasource.url"));
props.put("username", env.getProperty("d7.datasource.username"));
props.put("password", env.getProperty("d7.datasource.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* mysql数据库数据源创建方法
* @return 创建好的数据源
* @throws Exception
*/
@Bean
public DataSource mysqlDataSource() throws Exception {
//注意,下方env.getProperty的参数,就是你在application.properties中配置的key
Properties props = new Properties();
props.put("driverClassName", env.getProperty("mysql.datasource.driver-class-name"));
props.put("url", env.getProperty("mysql.datasource.url"));
props.put("username", env.getProperty("mysql.datasource.username"));
props.put("password", env.getProperty("mysql.datasource.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* 多数据源配置
* 特别说明:@Qualifier这个注解是用来指定注入类的,当通过@bean创建实体时,实体的name会默认使用方法名
* 所以@Qualifier的参数就是上面两个创建数据源的方法的方法名
* @param d7 达梦数据库数据源
* @param mysql mysql数据库数据源
* @return 动态数据源
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("d7DataSource") DataSource d7,
@Qualifier("mysqlDataSource") DataSource mysql) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.d7, d7);
targetDataSources.put(DatabaseType.mysql, mysql);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(d7);// 默认的datasource设置为myTestDbDataSource
return dataSource;
}
/**
* 根据数据源创建SqlSessionFactory
* @param d7 达梦数据库数据源
* @param mysql mysql数据库数据源
* @return SqlSessionFactory
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("d7DataSource") DataSource d7,
@Qualifier("mysqlDataSource") DataSource mysql) throws Exception{
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(this.dataSource(d7, mysql));
fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));
return fb.getObject();
}
/**
* 配置事务管理器
* @param dataSource 动态数据源
* @return DataSourceTransactionManager
* @throws Exception
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
至此为止,多数据源的基本配置就做好了,那么接下来怎么使用呢?首先我们准备了一个mapper,如下:
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* 用户mapper
* @author freddy
*/
@Mapper
@Repository("userMapper")
public interface UserMapper{
List<UserEntity> findUsers();
List<UserEntity> findUsersMysql();
}
该mapper里有两个抽象方法,第一个是用于达梦数据库查询用户的,一个是用于mysql数据库查询用户的。当然,这只是测试用的写法,实际项目应用中,应该是不同的数据源有单独的mapper。
之后在mapper.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.freddy.mfd7.test.UserMapper">
<select id="findUsers" resultType="com.freddy.mfd7.test.UserEntity">
select * from "demo";
</select>
<select id="findUsersMysql" resultType="com.freddy.mfd7.test.UserEntity">
select * from demo;
</select>
</mapper>
不同的数据库查询语句可能是不一样的,所以我们写好对应的sql,注入到对应的mapper中即可。
之后就是调用mapper了,为了方便我这里没分层,就直接在controller中调用mapper了。代码如下,需要注意的地方见注释:
import javax.annotation.Resource;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.freddy.mfd7.test.moreDatasource.DatabaseContextHolder;
import com.freddy.mfd7.test.moreDatasource.DatabaseType;
@ResponseBody
@Controller
public class DemoAction {
DataSourceFactory fac = new PooledDataSourceFactory();
@Resource(name="userMapper")
private UserMapper userMapper;
@RequestMapping("queryD7")
public String queryD7(){
/* 特别注意,在调用mapper之前,必须调用该方法来切换数据源,所以之前直接使用mapper作为dao层的项目,
可能要新增一个dao层,然后再在dao层中调用mapper了。当然,也可以通过其他方式实现,但是必须有切换数据源的操作。 */
DatabaseContextHolder.setDatabaseType(DatabaseType.d7);
return userMapper.findUsers().toString();
}
@RequestMapping("queryMysql")
public String queryMysql(){
DatabaseContextHolder.setDatabaseType(DatabaseType.mysql);
return userMapper.findUsersMysql().toString();
}
}
本文的全部代码就是这样,大家看后有什么不懂的,可以在评论或者私信里问我。