1. 基于 MyBatis 配置实现多数据源切换
借助 Spring 的 AbstractRoutingDataSource,能在运行时动态切换数据源。
// 数据源上下文持有者,用于存储当前线程使用的数据源标识
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String key) {
contextHolder.set(key);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
// 动态数据源实现
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
// 配置类
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
// 配置MySQL数据源
DataSource mysqlDataSource = DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver")
.url("jdbc:mysql://localhost:3306/mysql_db")
.username("root")
.password("password")
.build();
// 配置GreatDB数据源
DataSource greatDBDataSource = DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver") // GreatDB兼容MySQL驱动
.url("jdbc:mysql://localhost:3306/greatdb_db")
.username("root")
.password("password")
.build();
targetDataSources.put("mysql", mysqlDataSource);
targetDataSources.put("greatdb", greatDBDataSource);
dynamicDataSource.setDefaultTargetDataSource(mysqlDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
}
// 服务层使用示例
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getUsersFromMySQL() {
try {
DataSourceContextHolder.setDataSource("mysql");
return userMapper.selectAll();
} finally {
DataSourceContextHolder.clearDataSource();
}
}
public List<User> getUsersFromGreatDB() {
try {
DataSourceContextHolder.setDataSource("greatdb");
return userMapper.selectAll();
} finally {
DataSourceContextHolder.clearDataSource();
}
}
}
2. 在 Mapper XML 中编写数据库方言适配
当 SQL 语法在不同数据库之间存在差异时,可通过 databaseIdProvider 来适配。
<!-- mybatis-config.xml -->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="GreatDB" value="greatdb"/> <!-- GreatDB兼容MySQL,可能需要自定义识别 -->
</databaseIdProvider>
<!-- UserMapper.xml -->
<select id="selectAll" resultMap="BaseResultMap">
<!-- 通用SQL -->
SELECT * FROM user
<if test="_databaseId == 'mysql'">
-- MySQL特有的SQL片段
</if>
<if test="_databaseId == 'greatdb'">
-- GreatDB特有的SQL片段
</if>
</select>
3. 使用不同的 Mapper 接口对应不同的数据库
为不同的数据库创建独立的 Mapper 接口和 XML 文件。
// MySQL数据源的Mapper
@Mapper
@DataSource("mysql") // 自定义注解,用于AOP切换数据源
public interface MySQLUserMapper {
List<User> selectAll();
}
// GreatDB数据源的Mapper
@Mapper
@DataSource("greatdb")
public interface GreatDBUserMapper {
List<User> selectAll();
}
4. 借助 AOP 实现数据源自动切换
通过自定义注解和 AOP 来自动管理数据源切换。
// 自定义数据源注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "mysql";
}
// AOP切面
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(com.example.demo.DataSource)")
public void before(JoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
DataSource dataSource = signature.getMethod().getAnnotation(DataSource.class);
if (dataSource == null) {
dataSource = signature.getDeclaringType().getAnnotation(DataSource.class);
}
if (dataSource != null) {
DataSourceContextHolder.setDataSource(dataSource.value());
}
}
@After("@annotation(com.example.demo.DataSource)")
public void after(JoinPoint point) {
DataSourceContextHolder.clearDataSource();
}
}