这真的让我感到困惑。我有一个Spring Boot(2.1.2)应用程序,通过MyBatis和Spring管理两个数据源。我有多个MyBatis映射器,每个映射器都配置为使用特定的数据源。该配置的代码如下:
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class MyBatisConfig {
private static final String POSTGRES_SESSION_FACTORY = "postgresSessionFactory";
private static final String MYSQL_SESSION_FACTORY = "mySqlDbSessionFactory";
@Bean(name = POSTGRES_SESSION_FACTORY, destroyMethod = "")
@Primary
public SqlSessionFactoryBean postgresSessionFactory(
@Qualifier(DataSourceConfig.PRIMARY_DATA_SOURCE) final DataSource oneDataSource,
ApplicationContext applicationContext) throws Exception {
final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis-config.xml"));
sqlSessionFactoryBean.setDataSource(oneDataSource);
SqlSessionFactory sqlSessionFactory;
sqlSessionFactory = sqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().addMapper(Mapper1.class);
sqlSessionFactory.getConfiguration().addMapper(Mapper2.class);
return sqlSessionFactoryBean;
}
@Bean(name = MYSQL_SESSION_FACTORY, destroyMethod = "")
public SqlSessionFactoryBean mySqlSessionFactory(
@Qualifier(DataSourceConfig.SECONDARY_DATA_SOURCE) final DataSource anotherDataSource,
ApplicationContext applicationContext) throws Exception {
final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis-config-sql.xml"));
sqlSessionFactoryBean.setDataSource(anotherDataSource);
final SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().addMapper(Mapper3.class);
return sqlSessionFactoryBean;
}
@Bean
public MapperFactoryBean<Mapper1> accountMapperFactory(@Qualifier(POSTGRES_SESSION_FACTORY) final SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {
MapperFactoryBean<Mapper1> factoryBean = new MapperFactoryBean<>(Mapper1.class);
factoryBean.setSqlSessionFactory(sqlSessionFactoryBean.getObject());
return factoryBean;
}
@Bean
public MapperFactoryBean<Mapper2> domainMapperFactory(@Qualifier(POSTGRES_SESSION_FACTORY) final SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {
MapperFactoryBean<Mapper2> factoryBean = new MapperFactoryBean<>(Mapper2.class);
factoryBean.setSqlSessionFactory(sqlSessionFactoryBean.getObject());
return factoryBean;
}
@Bean
public MapperFactoryBean<Mapper3> usageMapperFactory(@Qualifier(MYSQL_SESSION_FACTORY) final SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {
MapperFactoryBean<Mapper3> factoryBean = new MapperFactoryBean<>(Mapper3.class);
factoryBean.setSqlSessionFactory(sqlSessionFactoryBean.getObject());
return factoryBean;
}
}
如果使用调试器,则可以在初始化这些bean时将它们全部指向正确的数据源。 (Mapper1和Mapper2的SqlSessionFactorys连接到postgres数据源,Mapper3的SqlSessionFactory连接到mysql数据源。
但是奇怪的是,当它们被注入到服务中时,所有三个Mappers都连接到了postgres数据源。在这一点上,我感到很困惑。
服务和注入非常简单:
@Autowired private Mapper1 mapper1;
@Autowired private Mapper2 mapper2;
@Autowired private Mapper3 mapper3;
但是,当我调用该服务并用调试器停止它时,我可以看到mapper3连接到错误的数据源(postgres)。
有什么想法吗?需要更多信息吗?
答案 0 :(得分:0)
当使用MyBatis配置多个数据源时,我具有相同的效果。在运行时,所有映射器都使用了最后定义的数据源连接凭据,尽管在启动过程中一切看起来都很好。
我可以将问题缩小到factoryBean定义并设置配置。看起来,配置对象在所有数据源之间共享(正如我在此处设置的那样),因为我从MyBatisProperties中设置了相同的实例。在运行时,配置也是MyBatis从Environment属性获取数据源的地方。
“魔术”发生在factoryBean.getObject()
和factoryBean.buildSqlSessionFactory()
上。在这里,数据源被设置为配置。如果未显式设置配置,则会创建一个新的默认配置。否则,配置对象将被重用。因此,当使用相同的配置对象初始化多个工厂bean时,配置对象将注入每个数据源,而最后一个数据源将保留。
这可能是一个错误,因为作为Mybatis用户,我希望Mybatis使用factoryBean.setDataSource(aDatasource)
提供的数据源。因此,当我注释掉factoryBean.setConfiguration
时,一切都会按预期进行,但是当然我的配置未应用。
@Bean("sqlSessionFactoryA")
public SqlSessionFactory sqlSessionFactory(@Qualifier("datasourceA") DataSource aDataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(aDataSource);
factoryBean.setConfigurationProperties(properties.getConfigurationProperties());
// factoryBean.setConfiguration(properties.getConfiguration());
return factoryBean.getObject();
}
作为解决方案,我将MyBatisProperties
设置为 prototype 范围,以便在每次注入时创建一个具有新配置对象的新属性对象。这是必要的,因为我不想编写一个复制方法,并且org.apache.ibatis.session.Configuration
不提供一个复制构造函数。
@Bean
@Primary
@Scope("prototype")
public MybatisProperties myBatisProperties() {
return new MybatisProperties();
}
现在我也可以应用配置,并且一切正常。
org.apache.ibatis.session.Configuration tmpConfiguration = properties.getConfiguration();
factoryBean.setConfiguration(tmpConfiguration);