1
votes

I followed this tutorial to configure a Spring Batch job in Java. It makes provision for multiple data sources by using an interface that's then implemented by each data source.

This is what I have so far:

InfrastructureConfig.java

public interface InfrastructureConfiguration {
    @Bean
    DataSource dataSource();
}

MySQLConfig.java

@Configuration
@Primary
public class MySQLConfiguration implements InfrastructureConfiguration {

    @Bean
    public DataSource dataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/employees?useSSL=false");
        dataSource.setUsername("testing");
        dataSource.setPassword("testing");
        return dataSource;
    }
}

PostgreSQLConfig.java

@Configuration
public class PostgreSQLConfiguration implements InfrastructureConfiguration {

    @Bean
    public DataSource dataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
        dataSource.setUsername("postgres");
        dataSource.setPassword("testing");
        return dataSource;
    }
}

JobConfig.java

@Configuration
public class JobConfig {

    @Autowired
    private InfrastructureConfig infrastructureConfig

    ....
}

By using the @Primary annotation for my MySQLConfig, I'd expect the mySQLConfig bean to be used. Instead, I get this:

2017-03-09 12:46:21.422  INFO 1496 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'dataSource' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=mySQLConfiguration; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [config/MySQLConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=postgreSQLConfiguration; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [config/PostgreSQLConfiguration.class]]

It's overriding the mySQLConfig bean with the postgreSQLConfig bean, and thus using the postgresql driver. The question is, why?

3

3 Answers

1
votes

Place @Primary on the method (next to @Bean) instead of on the class level.

0
votes

Try to use @Qualifier(name = "") then to explicitly pick instance which you want.

It's also strange that you are having @Bean annotation on interface method. Also it looks bad practice for me to use autowire @Configuration classes. They are java based configs for instantiating beans.

0
votes

You could try the @Profiles annotation.

Annotate mySql configuration class with @Profile("mySql") and the Postgres configuration with @Profile("myPostgresql")

Then annotate the JobConfig configuration with @ActiveProfiles("mySql"). This way JobConfig should ignore the postgres configuration.

Say you use MySQL in test, you could annotate the test class with @ActiveProfiles("mySql")

Spring Profiles documentation

Example from the documentation:

@Configuration
public class AppConfig {

  @Bean("dataSource")
  @Profile("development") 
  public DataSource standaloneDataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.HSQL)
        .addScript("classpath:com/bank/config/sql/schema.sql")
        .addScript("classpath:com/bank/config/sql/test-data.sql")
        .build();
  }

  @Bean("dataSource")
  @Profile("production") 
  public DataSource jndiDataSource() throws Exception {
    Context ctx = new InitialContext();
    return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
  }
}