Search code examples
springspring-dataspring-data-jpaentitymanagertransactional

Spring JPA Multiple Datasources


I've seen similar questions on here but haven't been able to find an answer that truely works for me.

I have 2 different datasources, each with its own transaction manager and entity manager factory. They are each defined in their own config class:

@Configuration
@ComponentScan({
"com.sprint.cst.data.v8p",
"com.sprint.v8p.data",
"com.sprint.v8p.data.util",
"com.sprint.v8p.toptower.repositories",
"com.sprint.v8p.toptower.util"})
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {
    "com.sprint.cst.data.v8p.repository",
    "com.sprint.v8p.repository",
    "com.sprint.eib.data" })
public class JpaConfiguration {

@Value("${cst.db.generateDdl:true}")
boolean generateDdl;
String ddlGenerationStrategy = "create-or-extend-tables";
@Value("${cst.db.showSql:true}")
boolean showSql;

private String[] defaultEntityPackagesToScan =
        new String[]{
        "com.sprint.v8p.domain",
        "com.sprint.v8p.toptower.domain",
        "com.sprint.cst.data.v8p.entity",
        "com.sprint.eib.entity"};

private JpaDialect jpaDialect = new EclipseLinkJpaDialect();


/// Primary JPA
@Primary
@Autowired
@Bean(name = "entityManagerFactory")
@Qualifier("entityManagerFactory")
public LocalContainerEntityManagerFactoryBean defaultEntityManagerFactory(DataSource dataSource) {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    //em.setPersistenceUnitName(UnitNames.DefaultPersistenceUnit);
    em.setDataSource(dataSource);
    em.setPackagesToScan(defaultEntityPackagesToScan);
    em.setJpaVendorAdapter(vendorAdapter());
    em.setJpaDialect(jpaDialect);
    em.setJpaProperties(defaultJpaProperties());
    return em;
}

@Primary
@Bean(name="transactionManager")
public PlatformTransactionManager defaultTransactionManager(
        EntityManagerFactory emf) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(emf);

    return transactionManager;
}

//'Datasource' Transaction Manager
//leveraged by ice events functionality; batch inserts/updates
@Autowired
@Bean(name="dsTransactionManager")
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
    dataSourceTransactionManager.setDataSource(dataSource);
    return dataSourceTransactionManager;
}


/// Other Beans

@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
    return new PersistenceExceptionTranslationPostProcessor();
}

@Bean
public PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor(){
    return new PersistenceAnnotationBeanPostProcessor();
}

And:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    basePackages = {
        "com.sprint.cst.data.v8p.sp2.repository"},
    entityManagerFactoryRef="sp2EntityManagerFactory",
    transactionManagerRef="sp2TransactionManager")
public class JpaSp2Configuration {

@Value("${cst.db.generateDdl:true}")
boolean generateDdl;
//@Value("${cst.db.eclipselink.ddl-generation:'create-or-extend-tables'}")
String ddlGenerationStrategy = "create-or-extend-tables";
@Value("${cst.db.showSql:true}")
boolean showSql;

private String[] sp2EntityPackagesToScan =
        new String[]{
        "com.sprint.cst.data.v8p.sp2.entity"};

private JpaDialect jpaDialect = new EclipseLinkJpaDialect();

static final String sp2DataSourceJNDIName = "jdbc/cst-ds-sp2";

/// Secondary JPA
@Bean(name= "sp2EntityManagerFactory")
@Qualifier("sp2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean sp2EntityManagerFactory(@Qualifier("sp2DataSource") DataSource sp2DataSource) {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setBeanName("sp2EntityManagerFactory");
    //em.setPersistenceUnitName(UnitNames.Sp2PersistenceUnit);
    em.setDataSource(sp2DataSource);
    em.setPackagesToScan(sp2EntityPackagesToScan);
    em.setJpaVendorAdapter(vendorAdapter());
    em.setJpaDialect(jpaDialect );
    em.setJpaProperties(defaultJpaProperties());
    return em;
}

@Autowired
@Bean(name="sp2TransactionManager")
@Qualifier("sp2TransactionManager")
public PlatformTransactionManager sp2TransactionManager(
        @Qualifier("sp2EntityManagerFactory") LocalContainerEntityManagerFactoryBean sp2EntityManagerFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(sp2EntityManagerFactory.getNativeEntityManagerFactory());
    //transactionManager.setPersistenceUnitName(UnitNames.Sp2PersistenceUnit);
    return transactionManager;
}

The datasources exist in a separate datasource config. The 'primary' functions are working fine, the 'SP2' functions are not. For example:

@Transactional("sp2TransactionManager")
@Override
public void saveCallHistory(CallHistoryObject callHistoryObject) {
    .
    .
    .

    SolutionInfoEntity solutionInfoResult = solutionInfoRepository.save(callHistoryObject.getSolutionInfoEntity());
    SolutionInfoEntity save = solutionInfoRepository.findOne(callHistoryObject.getSolutionInfoEntity().getSp2Id());

The 'save' object here actually does return a result, but nothing is actually persisted in the DB. Any ideas? I feel like I've tried almost everything.


Solution

  • Make sure your repositories are in different packages (At least 1 per dataSource). And in the configuration where you have

    @EnableJpaRepositories(basePackages = {
    "com.sprint.cst.data.v8p.repository",
    "com.sprint.v8p.repository",
    "com.sprint.eib.data" })
    

    each connection should have only those repositories enabled.

    For example, if dataSource (DS1) operates on package

    com.spring.ds1.repository
    

    and dataSource (DS2) operates on package

    com.spring.ds2.repository
    

    The JpaRepositories configuration would look like this

    In DS1 configuration

    @EnableJpaRepositories(basePackages = {"com.spring.ds1.repository"})
    

    In DS2 configuration

    @EnableJpaRepositories(basePackages = {"com.spring.ds2.repository"})
    

    I had similar problems with Cassandra and with the above configuration, I was able to solve it.