问题背景
在已有Mysql项目中,其中一些库需要迁移到Pgsql中,领导说用PgSql性能高一点。(做复杂查询的Text类型数据)
在项目中同时使用 MySQL 和 PostgreSQL 两种数据库,要求:
- 默认Mapper为 MySQL,主要处理绝大部分业务逻辑。且为Mybatis-Plus
- 特定的Mapper使用 PostgreSQL,并配套独立的 MyBatis Mapper。
遇到的问题
- 起初设想,能否MySQL用Nacos的配置文件,单独给PgSQL配置Java数据源
结果会报错,
NoSuchBeanDefinitionException
,原因可能是我需要配置PgSQL的Mapper会和正常的Mapper放在一个文件夹,新配置的PgSQL数据源将其覆盖了
- 分别配置两个Java的SQL数据源,调用正常的Mapper的方法仍然会报错
No qualifying bean of type 'org.mybatis.spring.SqlSessionTemplate' available: expected single matching bean but found 2
- 本身该方法返回的就是列表值,现在反而限制返回
解决
问题一解决:
将需要配置的Mapper移动到对应文件夹,并增加MySQL和PgSQL的单独Java配置
MySQL数据源代码:
@Configuration
@MapperScan(
basePackages = "com.opencloud.qywechat.server.mapper", // 扫描整个包
sqlSessionFactoryRef = "mysqlSqlSessionFactory" // 默认绑定 MySQL 数据源
)
public class MysqlDataSourceConfig {
@Bean(name = "mysqlDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "mysqlSqlSessionFactory")
@Primary
public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/mysql/**/*.xml")
);
return sessionFactory.getObject();
}
@Bean(name = "mysqlSqlSessionTemplate")
@Primary
public SqlSessionTemplate mysqlSqlSessionTemplate(
@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
PgSQL数据源代码
@Configuration
@MapperScan(
basePackages = "com.opencloud.qywechat.server.mapper", // 扫描整个包
annotationClass = PgSqlMapper.class, // 只扫描标注了 @PgSqlMapper 的 Mapper
sqlSessionFactoryRef = "pgsqlSqlSessionFactory"
)
public class PgsqlDataSourceConfig {
@Bean(name = "pgsqlDataSource")
@ConfigurationProperties(prefix = "spring.datasource.pgsql")
public DataSource pgsqlDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "pgsqlSqlSessionFactory")
public SqlSessionFactory pgsqlSqlSessionFactory(@Qualifier("pgsqlDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/pgsql/**/*.xml")
);
return sessionFactory.getObject();
}
@Bean(name = "pgsqlSqlSessionTemplate")
public SqlSessionTemplate pgsqlSqlSessionTemplate(
@Qualifier("pgsqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
这里我自定义了注解@PgSqlMapper
,检测到有该注解的Mapper才使用PgSQL的数据源
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PgSqlMapper {
}
Tips: 不同数据源的Mapper不要放在同一文件夹下
因为业务问题,对应的PgSQL的Mapper不得不放到MySQL的Mapper的子文件夹下面,所以,我在MySQL数据源配置加上@Primary
配置,表明先加载这个数据源,后面用到对应PgSQL的Mapper再通过@PgSqlMapper
使用PgSQL数据源。
问题二解决:
查了资料才发现
MyBatis-Plus 作为 MyBatis 的增强工具,其底层依赖与 MyBatis 类似,但在部分配置上存在差异。例如,
SqlSessionFactory
需要用 MyBatis-Plus 提供的MybatisSqlSessionFactoryBean
来替代原生的SqlSessionFactoryBean
,否则可能会导致某些增强功能(如分页插件)失效。
所以将其改为对应的 MybatisSqlSessionFactoryBean
即可
/**
* 配置 MyBatis-Plus 的 SqlSessionFactory
*/
@Bean(name = "mysqlSqlSessionFactory")
@Primary
public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
// 指定 Mapper XML 文件的位置
sqlSessionFactoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*.xml")
);
return sqlSessionFactoryBean.getObject();
}
同样的,如果PgSQL也用到MP相关的,也要配置MybatisSqlSessionFactoryBean
加强。
除此之外,如果配置了MP的分页插件,也要在数据源的sqlSession工厂里添加上去
@Bean(name = "mysqlSqlSessionFactory")
@Primary
public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
// 配置分页插件
sqlSessionFactoryBean.setPlugins(new PaginationInterceptor());
sqlSessionFactoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/mysql/**/*.xml")
);
return sqlSessionFactoryBean.getObject();
}