在SSM(Spring+SpringMVC+MyBatis)框架中实现一次操作多数据库的需求,通常涉及多个数据源的配置、动态切换以及事务管理,以下从配置、代码实现、事务处理等方面详细说明具体步骤和注意事项。
多数据源配置
首先需要在Spring配置文件中定义多个数据源,每个数据源对应一个数据库,配置两个数据源dataSource1和dataSource2,分别指向不同的数据库。
<!-- 数据源1 -->
<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db1"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 数据源2 -->
<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db2"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
配置SqlSessionFactory和Mapper扫描
每个数据源需要独立的SqlSessionFactory和MapperScannerConfigurer,通过指定不同的sqlSessionFactoryBeanName和basePackage来实现隔离。
<!-- SqlSessionFactory1 -->
<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource1"/>
<property name="mapperLocations" value="classpath:mapper/db1/*.xml"/>
</bean>
<!-- SqlSessionFactory2 -->
<bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource2"/>
<property name="mapperLocations" value="classpath:mapper/db2/*.xml"/>
</bean>
<!-- Mapper扫描1 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper.db1"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory1"/>
</bean>
<!-- Mapper扫描2 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper.db2"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory2"/>
</bean>
动态数据源切换
通过继承AbstractRoutingDataSource实现动态数据源切换,在determineCurrentLookupKey()方法中返回当前线程对应的数据源标识。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
定义线程安全的DataSourceContextHolder来存储当前数据源类型:

public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String type) {
contextHolder.set(type);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
在Spring配置中注册动态数据源:
<bean id="dynamicDataSource" class="com.example.config.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="db1" value-ref="dataSource1"/>
<entry key="db2" value-ref="dataSource2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource1"/>
</bean>
注解实现数据源切换
自定义注解@DataSource,结合AOP实现动态切换:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "db1";
}
AOP切面实现:
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(dataSource)")
public void setDataSource(DataSource dataSource) {
DataSourceContextHolder.setDataSourceType(dataSource.value());
}
@After("@annotation(dataSource)")
public void clearDataSource(DataSource dataSource) {
DataSourceContextHolder.clearDataSourceType();
}
}
在Service方法上使用注解:

@Service
public class UserService {
@DataSource("db1")
public void insertUser(User user) {
// 操作db1
}
@DataSource("db2")
public void insertLog(Log log) {
// 操作db2
}
}
多数据源事务管理
多数据源事务需要使用JtaTransactionManager或编程式事务,以下是基于JtaTransactionManager的配置:
-
添加JTA依赖(如Atomikos或Bitronix):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency>
-
配置JTA事务管理器:
<bean id="jtaTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"/>
-
在Service方法上添加
@Transactional注解,确保多个数据源操作在同一个事务中:
@Transactional public void transferData() { DataSourceContextHolder.setDataSourceType("db1"); userMapper.insert(user); // db1操作 DataSourceContextHolder.setDataSourceType("db2"); logMapper.insert(log); // db2操作 }
注意事项
- 线程安全:
DataSourceContextHolder使用ThreadLocal确保线程隔离。 - 事务一致性:跨数据库事务需保证所有数据库支持XA协议,或采用最终一致性方案。
- 性能影响:频繁切换数据源可能影响性能,建议按模块划分数据源。
- Mapper隔离:不同数据源的Mapper接口和XML文件需严格分开,避免冲突。
相关问答FAQs
Q1: 如何在同一个Service方法中同时操作多个数据库?
A: 可以通过手动切换数据源实现,在Service方法中,先调用DataSourceContextHolder.setDataSourceType("db1")执行第一个数据库操作,再切换为"db2"执行第二个数据库操作,若需事务一致性,需结合JTA事务管理器,确保所有操作在同一个事务中提交或回滚。
Q2: 多数据源配置下,MyBatis的Mapper扫描如何避免冲突?
A: 为每个数据源配置独立的SqlSessionFactory和MapperScannerConfigurer,并通过basePackage指定不同的Mapper接口包路径。db1的Mapper放在com.example.mapper.db1,db2的Mapper放在com.example.mapper.db2,确保XML文件和接口包路径一一对应,避免扫描混乱。
来源互联网整合,作者:小编,如若转载,请注明出处:https://www.aiboce.com/ask/245367.html