java使用AOP切面,自定义切换数据库源,mapper控制执行不同的sql语句

发布于:2025-06-27 ⋅ 阅读:(20) ⋅ 点赞:(0)

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();
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到