整体流程如下;
分为XMl配置方案与SpringBoot集成两个方案
1 与Spring集成采用XML配置,没有了Controller层,但是手动设置当前线程访问的数据库名,
项目整体目录结构:
自定义的数据源DynamicDataSource:
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 取得当前使用哪个数据源
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
数据源枚举:
public enum DBTypeEnum {
one("dataSource_one"), two("dataSource_two");
private String value;
DBTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
数据源存储获取处理中心:
public class DbContextHolder {
@SuppressWarnings("rawtypes")
private static final ThreadLocal contextHolder = new ThreadLocal<String>();
/**
* 设置数据源
* @param dbTypeEnum
*/
@SuppressWarnings("unchecked")
public static void setDbType(DBTypeEnum dbTypeEnum) {
contextHolder.set(dbTypeEnum.getValue());
}
/**
* 取得当前数据源
* @return
*/
public static String getDbType() {
return contextHolder.get().toString();
}
/**
* 清除上下文数据
*/
public static void clearDbType() {
contextHolder.remove();
}
}
配置中心:
<!-- 扫描Service层Bean注入到容器中 -->
<context:component-scan base-package="com.test.service" />
<!-- 开启注解 -->
<context:annotation-config/>
<bean id="dataSource_one" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="root" />
<property name="password" value="123123" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test01?characterEncoding=utf8&useSSL=true"/>
</bean>
<bean id="dataSource_two" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="root" />
<property name="password" value="123123" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test02?characterEncoding=utf8&useSSL=true"/>
</bean>
<bean id="dataSource" class="com.test.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSource_one" value-ref="dataSource_one" />
<entry key="dataSource_two" value-ref="dataSource_two" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource_two" />
</bean>
<!-- - - - - - - - sessionFactory - - - - - - - -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:sqlMapConfig.xml" />
</bean>
<!-- - - - - - - DAO - - - - - - - -->
<!-- 新添加表时要添加 的位置 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="mapperInterface" value="com.test.dao.user.UserMapper" />
</bean>
MyBatis配置:
sqlMapConfig:
<configuration>
<!-- - - - - - - 懒加载和缓存 - - - - - - - - - - -->
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- - - - - - - - 物理分页拦截器 - - - - - - - -->
<!-- - - - - - - -映射文件路径- - - - - - -->
<!-- 新添加表时要添加 的位置 -->
<mappers>
<mapper resource="com/test/dao/mapper/user/UserMapper.xml"/>
</mappers>
</configuration>
Service层与Dao层代码可具体看:
测试:
public class TestMain {
public static void main(String[] args) {
test();
}
public static void test() {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
DbContextHolder.setDbType(DBTypeEnum.two);//单前线程选择数据源
UserService userService=(UserService)ac.getBean("userServiceImpl");
User user = userService.getUserInfo(1);
System.out.println(user);
}
}
2 与SpringBoot的集成与上中相似,完全按照流程图中的设计,在Service层中通过注解的方式来设置访问的数据源。定义一个切面,拦截所有Service的线程,根据注解的值来设置当前线程访问 的数据源。
Service
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@DataSource(DataSourceType.READ)
public User getUserInfoByRead(Integer userId) {
// TODO Auto-generated method stub
return userMapper.selectUserById(userId);
}
@DataSource(DataSourceType.WRITE)
public User getUserInfoByWrite(Integer userId){
return userMapper.selectUserById(userId);
}
}
整合Mybatis:
@Configuration
@EnableConfigurationProperties({MasterDataSourceProperties.class,SlaveDataSourceProperties.class})
@MapperScan(basePackages = {"com.text.dao"})
public class MybatisPlusConfig {
private static final Logger logger=LoggerFactory.getLogger(MybatisPlusConfig.class);
@Autowired
private MasterDataSourceProperties masterDataSourceProperties;
@Autowired
private SlaveDataSourceProperties slaveDataSourceProperties;
@Bean("dataSource")
public ChooseDataSource dataSource() {
logger.info("-------------------数据源加载开始-------------");
DruidDataSource masterDataSource = new DruidDataSource();
masterDataSourceProperties.config(masterDataSource);
DruidDataSource slaveDataSource = new DruidDataSource();
slaveDataSourceProperties.config(slaveDataSource);
try {
masterDataSource.init();
slaveDataSource.init();
} catch (SQLException e) {
logger.error("数据源加载失败");
e.printStackTrace();
}
Map<Object,Object> map = new HashMap<Object,Object>();
map.put("read", masterDataSource);
map.put("write", slaveDataSource);
ChooseDataSource chooseDataSource = new ChooseDataSource();
chooseDataSource.setTargetDataSources(map);
chooseDataSource.setDefaultTargetDataSource(masterDataSource);
logger.info("-------------------数据源加载结束-------------");
return chooseDataSource;
}
}
切面拦截事件:
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
private static Logger log = LoggerFactory.getLogger(DataSourceAspect.class);
@Pointcut(value="execution(* com.text.service.impl.*.*.*(..))")
public void pointCut() {
};
@Before(value = "pointCut()")
public void before(JoinPoint point) {
DataSource dataSource = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(DataSource.class);
if (dataSource != null) {
log.debug("设置数据源为{}" ,dataSource.value());
HandleDataSource.putDataSource(dataSource.value());
} else {
log.error("设置数据源失败,将使用默认的数据源");
}
}
@After(value = "pointCut()")
public void after() {
HandleDataSource.setDefault(DataSourceType.WRITE);
log.debug("业务结束,设置默认数据源为write");
}
@AfterThrowing(value = "pointCut()")
public void afterThrowing() {
HandleDataSource.setDefault(DataSourceType.WRITE);
log.debug("业务出现异常,设置数据源为为write") ;
}
}
测试可通过:
http://localhost:8081/user/getUserInfoByWrite/1
http://localhost:8081/user/getUserInfoByRead/1
创建两个数据库,相同ID不同其他数据即可测试结果。
具体可查看代码。http://download.csdn.net/download/btwangzhi/10215520
http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247486593&idx=1&sn=c98328061adc5621d1cac3bee3bfeacb&chksm=ebd633addca1babb10cc15c8ef6d7bef982c9ddf940246303c9ae06274574bff98ed7b76e841&mpshare=1&scene=24&srcid=1018vnMtwsrdQd3ZkMrssxuA#rd