由于项目原因,做了一下配置多数据源的调查,结果如下:
项目是标准的spring + mybatis
注:配置方面相对来说简化过,不是完整版,我只展示出重要部分
数据库连接配置文件:
#数据库(1)
jdbc_url=jdbc:mysql://localhsot:3306/first?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_username=root
jdbc_password=root
#数据库(2)
jdbc_url_second=jdbc:mysql://localhost:3306/second?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_username_second=root
jdbc_password_second=root
数据源(1)配置:
<!-- 数据源(1) -->
<bean name="firstDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_username}" />
<property name="password" value="${jdbc_password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="1" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="200" />
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="100" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="1" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
</bean>
数据源(2)配置:
<!-- 数据源(2) -->
<bean name="secondDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url_second}" />
<property name="username" value="${jdbc_username_second}" />
<property name="password" value="${jdbc_password_second}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="1" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="200" />
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="100" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="1" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
</bean>
数据源切换配置:
<bean id="dynamicDataSource" class="com.test.common.dataSource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- 指定lookupKey和与之对应的数据源 -->
<entry key="firstDataSource" value-ref="firstDataSource"></entry>
<entry key="secondDataSource" value-ref="secondDataSource"></entry>
</map>
</property>
<!-- 这里可以指定默认的数据源 -->
<property name="defaultTargetDataSource" ref="firstDataSource" />
</bean>
DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource{
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
public static String getDataSource() {
return dataSources.get();
}
public static void setDataSource(String dataSource) {
dataSources.set(dataSource);
}
public static void clearDataSource() {
dataSources.remove();
}
@Override
protected Object determineCurrentLookupKey() {
return dataSources.get();
}
}
mybatis 相关配置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="mapperLocations" value="classpath:com/test/mapping/*.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.test.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
写的过程中想到,如果在 controller 中调用某方法需要切换库的时候,还需要在这个方法前 写一句
(DynamicDataSource.setDataSource("配置文件中配置的数据源的key"))
(业务需要)切换后还需要将刚才设置的源删除(要不然之后就会是刚才切换的数据源),这样的话 就还需要在 刚才的方法后写一句 (DynamicDataSource.clearDataSource())
这样太麻烦了,所以就想了一种处理方式,用注解,嘿嘿嘿,果然适当的懒才能让人进步
事务相关配置:
注:
- 事务相关的部分,本项目配置的是 用 *Impl 结束的实现类,这些 实现类的方法中 有满足下面配置的 才会开启事务
- 一旦进入事务后就无法修改数据源,所以只能在开启事务前去修改数据源
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource" />
</bean>
<!-- 拦截器方式配置事物 -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="append*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="modify*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>
<bean id="dataSourceExchange" class="com.test.common.dataSource.DataSourceExchange"/>
<aop:config>
<aop:pointcut id="transactionPointcut" expression="execution(* com.test.*.service..*Impl.*(..))" />
<!-- 开启事务前先判断是否需要切换数据源 -->
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="dataSourceExchange" order="1"/>
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" order="2"/>
</aop:config>
DataSource.java
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value();
}
DataSourceExchange.java
@Component
public class DataSourceExchange implements MethodBeforeAdvice, AfterReturningAdvice {
Logger log = LoggerFactory.getLogger("【数据源切换器】");
/**
* 拦截目标方法,获取由 @DataSource 指定的数据源标识,设置到线程存储中以便切换数据源
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
if(method.isAnnotationPresent(DataSource.class)){
DataSource dataSource = method.getAnnotation(DataSource.class);
DynamicDataSource.setDataSource(dataSource.value());
log.info("数据源切换至:" + DynamicDataSource.getDataSource());
}
}
/**
* 方法结束后(移除数据源)
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
DynamicDataSource.clearDataSource();
log.info("数据源已移除!");
}
}
简单示例:
@Controller
@RequestMapping("/test")
public class TestController {
@Autowired
TestService testService;
/**
* 纯测试
*/
@RequestMapping(value = "/justTest ", produces = "application/json;charset=utf-8", method = RequestMethod.POST)
@ResponseBody
public String justTest (){
log.info("此时连接的是默认的数据源(1)");
/**
* 如果没有配置 aop 相关部分,切换数据源写法:
* DynamicDataSource.setDataSource("secondDataSource"); -- 切换第二数据源
* testService.justTest();
* DynamicDataSource.clearDataSource(); -- 移除已设置数据源,恢复到默认数据源
*/
testService.justTest();
return null;
}
}
public interface TestService {
@DataSource("secondDataSource")
void justTest();
}
@Service
public class TestServiceImpl implements TestService {
@Override
public void justTest() {
//往下我就不写了,按照上面的配置
//进到这里后,已经切换到了第二数据源
}
}
大概就是这样,我尽量写明白,写的不好别见怪