Spring Boot 配置 Mybatis 读写分离
JPA 的读写分离配置不能应用在 Mybatis 上, 所以 Mybatis 要单独处理
为了不影响原有代码, 使用了增加拦截器的方式, 在拦截器里根据 SQL 的 CRUD 来路由到不同的数据源
需要单独增加Mybatis的配置
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 注册插件
sessionFactory.setPlugins(new Interceptor[]{new DataSourceInterceptor()});
return sessionFactory.getObject();
}
}
部署后发现没有生效, 打断点发现 SqlSessionFactoryBean 没有注册成功, 因为是老项目, 引入的包里已经有一个 Mybatis 的配置了, 我不能直接覆盖, 所以用 BeanPostProcessor 来在原有 SqlSessionFactoryBean 初始化时加入拦截器的配置
@Configuration
public class MyBatisConfig {
@Bean
@ConditionalOnBean(name="routingDataSource")
public BeanPostProcessor sqlSessionFactoryBeanPostProcessor(@Qualifier("routingDataSource") DataSource routingDataSource) {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SqlSessionFactory) {
SqlSessionFactory SqlSessionFactory = (SqlSessionFactory) bean;
try {
SqlSessionFactory.getConfiguration().addInterceptor(new DataSourceInterceptor());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return bean;
}
};
}
}
部署后发现还是未生效, 调试发现是引入的包里已经定义了一个默认的数据源,而且标注了@Primary, 而原有 Mybatis 的配置里直接使用了这个数据源
于是使用了一个hack的方法, 使用反射在 Mybatis 配置 SqlSessionFactoryBean 初始化时, 把数据源重新设置成有主从配置的数据源
@Configuration
public class MyBatisConfig {
@Bean
@ConditionalOnBean(name="routingDataSource")
public BeanPostProcessor sqlSessionFactoryBeanPostProcessor(@Qualifier("routingDataSource") DataSource routingDataSource) {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SqlSessionFactory) {
try {
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) bean;
org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
// 使用反射或其他方式修改配置中的数据源
Field dataSourceField = configuration.getEnvironment().getClass().getDeclaredField("dataSource");
dataSourceField.setAccessible(true);
dataSourceField.set(configuration.getEnvironment(), routingDataSource);
} catch (Exception e) {
throw new BeansException("Failed to modify SqlSessionFactory", e) {};
}
}
return bean;
}
};
}
}
再次部署测试通过