- A+
有一定开发经验的同学都知道,在业务发展到一定瓶颈的时候,单数据源是无法支撑应用正常的读操作以及写操作,这个时候我们就需要对应用进行改造,实现 读写 分离的方式来优化整个应用的架构,今天这篇文章就带大家了解mybatis如何实现读写分离的功能。
准备工作
各个组件的版本信息
SpringBoot 2.1.0.RELEASE
mybatis 2.1.3
mysql 5.7
目录结构
这里我们实现的是mybatis动态数据源加载,具体思路:
自定义SqlSession模板,继承mybatis中的SqlSessionTemplate并实现具体方法,这里自定义了CustomSqlSessionTemplate类用来设置具体加载的数据源。使用HashMap容器来存储相对应的数据源名称,比如设置 “写” 数据源,它的key可以对应为“write”,设置“读”数据源,它的key可以对应为“read”。在使用具体数据源的时候,可以使用当前线程的变量ThreadLocal获取相对应的数据源,然后进行CRUD操作。
我们先来看下具体目录结构:
上述文件中,我们只需要关心datasource目录下,动态数据源的实现。接下来我们来分析下具体文件内容
applcation.yml
spring:
datasource:
write:
db:
url: jdbc:mysql://127.0.0.1:3306/mybatis?autoReconnect=true&useSSL=false&connectTimeout=10000
username: root
password: mengxi
read:
db:
url: jdbc:mysql://127.0.0.1:3306/mybatis1?autoReconnect=true&useSSL=false&connectTimeout=10000
username: root
password: mengxi
server:
port: 8001
上面的配置文件定义了两个数据源,一个 write、read。这里笔者将write数据源设置为主数据源,也就是在Spring加载配置文件的时候将write数据源设置为当前数据源。如果数据源较多的话,可以设置一个动态数据源表,在加载的时候首先遍历动态数据源表中的数据,然后对所有数据源进行加载。
mybatis数据源配置类
package com.mengxi.summary.multiple.mybatis.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.mengxi.summary.multiple.mybatis.config.datasource.CustomSqlSessionTemplate;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* @Description mysql对应的mybatis配置信息
* @Author mengxi
* @date 2020-07-15 10:28
*/
@Configuration
@MapperScan(basePackages = {"com.mengxi.summary.multiple.mybatis.mapper"}, sqlSessionTemplateRef = "sqlSessionTemplate")
public class MysqlForMybatisConfiguration {
@Value("${spring.datasource.write.db.url}")
private String url;
@Value("${spring.datasource.write.db.username}")
private String username;
@Value("${spring.datasource.write.db.password}")
private String password;
/**
* mybatis mapper resource 路径
*/
private final static String MAPPER_PATH = "classpath:mapper/*.xml";
public static final Integer INIT_SIZE = 5;
public static final Integer MIN_IDLE = 10;
public static final Integer MAX_ACTIVE = 100;
public static final Integer MAX_WAIT = 60000;
@Primary
@Bean(name = "basicDataSource")
public DataSource basicDataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
// 配置初始化大小、最小、最大
druidDataSource.setInitialSize(INIT_SIZE);
druidDataSource.setMinIdle(MIN_IDLE);
druidDataSource.setMaxActive(MAX_ACTIVE);
druidDataSource.setPoolPreparedStatements(false);
// 配置获取连接等待超时的时间
druidDataSource.setMaxWait(MAX_WAIT);
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
druidDataSource.setTimeBetweenEvictionRunsMillis(2000);
// 配置一个连接在池中最小生存的时间,单位是毫秒
druidDataSource.setMinEvictableIdleTimeMillis(600000);
druidDataSource.setMaxEvictableIdleTimeMillis(900000);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTestOnBorrow(true);
druidDataSource.setTestOnReturn(false);
druidDataSource.setKeepAlive(true);
// 配置监控统计拦截的filters
druidDataSource.setFilters("stat");
druidDataSource.init();
return druidDataSource;
}
@Bean(name = "mysqlTransactionManager")
public DataSourceTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(basicDataSource());
}
@Bean(name = "mysqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("basicDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//添加mapper 扫描路径
PathMatchingResourcePatternResolver
pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(pathMatchingResourcePatternResolver.getResources(MAPPER_PATH));
//设置datasource
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
/**
* 数据源模板
*
* @param factorySystem
* @return
* @throws Exception
*/
@Bean(name = "sqlSessionTemplate")
public CustomSqlSessionTemplate sqlSessionTemplate(@Qualifier("mysqlSessionFactory")
SqlSessionFactory factorySystem) throws Exception {
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
sqlSessionFactoryMap.put("basic", factorySystem);
CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(factorySystem);
customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap);
return customSqlSessionTemplate;
}
/**
* 获取数据源
*
* @param
* @return
*/
public DataSource getDataSource(String url, String username, String password) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
public org.apache.ibatis.session.Configuration configuration() {
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
return configuration;
}
/**
* 创建数据源
*
* @param dataSource
* @return
*/
public SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setVfs(SpringBootVFS.class);
bean.setTypeAliasesPackage("com.mengxi.summary.multiple.mybatis.domain");
bean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(MAPPER_PATH));
bean.setConfiguration(configuration());
return bean.getObject();
}
}
MysqlForMybatisConfiguration配置类,设置主数据源,设置对应的mapper类以及配置类,创建数据源,获取数据源等方法。
自定义SqlSeesion模板 CustomSqlSessionTemplate
package com.mengxi.summary.multiple.mybatis.config.datasource;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.*;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.*;
/**
* 数据源模板
*
* @author mengxi
* @date 2020年07月15日17:51:39
*/
public class CustomSqlSessionTemplate extends SqlSessionTemplate {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
private SqlSessionFactory defaultTargetSqlSessionFactory;
public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
this.targetSqlSessionFactorys = targetSqlSessionFactorys;
}
public Map<Object, SqlSessionFactory> getTargetSqlSessionFactorys() {
return targetSqlSessionFactorys;
}
public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
}
public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
.getEnvironment().getDataSource(), true));
}
public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
super(sqlSessionFactory, executorType, exceptionTranslator);
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] {SqlSession.class},
new SqlSessionInterceptor());
this.defaultTargetSqlSessionFactory = sqlSessionFactory;
}
@Override
public SqlSessionFactory getSqlSessionFactory() {
SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(
DataSourceContextHolder.getDatasourceType());
if (targetSqlSessionFactory != null) {
return targetSqlSessionFactory;
} else if (defaultTargetSqlSessionFactory != null) {
return defaultTargetSqlSessionFactory;
} else {
Assert.notNull(targetSqlSessionFactorys,
"Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
Assert.notNull(defaultTargetSqlSessionFactory,
"Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
}
return this.sqlSessionFactory;
}
@Override
public Configuration getConfiguration() {
return this.getSqlSessionFactory().getConfiguration();
}
@Override
public ExecutorType getExecutorType() {
return this.executorType;
}
@Override
public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
return this.exceptionTranslator;
}
/**
* {@inheritDoc}
*/
@Override
public <T> T selectOne(String statement) {
return this.sqlSessionProxy.<T>selectOne(statement);
}
/**
* {@inheritDoc}
*/
@Override
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.<T>selectOne(statement, parameter);
}
/**
* {@inheritDoc}
*/
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.sqlSessionProxy.<K, V>selectMap(statement, mapKey);
}
/**
* {@inheritDoc}
*/
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.sqlSessionProxy.<K, V>selectMap(statement, parameter, mapKey);
}
/**
* {@inheritDoc}
*/
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return this.sqlSessionProxy.<K, V>selectMap(statement, parameter, mapKey, rowBounds);
}
/**
* {@inheritDoc}
*/
@Override
public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.<E>selectList(statement);
}
/**
* {@inheritDoc}
*/
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.<E>selectList(statement, parameter);
}
/**
* {@inheritDoc}
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.<E>selectList(statement, parameter, rowBounds);
}
/**
* {@inheritDoc}
*/
@Override
public void select(String statement, ResultHandler handler) {
this.sqlSessionProxy.select(statement, handler);
}
/**
* {@inheritDoc}
*/
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, handler);
}
/**
* {@inheritDoc}
*/
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
}
/**
* {@inheritDoc}
*/
@Override
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}
/**
* {@inheritDoc}
*/
@Override
public int insert(String statement, Object parameter) {
return this.sqlSessionProxy.insert(statement, parameter);
}
/**
* {@inheritDoc}
*/
@Override
public int update(String statement) {
return this.sqlSessionProxy.update(statement);
}
/**
* {@inheritDoc}
*/
@Override
public int update(String statement, Object parameter) {
return this.sqlSessionProxy.update(statement, parameter);
}
/**
* {@inheritDoc}
*/
@Override
public int delete(String statement) {
return this.sqlSessionProxy.delete(statement);
}
/**
* {@inheritDoc}
*/
@Override
public int delete(String statement, Object parameter) {
return this.sqlSessionProxy.delete(statement, parameter);
}
/**
* {@inheritDoc}
*/
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
/**
* {@inheritDoc}
*/
@Override
public void commit() {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
}
/**
* {@inheritDoc}
*/
@Override
public void commit(boolean force) {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
}
/**
* {@inheritDoc}
*/
@Override
public void rollback() {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
}
/**
* {@inheritDoc}
*/
@Override
public void rollback(boolean force) {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
}
/**
* {@inheritDoc}
*/
@Override
public void clearCache() {
this.sqlSessionProxy.clearCache();
}
/**
* {@inheritDoc}
*/
@Override
public Connection getConnection() {
return this.sqlSessionProxy.getConnection();
}
/**
* {@inheritDoc}
*
* @since 1.0.2
*/
@Override
public List<BatchResult> flushStatements() {
return this.sqlSessionProxy.flushStatements();
}
/**
* Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
* unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
* the {@code PersistenceExceptionTranslator}.
*/
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = getSqlSession(
CustomSqlSessionTemplate.this.getSqlSessionFactory(),
CustomSqlSessionTemplate.this.executorType,
CustomSqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (CustomSqlSessionTemplate.this.exceptionTranslator != null
&& unwrapped instanceof PersistenceException) {
Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory());
}
}
}
}
设置自定义SqlSeesion模板,这个类中我们只需要关注 setTargetSqlSessionFactorys
与 getSqlSessionFactory
方法。
setTargetSqlSessionFactorys
在自定义模板中,设置变量 targetSqlSessionFactorys,也就是上述说的 HashMap 容器,在load数据源的时候设置对应的数据源。
getSqlSessionFactory
SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(
DataSourceContextHolder.getDatasourceType());
实现动态数据源其实就是上面这段代码,在获取 目标数据源工厂的时候,获取当前线程设置的数据源类型即可。
数据库上下文切换类 DataSourceContextHolder
package com.mengxi.summary.multiple.mybatis.config.datasource;
import java.util.ArrayList;
import java.util.List;
/**
* 数据库上下文切换
*
* @author mengxi
* @date 2020年07月15日17:52:12
*/
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static List<String> dataSourceKeys = new ArrayList<>();
/**
* 设置当前数据库。
*
* @param dbType
*/
public static void setDatasourceType(String dbType) {
contextHolder.set(dbType);
}
public static void setDefaultDataSource() {
}
/**
* 取得当前数据源。
*
* @return
*/
public static String getDatasourceType() {
String str = contextHolder.get();
return str;
}
/**
* 清除上下文数据
*/
public static void clearDatasourceType() {
contextHolder.remove();
}
/**
* @param dataSourceId
* @return
*/
public static boolean containsDataSource(String dataSourceId) {
return dataSourceKeys.contains(dataSourceId);
}
}
在进行业务库操作时,通过调用setDatasourceType
设置当前数据源类型,是读库还是写库。
数据源管理类 DataSourceManager
package com.mengxi.summary.multiple.mybatis.config.datasource;
import com.mengxi.summary.multiple.mybatis.config.MysqlForMybatisConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数据源管理
*
* @author mengxi
* @date 2020年07月15日18:00:55
*/
@Slf4j
@Component
public class DataSourceManager {
@Value("${spring.datasource.read.db.url}")
private String url;
@Value("${spring.datasource.read.db.username}")
private String username;
@Value("${spring.datasource.read.db.password}")
private String password;
@Resource
private CustomSqlSessionTemplate sqlSessionTemplate;
@Resource
private MysqlForMybatisConfiguration mysqlForMybatisConfiguration;
/**
* 初始或者重载入数据源
*
* @throws Exception
*/
@PostConstruct
public void loadDataSource() throws Exception {
//后续数据源可以设置为数据库中保存,此处可以循环加载数据源
Map<Object, SqlSessionFactory> newSqlSessionFactoryMap = new HashMap<>(16);
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = sqlSessionTemplate.getTargetSqlSessionFactorys();
//如果存在多集合 可使用 for() 循环来配置数据源
SqlSessionFactory sqlSessionFactory = mysqlForMybatisConfiguration.createSqlSessionFactory(
mysqlForMybatisConfiguration.getDataSource(url, username, password));
newSqlSessionFactoryMap.put("read", sqlSessionFactory);
newSqlSessionFactoryMap.putAll(sqlSessionFactoryMap);
this.sqlSessionTemplate.setTargetSqlSessionFactorys(newSqlSessionFactoryMap);
}
}
数据源管理类设置读库的数据源加载内容,在Spring加载的时候对当前读库数据源进行初始化,@PostConstruct注解的作用就是初始化实例。从loadDataSource
方法注释可以看出,可以设置“动态数据源表”,在初始化的时候读取所有数据源信息进行加载,然后利用for循环,将加载好的数据源信息容器设置到 自定义SqlSeesion模板CustomSqlSessionTemplate中,在使用的时候,通过当前线程变量直接get出所需要使用的数据源类型即可。
下面我们来看看如何在业务代码中具体使用动态数据源。
用户实现类 UserServiceImpl
package com.mengxi.summary.multiple.mybatis.serivce.impl;
import com.mengxi.summary.multiple.mybatis.config.datasource.DataSourceContextHolder;
import com.mengxi.summary.multiple.mybatis.domain.User;
import com.mengxi.summary.multiple.mybatis.mapper.UserMapper;
import com.mengxi.summary.multiple.mybatis.serivce.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Description 用户实现类
* @Author mengxi
* @date 2020-07-16 17:12
* @Copyright 2019 Alibaba.com All right reserved.
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public User selectUser() {
DataSourceContextHolder.setDatasourceType("read");
User user = userMapper.selectByPrimaryKey(1L);
return user;
}
@Override
public void insert(User user) {
DataSourceContextHolder.setDatasourceType("basic");
userMapper.insert(user);
}
}
笔者在这里举了两个简单的例子
一个是 查询所有用户信息,使用的是 读库。
一个是 增加用户信息,使用的是 写库(这里笔者设置类型是 "basic")
现场演示 UserServiceTest
package com.mengxi.summary.multiple.mybatis.serviceTest;
import com.mengxi.summary.multiple.mybatis.SummaryMultipleMybatisApplication;
import com.mengxi.summary.multiple.mybatis.domain.User;
import com.mengxi.summary.multiple.mybatis.serivce.UserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @Description 用户测试类
* @Author mengxi
* @date 2020-07-16 17:18
* @Copyright 2019 Alibaba.com All right reserved.
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {SummaryMultipleMybatisApplication.class})
public class UserServiceTest {
@Resource
private UserService userService;
@Test
public void userServiceTest() {
log.info("测试数据源read,读取read库 mybatis1 user 信息..");
User user = userService.selectUser();
log.info("user info -----> " + user.toString());
}
@Test
public void userServiceInsertTest() {
log.info("测试数据源basic,增加baisc数据库 mybatis user 信息..");
User user = new User();
user.setName("add datasource basic , my database name is mybatis,add info by userServiceInsertTest");
user.setAge("15");
userService.insert(user);
log.info("user id -----> " + user.getId());
}
}
演示的方法很简单,一个读库,一个写库。
mysql数据库
先来运行写库的测试方法。
写库中存在两条内容,运行测试类,设置age为 “15”。运行日志如下:
10:06:48.579 default [main] INFO c.m.s.m.m.s.UserServiceTest - 测试数据源basic,增加baisc数据库 mybatis user 信息..
10:06:48.956 default [main] INFO c.m.s.m.m.s.UserServiceTest - user id -----> 4
再来运行读库的测试方法。
读库中只存在一条信息,运行测试类。运行日志如下:
10:09:33.347 default [main] INFO c.m.s.m.m.s.UserServiceTest - 测试数据源read,读取read库 mybatis1 user 信息..
10:09:33.395 default [main] INFO c.alibaba.druid.pool.DruidDataSource - {dataSource-2} inited
10:09:33.548 default [main] INFO c.m.s.m.m.s.UserServiceTest - user info -----> User(id=1, name=我是read数据源,我的数据库名称是 mybatis1, age=10)
可以看到,运行读库后,将mybatis1中的数据罗列出来。
大家可以根据自己的数据源进行动态设置,可以设置一主多从等模式。
源码地址:mybaits动态数据源
- 我的微信
- 加好友一起交流!
-
- 微信公众号
- 关注公众号获取分享资源!
-