-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi Datasource #78
Comments
+1 |
2 similar comments
+1 |
+1 |
It looks like you can not atm. It uses a single datasource. Refer to http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/ - quick setup section and the source code. Looks like an enhancement request if you need it to support multiple datasource. Or submit a patch? |
+1 |
Here is my double datasource config: maven dependency:
Datasource km config class:
Here is datasource esb config class:
Then you can use the two annotation:
And, Don't forget exclude datasource auto configuration on start class:
EDIT: remove the EsbEnabledCondition
|
Thanks @jacks808 , here is my double Oracle datasources example in Spring Boot (hopefully it is simpler). pom.xml:
resources/application.properties:
CrdDatasoureConfig.java:
StagingDatasourceConfig.java:
The mapper for CrdDatasource is under package com.cg.crd.etl.mappers.crd
The mapper for StagingDatasource is under package com.cg.crd.etl.mappers.staging
Now, here is the test code
|
I want to get datasource id through custom annotationClass's in @MapperScan, but I got null. |
@JesseBlackman If you want to use different annotation to mark 1.Impl 2 different Annotation for mark your mapper class,
NOTE: notice the attribute of annotationClass this should same as your mark annotation. And, finally mark mapper class by UseDatasourceA You can find more detail in my comment in this post. |
@jacks808 Nice! It's works for me . Thanks a lot. |
@jacks808 Can you show your EsbEnabledCondition.class ? |
@phengtola |
multi datasource only config once ?
this not work for second datasource? |
The MyBatis Spring Boot AutoConfigure support single datasource(or |
so . how can add second config? |
You can inject a |
If you defined a custom |
@kazuki43zoo yes, I add config properties, but get cycle dependencies @Configuration
@MapperScan(annotationClass = Mapper.class, basePackages = {"service.allocate.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceConfig {
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
public DataSourceConfig(MybatisProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
}
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
return SqlSessionFactoryBuilder.build(dataSource, properties, resourceLoader, interceptors, databaseIdProvider);
}
}
@Configuration
@MapperScan(annotationClass = ETLMapper.class, basePackages = {"service.allocate.mapper"}, sqlSessionFactoryRef = "etlSqlSessionFactory")
public class ETLDataSourceConfig {
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
public ETLDataSourceConfig(MybatisProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
}
@Bean(name = "etlDataSource")
@ConfigurationProperties(prefix = "spring.etl.datasource")
public DataSource etlDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "etlSqlSessionFactory")
public SqlSessionFactory etlSqlSessionFactory(DataSource etlDataSource) throws Exception {
return SqlSessionFactoryBuilder.build(etlDataSource, properties, resourceLoader, interceptors, databaseIdProvider);
}
}
public static SqlSessionFactory build(DataSource dataSource, MybatisProperties properties, ResourceLoader resourceLoader, Interceptor[] interceptors, DatabaseIdProvider databaseIdProvider) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(properties.getConfigLocation())) {
sqlSessionFactoryBean.setConfigLocation(resourceLoader.getResource(properties.getConfigLocation()));
}
sqlSessionFactoryBean.setConfiguration(properties.getConfiguration());
if (properties.getConfigurationProperties() != null) {
sqlSessionFactoryBean.setConfigurationProperties(properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(interceptors)) {
sqlSessionFactoryBean.setPlugins(interceptors);
}
if (databaseIdProvider != null) {
sqlSessionFactoryBean.setDatabaseIdProvider(databaseIdProvider);
}
if (StringUtils.hasLength(properties.getTypeAliasesPackage())) {
sqlSessionFactoryBean.setTypeAliasesPackage(properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(properties.getTypeHandlersPackage())) {
sqlSessionFactoryBean.setTypeHandlersPackage(properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(properties.resolveMapperLocations())) {
sqlSessionFactoryBean.setMapperLocations(properties.resolveMapperLocations());
}
return sqlSessionFactoryBean.getObject();
} ***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| dataSourceConfig
↑ ↓
| thriftServerConfig this inject TAllocateServiceImpl
↑ ↓
| TAllocateServiceImpl this inject allocateMapper
↑ ↓
| allocateMapper annotation @Mapper
└─────┘ |
@kazuki43zoo help |
I've added a multiple datasource demo application on https://github.com/kazuki43zoo/mybatis-spring-boot-multi-ds-demo. Please check this. |
@Dreampie I can't help you with just a provided information ... If you need my help, please provide demo(reproduce) application on GitHub. |
@jacks808 Thank you. It works now. |
@kazuki43zoo @jacks808 thanks, https://github.com/Dreampie/mybatis-spring-boot-multi-ds-demo, I resolve cycle dependency, but mapperscan with annotation |
@Dreampie
The public SqlSessionFactory build(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(mybatisProperties.getConfigLocation())) {
sqlSessionFactoryBean.setConfigLocation(resourceLoader.getResource(mybatisProperties.getConfigLocation()));
}
+ if(mybatisProperties.getConfiguration() != null) {
+ Configuration configuration = new Configuration();
+ BeanUtils.copyProperties(mybatisProperties.getConfiguration(), configuration);
+ sqlSessionFactoryBean.setConfiguration(configuration);
+ }
- sqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration());
// ...
return sqlSessionFactoryBean.getObject();
}
You should specify a @Bean(name = "etlSqlSessionFactory")
+ public SqlSessionFactory etlSqlSessionFactory(@Qualifier("etlDataSource") DataSource etlDataSource)
- public SqlSessionFactory etlSqlSessionFactory(DataSource etlDataSource) throws Exception {
return mybatisConfigurationSupport.build(etlDataSource);
} or @Bean(name = "etlSqlSessionFactory")
+ public SqlSessionFactory etlSqlSessionFactory() throws Exception {
+ return mybatisConfigurationSupport.build(etlDataSource());
- public SqlSessionFactory etlSqlSessionFactory(DataSource etlDataSource) throws Exception {
- return mybatisConfigurationSupport.build(etlDataSource);
} |
Thanks @kazuki43zoo to provide an example |
@kazuki43zoo thanks for your help |
@jacks808 thank you for your answer |
@phengtola EsbEnabledCondition.class is empty annotation class, here is the code:public @interface UseDatasourceESB { }are you sure? |
@googlefan EsbEnabledCondition is a condition impl class use for check wether the esb datasource enable. It's non business with the dual datasources. |
@jacks808 yeah ! Thank you . It works. |
@googlefan That's my pleasure |
Thanks @jacks808 - The solution worked perfectly. |
@jacks808 |
Just for lazy guys: package bj;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import com.zaxxer.hikari.HikariDataSource;
import io.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
/**
* Created by [email protected] at 2018/12/6 下午9:29
* <p>
* MyBatis多数据源演示
*/
@SpringBootApplication(exclude = {SpringBootConfiguration.class})
@Configuration
@MapperScan(annotationClass = Mapper.class, basePackageClasses = MyBatisApp.class,
sqlSessionFactoryRef = "sqlSessionFactory")
public class MyBatisApp implements ApplicationListener<ApplicationReadyEvent> {
/**
* SecondaryMapper配置
* \@MapperScan 注解一次只能添加一个,所以需要单独再加一个配置类
* 自定义@MapperScan会替换MyBatis自动添加的默认@MapperScan。所以主@MapperScan也必须显式添加
*/
@Configuration
@MapperScan(annotationClass = SecondaryMapper.class, basePackageClasses = MyBatisApp.class,
sqlSessionFactoryRef = "sqlSessionFactorySecond")
static class SecondaryMapperConfiguration {
}
public static void main(String[] args) {
new SpringApplication(MyBatisApp.class) {{
setWebApplicationType(WebApplicationType.NONE);
}}.run(args);
}
@Resource
private DataSource dataSource;
@Resource
private DataSource dataSourceSecond;
@Resource
private JdbcTemplate jdbcTemplate;
@Resource
private UserMapper userMapper;
@Resource
private SecondaryUserMapper secondaryUserMapper;
private void initLogger() {
((Logger) LoggerFactory.getLogger(MyBatisApp.class)).setLevel(Level.DEBUG);
((Logger) LoggerFactory.getLogger(JdbcTemplate.class)).setLevel(Level.DEBUG);
}
private void initDatabase() {
String oldDatabase = jdbcTemplate.queryForObject("SELECT DATABASE()", String.class);
jdbcTemplate.execute("DROP SCHEMA IF EXISTS one");
jdbcTemplate.execute("CREATE SCHEMA one");
jdbcTemplate.execute("USE one");
jdbcTemplate.execute("CREATE TABLE user(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(32) CHARSET 'utf8')");
jdbcTemplate.execute("INSERT INTO user(name) VALUES ('人民的儿子')");
jdbcTemplate.execute("INSERT INTO user(name) VALUES ('人民的孙子')");
jdbcTemplate.execute("INSERT INTO user(name) VALUES ('人民的曾孙子')");
jdbcTemplate.execute("DROP SCHEMA IF EXISTS two");
jdbcTemplate.execute("CREATE SCHEMA two");
jdbcTemplate.execute("USE two");
jdbcTemplate.execute("CREATE TABLE user(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(32) CHARSET 'utf8')");
jdbcTemplate.execute("INSERT INTO user(name) VALUES ('人民的爹')");
jdbcTemplate.execute("INSERT INTO user(name) VALUES ('人民的爷')");
jdbcTemplate.execute("INSERT INTO user(name) VALUES ('人民的太爷')");
jdbcTemplate.execute("INSERT INTO user(name) VALUES ('人民的老太爷')");
jdbcTemplate.execute("USE " + oldDatabase);
}
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
initLogger();
initDatabase();
System.out.println("Users:");
userMapper.selectAll().forEach(System.out::println);
System.out.println("Secondary users:");
secondaryUserMapper.selectAll().forEach(System.out::println);
}
/**
* 主数据源
* <p>
* 如果不添加@Primary注解, MyBatis可以工作,但是JdbcTemplate无法注入
*
* @return .
*/
@Primary
@Bean
public DataSource dataSource() {
return new HikariDataSource() {{
setJdbcUrl("jdbc:mysql://localhost/one?useUnicode=true&characterEncoding=utf8");
setUsername("root");
setPassword("root");
}};
}
/**
* 副数据源
*
* @return .
*/
@Bean
public DataSource dataSourceSecond() {
return new HikariDataSource() {{
setJdbcUrl("jdbc:mysql://localhost/two?useUnicode=true&characterEncoding=utf8");
setUsername("root");
setPassword("root");
}};
}
/**
* 主SqlSessionFactory。使用主数据源。自定义SqlSessionFactory后,MyBatis就不自动添加SqlSessionFactory了,所以必须有
*
* @return .
* @throws Exception .
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
return new SqlSessionFactoryBean() {{
setDataSource(dataSource);
}}.getObject();
}
/**
* 副SqlSessionFactory。使用副数据源
*
* @return .
* @throws Exception .
*/
@Bean
public SqlSessionFactory sqlSessionFactorySecond() throws Exception {
return new SqlSessionFactoryBean() {{
setDataSource(dataSourceSecond);
}}.getObject();
}
@Mapper
interface UserMapper {
@Select("SELECT * FROM user")
List<Map<String, Object>> selectAll();
}
@SecondaryMapper
interface SecondaryUserMapper {
@Select("SELECT * FROM user")
List<Map<String, Object>> selectAll();
}
/**
* 自定义Mapper注解,用于标识使用的数据源
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface SecondaryMapper {
}
} |
And the console output:
|
How to config two datasource.
The text was updated successfully, but these errors were encountered: