Google Code Prettify

2019年1月10日 星期四

Spring Batch: multiple datasource

在 spring boot 環境中,使用 spring batch,如果需要連線兩個不同資料庫,要怎麼做呢?
  • 在 application.properties 定義多個 dataSource
batch.datasource.driver-class-name=oracle.jdbc.OracleDriver
batch.datasource.url=jdbc:oracle:thin:@192.168.50.12:1521:testDB1
batch.datasource.username=dbuser1
batch.datasource.password=p@ssword

db1.datasource.driver-class-name=oracle.jdbc.OracleDriver
db1.datasource.url=jdbc:oracle:thin:@192.168.50.12:1521:testDB1
db1.datasource.username=dbuser1
db1.datasource.password=p@ssword

db2.datasource.driver-class-name=oracle.jdbc.OracleDriver
db2.datasource.url=jdbc:oracle:thin:@192.168.51.168:1521:testDB2
db2.datasource.username=dbuser2
db2.datasource.password=p@ssword
如上,db1、db2 分別連到不同的兩個資料庫,但是,除此之外,還要設定一個給 spring batch 使用,所以有 batch (第一個) 的 dataSource 設定,雖然 spring batch 與 db1 使用同一個資料庫,還是要另外設定 dataSource,否則在執行到 spring batch 的 tasklet 時,如果裡面有存取到 db1、db2 的資料庫時,會抓不到 transaction。
  • 設定多個 dataSource
@Configuration
public class MultipleDataSourceConfig {
    @Autowired
    private Environment env;
 
    @Bean(name = "batchDatasource")
    @ConfigurationProperties(prefix = "batch.datasource")
    public DataSource batchDataSource() {
        return DataSourceBuilder.create()
           .driverClassName(env.getProperty("batch.datasource.driver-class-name"))
           .url(env.getProperty("batch.datasource.url"))
           .username(env.getProperty("batch.datasource.username"))
           .password(env.getProperty("batch.datasource.password"))
           .build();
    }

    @Bean(name = "db1Datasource")
    @Primary
    @ConfigurationProperties(prefix = "db1.datasource")
    public DataSource db1DataSource() {
        return DataSourceBuilder.create()
           .driverClassName(env.getProperty("db1.datasource.driver-class-name"))
           .url(env.getProperty("db1.datasource.url"))
           .username(env.getProperty("db1.datasource.username"))
           .password(env.getProperty("db1.datasource.password"))
           .build();
    }
 
    @Bean(name = "db2Datasource")
    @ConfigurationProperties(prefix = "db2.datasource")
    public DataSource db2DataSource() {
        return DataSourceBuilder.create()
           .driverClassName(env.getProperty("db2.datasource.driver-class-name"))
           .url(env.getProperty("db2.datasource.url"))
           .username(env.getProperty("db2.datasource.username"))
           .password(env.getProperty("db2.datasource.password"))
           .build();
    }
}
  • 為每個 dataSource 設定相關的 transactionManager
    • BatchDbConfig.java
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
   entityManagerFactoryRef = "batchEntityManagerFactory",
   basePackages = { "idv.steven.batch.dao" },
   transactionManagerRef = "batchTransactionManager"
)
public class BatchDbConfig {
 @Autowired
 @Qualifier("batchDatasource")
 private DataSource datasource;
 
    private JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();

        adapter.setDatabase(Database.ORACLE);
        adapter.setShowSql(true);
        adapter.setGenerateDdl(false);
        adapter.setDatabasePlatform("org.hibernate.dialect.Oracle12cDialect");

        return adapter;
    }
    
    /**
     * 載入 Entity
     * @return
     */
    @Bean
    public LocalContainerEntityManagerFactoryBean batchEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(datasource);
        emf.setPackagesToScan(new String[] { "idv.steven.batch.entity" });
        emf.setJpaVendorAdapter(this.jpaVendorAdapter());
        emf.setSharedCacheMode(SharedCacheMode.NONE);

        return emf;
    }
    
    @Bean(name = "batchTransactionManager")
    public PlatformTransactionManager batchTransactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(this.batchEntityManagerFactory().getObject());
        
        return tm;
    }
}
    • DB1DbConfig.java
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
   entityManagerFactoryRef = "db1EntityManagerFactory",
   basePackages = { "idv.steven.database.db1.dao" },
   transactionManagerRef = "db1TransactionManager"
)
public class DB1DbConfig {
 @Autowired
 @Qualifier("db1Datasource")
 private DataSource datasource;
 
    private JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();

        adapter.setDatabase(Database.ORACLE);
        adapter.setShowSql(true);
        adapter.setGenerateDdl(false);
        adapter.setDatabasePlatform("org.hibernate.dialect.Oracle12cDialect");

        return adapter;
    }
    
    /**
     * 載入 Entity
     * @return
     */
    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean db1EntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(datasource);
        emf.setPackagesToScan(new String[] { "idv.steven.database.db1.entity" });
        emf.setJpaVendorAdapter(this.jpaVendorAdapter());
        emf.setSharedCacheMode(SharedCacheMode.NONE);

        return emf;
    }
    
    @Primary
    @Bean(name = "db1TransactionManager")
    public PlatformTransactionManager db1TransactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(this.db1EntityManagerFactory().getObject());
        
        return tm;
    }
}
    • DB2DbConfig.java
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
   entityManagerFactoryRef = "db2EntityManagerFactory",
   basePackages = { "idv.steven.database.db2.dao" },
   transactionManagerRef = "db2TransactionManager"
)
public class DB2DbConfig {
 @Autowired
 @Qualifier("db2Datasource")
 private DataSource datasource;
 
    private JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();

        adapter.setDatabase(Database.ORACLE);
        adapter.setShowSql(true);
        adapter.setGenerateDdl(false);
        adapter.setDatabasePlatform("org.hibernate.dialect.Oracle12cDialect");

        return adapter;
    }
    
    /**
     * 載入 Entity
     * @return
     */
    @Bean
    public LocalContainerEntityManagerFactoryBean db2EntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(datasource);
        emf.setPackagesToScan(new String[] { "idv.steven.database.db2.entity" });
        emf.setJpaVendorAdapter(this.jpaVendorAdapter());
        emf.setSharedCacheMode(SharedCacheMode.NONE);

        return emf;
    }
    
    @Bean(name = "db2TransactionManager")
    public PlatformTransactionManager db2TransactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(this.db2EntityManagerFactory().getObject());
        
        return tm;
    }
}
三個 transaction manager 需有一個設定為 primary,萬一有任何的 dao 沒有指明用那一個,系統才知道預設是那個。
  • 指定 spring batch 使用那一個 dataSource
@Configuration
@EnableBatchProcessing
public class BatchConfig {
    @Bean
    BatchConfigurer configurer(@Qualifier("batchDatasource") DataSource dataSource){
  return new DefaultBatchConfigurer(dataSource);
    }
}

在有 @EnableBatchProcessing 的類別裡,建立一個 BatchConfigurer 的 bean,指定 spring batch 要使用的 dataSource。

沒有留言:

張貼留言