Mybatis Spring多数据库Java配置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18201075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:14:43  来源:igfitidea点击:

Mybatis Spring multiple databases Java configuration

javaspringmybatis

提问by Nestor Hernandez Loli

I'm working with Spring and Mybatis and I have two databases, the configuration for the first database was relative easy, but I can't get to work the second database with Spring and transactions, here is my code

我正在使用 Spring 和 Mybatis 并且我有两个数据库,第一个数据库的配置相对容易,但是我无法使用 Spring 和事务处理第二个数据库,这是我的代码

@Configuration
@ComponentScan(basePackages = {"hernandez.service", "hernandez.dao"})
@EnableTransactionManagement
@MapperScan(basePackages="hernandez.mapper" ) 
@Import(DbConfig2.class)
public class AppConfig {

@Bean(name = "dataSource")
public DataSource dataSource() {
    DriverManagerDataSource ds = new DriverManagerDataSource("com.mysql.jdbc.Driver",
            "jdbc:mysql://localhost:3306/northwind", "root", "");
    return ds;
}

@Bean
public SqlSessionFactoryBean sqlSessionFactory() {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource()); 
    return factoryBean;
}

@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource());
}
}

@Configuration
@MapperScan("loli.mapper" ) 
public class DbConfig2 {
@Bean(name = "dataSource_2")
public DataSource dataSource2() {
    DriverManagerDataSource ds = new DriverManagerDataSource("com.mysql.jdbc.Driver",
            "jdbc:mysql://localhost:3306/dmsolut_dmsms", "root", "");
    return ds;
}

@Bean
public SqlSessionFactory sqlSessionFactory2() throws Exception{
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource2());
    return factoryBean.getObject();
}

@Bean(name = "transactionManager_2")
public PlatformTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource2());
}
}

Is there a way to get this working with pure Spring Java configuration or at least with some XML? There's no official documentation to get two databases working in the Mybatis-Spring project

有没有办法使用纯 Spring Java 配置或至少使用一些 XML 来实现它?在 Mybatis-Spring 项目中没有让两个数据库工作的官方文档

采纳答案by Larry.Z

Multi datasources with mybatis are used in my project right now. This is an Example, add to your application.xml

我的项目中现在使用了带有 mybatis 的多数据源。这是一个示例,添加到您的 application.xml

  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
    <property name="url" value="${center.connectionURL}"/>
    <property name="username"  value="${userName}"/>
    <property name="password" value="${password}"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.xxx.dao.center"/>
    <property name="sqlSessionFactoryBeanName" value="cneterSqlSessionFactory"/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" name="cneterSqlSessionFactory">
    <property name="dataSource" ref="dataSource"></property>
    <property name="mapperLocations" value="classpath*:mapperConfig/center/*.xml"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--center db end-->
<!--exdb-->
<bean id="dataSourceEx" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
    <property name="url" value="${ex.connectionURL}"/>
    <property name="username"  value="${userName}"/>
    <property name="password" value="${password}"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.xxx.dao.ex"/>
    <property name="sqlSessionFactoryBeanName" value="exSqlSessionFactory"/>
</bean>
<bean id="sqlSessionFactoryEx" class="org.mybatis.spring.SqlSessionFactoryBean" name="exSqlSessionFactory">
    <property name="dataSource" ref="dataSourceEx"></property>
    <property name="mapperLocations" value="classpath*:mapperConfig/ex/*.xml"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean id="transactionManagerEx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSourceEx"/>
</bean>

回答by moleksyuk

Add answer with java config example we use in our project:

使用我们在项目中使用的 java 配置示例添加答案:

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@ComponentScan(basePackages = "com.mycompany")
@EnableTransactionManagement(proxyTargetClass = true)
public class ApplicationConfig2 {
    public static final String DATA_SOURCE_NAME_1 = "jdbc/dataSource1";
    public static final String DATA_SOURCE_NAME_2 = "jdbc/dataSource2";

    public static final String SQL_SESSION_FACTORY_NAME_1 = "sqlSessionFactory1";
    public static final String SQL_SESSION_FACTORY_NAME_2 = "sqlSessionFactory2";

    public static final String MAPPERS_PACKAGE_NAME_1 = "com.mycompany.mappers.dao1";
    public static final String MAPPERS_PACKAGE_NAME_2 = "com.mycompany.mappers.dao2";

    @Bean
    public DataSource dataSource1() {
        JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        return dsLookup.getDataSource(DATA_SOURCE_NAME_1);
    }

    @Bean
    public DataSource dataSource2() {
        JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        return dsLookup.getDataSource(DATA_SOURCE_NAME_2);
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }


    @Bean(name = SQL_SESSION_FACTORY_NAME_1)
    public SqlSessionFactory sqlSessionFactory1(DataSource dataSource1) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setTypeHandlersPackage(DateTimeTypeHandler.class.getPackage().getName());
        sqlSessionFactoryBean.setDataSource(dataSource1);
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
        sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
        sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
        return sqlSessionFactory;
    }

    @Bean(name = SQL_SESSION_FACTORY_NAME_2)
    public SqlSessionFactory sqlSessionFactory2(DataSource dataSource2) throws Exception {
        SqlSessionFactoryBean diSqlSessionFactoryBean = new SqlSessionFactoryBean();
        diSqlSessionFactoryBean.setTypeHandlersPackage(DateTimeTypeHandler.class.getPackage().getName());
        diSqlSessionFactoryBean.setDataSource(dataSource2);
        SqlSessionFactory sqlSessionFactory = diSqlSessionFactoryBean.getObject();
        sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
        sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
        return sqlSessionFactory;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer1() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage(MAPPERS_PACKAGE_NAME_1);
        configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_1);
        return configurer;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer2() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage(MAPPERS_PACKAGE_NAME_2);
        configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_2);
        return configurer;
    }
}

回答by kaibo

In my experience, you should also add @Primaryto one of the DataSourcebeans. Otherwise it will throw NoUniqueBeanDefinitionException.

根据我的经验,您还应该添加@Primary到其中一个DataSource豆子中。否则会抛出NoUniqueBeanDefinitionException

@Bean
@Primary
public DataSource dataSource1() {
    JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
    return dsLookup.getDataSource(DATA_SOURCE_NAME_1);
}

@Bean
public DataSource dataSource2() {
    JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
    return dsLookup.getDataSource(DATA_SOURCE_NAME_2);
}

回答by fabfas

You can use spring's AbstractRoutingDataSourceby extending it and overriding the method determineCurrentLookupKey().

您可以通过扩展和覆盖方法来使用 spring 的AbstractRoutingDataSourcedetermineCurrentLookupKey()

Spring Configuration

弹簧配置

You can define separate datasourcein spring configuration.

您可以datasource在弹簧配置中单独定义。

<!-- db2 data source -->
<bean id="db2DataSource" class="com.ibm.db2.jdbc.app.DB2Driver">
  <property name="serverName" value="${db2.jdbc.serverName}" />
  <property name="portNumber" value="${db2.jdbc.portNumber}" />
  <property name="user" value="${db2.jdbc.username}" />
  <property name="password" value="${db2.jdbc.password}" />
  <property name="databaseName" value="${db2.jdbc.databaseName}" />
</bean>

<!-- mysql data source -->
<bean id="mysqlDataSource" class="com.mysql.jdbc.Driver">
  <property name="serverName" value="${mysql.jdbc.serverName}" />
  <property name="portNumber" value="${mysql.jdbc.portNumber}" />
  <property name="user" value="${mysql.jdbc.username}" />
  <property name="password" value="${mysql.jdbc.password}" />
  <property name="databaseName" value="${mysql.jdbc.databaseName}" />
</bean>

Associate the datasource with customer:

将数据源与客户关联:

<bean id="customer" class="com.example.Customer">
  <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="dataSource" class="com.example.datasource.CustomerRoutingDataSource">
  <property name="targetDataSources">
  <map key-type="com.example.Customer">
     <entry key="db2" value-ref="mysqlDataSource"/>
     <entry key="mysql" value-ref="db2DataSource"/>
  </map>
  </property>
  <property name="defaultTargetDataSource" ref="mysql"/>
</bean>

Java

爪哇

package com.example;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class CustomerRoutingDataSource extends AbstractRoutingDataSource {

 @Bean
 CustomerContextHolder context;

 @Override
 protected Object determineCurrentLookupKey() {
  return context.getCustomerType();
 }
}

Basically, each request will have its context. You can associate datasourcewith request using mapped key. You can find more details here dynamic-datasource-routing

基本上,每个请求都有其上下文。您可以datasource使用映射键与请求关联。您可以在此处找到更多详细信息dynamic-datasource-routing

回答by P Frank

<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource1" />
    <property name="configLocation">
        <value>classpath:com/dtcc/dao/impl/DaoSqlMapConfig_MyBatis1.xml</value>
  </property>
   <property name="transactionFactory">
        <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
    </property>  
    <property name="mapperLocations" value="classpath*:com/dtcc/dao/impl/DaoEmfMyBatis.sp.xml"/> 
</bean>
<bean id="sqlSession1" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory1" />
</bean> 
<!-- MyBatis Changes Ends -->

<bean id="daoEmf" class="com.dtcc.dao.DaoEmfImpl">
    <property name="connectionType"><ref local="com.dtcc.sharedservices.utils.resources.ConnTypes.IBM_DB2_CONNECTION" /></property>
    <property name="jndiNameForLogging"><ref local="dataSourceName1" /></property>
    <property name="sqlSessionTemplate"> <ref local="sqlSession1" /></property>
    <property name="applicationLog"><ref local="appLog" /></property>
</bean>

As mentioned above, we need to give corresponding sessionFactory in your DaoImpl. You can not autowire SqlSessionTemplate in your DaoImpl class if you have more than one sessionFactory. Give unique name for each session factory and map it to your respective DaoImpl class. All you have to do is just to create object for SqlSessionTemplate with Setter method in DaoImpl class and you can make your db call using sqlSessionTemplate object as below, this.sqlSessionTemplate.selectList("ProcedureID", parameter);

如上所述,我们需要在你的 DaoImpl 中给出相应的 sessionFactory。如果您有多个 sessionFactory,则不能在 DaoImpl 类中自动装配 SqlSessionTemplate。为每个会话工厂指定唯一的名称并将其映射到您各自的 DaoImpl 类。您所要做的就是在 DaoImpl 类中使用 Setter 方法为 SqlSessionTemplate 创建对象,您可以使用 sqlSessionTemplate 对象进行数据库调用,如下所示, this.sqlSessionTemplate.selectList("ProcedureID", parameter);