Search code examples
javaeclipsejunitspring-batchsqlexception

Spring Batch - run as an application in Eclipse - SQLException invalid schema name - JUnit


I am new to Spring, thank you for your help
My JUnit test class works very well :

import java.sql.SQLException;
import javax.sql.DataSource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import junit.framework.TestCase;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/Spring-Test-Context.xml")
@Configuration
@EnableAutoConfiguration

public class TestConnexionJdbc extends TestCase {

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

    @Autowired
    private DataSource dataSource;

    private TaskletTruncateDonnees tasklet;



    @Test
    public void testTaskletInstanciation() throws Exception {

        assertNotNull("datasource is not null OK",this.dataSource);
        this.tasklet  = new TaskletTruncateDonnees(this.dataSource);
        assertNotNull("tasklet is not null OK",this.tasklet);
        assertEquals("tasklet dataSource username OK",this.tasklet.getDataSourceUsername(), "my_cnx_role");

    }

    @Test
    public void testTaskletTruncate() throws SQLException {
        assertNotNull("datasource is not null OK",this.dataSource);
        this.tasklet  = new TaskletTruncateDonnees(this.dataSource);
        assertNotNull("tasklet is not null OK",this.tasklet);
        this.tasklet.truncateDonnees();
    }

}

So I tried to run a similar Spring-Batch with Eclipse > run as an application : First the Launcher :

import org.springframework.boot.SpringApplication;

public class Main {

    public static void main(String [] args) {
        System.exit(SpringApplication.exit(SpringApplication.run(BatchConfiguration.class, args)));
    }
}

And the Batch :

@ContextConfiguration("/Spring-Batch-Context.xml")
@Configuration
@EnableBatchProcessing
@EnableAutoConfiguration
public class BatchConfiguration {

    private long initMillis;

    /*@Bean
    public static DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/my_database");
        dataSource.setUsername("my_cnx_role");
        dataSource.setPassword("my_password");
        System.out.println("bean dataSource =");
        System.out.println(dataSource);
        return dataSource;
    }*/


    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private DataSource dataSource;

    private TaskletTruncateDonnees taskletTruncate;

    //tag::readerwriterprocessor[]

    @Bean
    public ItemReader<Donnee> reader() {

        Collection<Donnee> collection = new ArrayList<Donnee>();

        for(int i=0;i<1000;i++) {
            Donnee donnee = new Donnee();
            donnee.setNum_ligne(i);
            donnee.setPrenom("Johnny_"+i);
            donnee.setNom("Roll_over_The_Mountain_"+i);
            donnee.setRue(i+" Main Avenue");
            donnee.setVille("Los Angeles District num "+i);
            donnee.setEtat("La_californie_"+i);
            collection.add(donnee);
        }

        ItemReader<Donnee> reader = new IteratorItemReader<Donnee>(collection);

        return reader;
    }


    @Bean
    public ItemProcessor<Donnee, Donnee> processor() {
        return new DonneeItemProcessor();
    }

    @Bean
    public ItemWriter<Donnee> writer(DataSource dataSource) {

        long deltaMillis = System.currentTimeMillis()-this.initMillis;

        JdbcBatchItemWriter<Donnee> writer = new JdbcBatchItemWriter<Donnee>();
        writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Donnee>());
        writer.setSql("insert into schema_batch_01.table_batch_donnees_massives values (:num_ligne,"+deltaMillis+",:prenom, :nom, :rue, :ville, :etat)");
        writer.setDataSource(dataSource);
        return writer;
    }
    // end::readerwriterprocessor[]


    @Bean
    public ItemWriter<Donnee> truncater(DataSource dataSource) {

        long deltaMillis = System.currentTimeMillis()-this.initMillis;

        JdbcBatchItemWriter<Donnee> writer = new JdbcBatchItemWriter<Donnee>();
        writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Donnee>());
        writer.setSql("insert into schema_batch_01.table_batch_donnees_massives values (:num_ligne,"+deltaMillis+",:prenom, :nom, :rue, :ville, :etat)");
        writer.setDataSource(dataSource);
        return writer;
    }

    @Bean
    public Job job(Step taskletTruncate,Step stepInserts) {

        this.initMillis = System.currentTimeMillis();

        Job job = jobBuilderFactory.get("job1")
                .incrementer(new RunIdIncrementer())
                .start(taskletTruncate).next(stepInserts)
                .build();

        return job;
    }

    @Bean 
    public Step taskletTruncate() {

        this.taskletTruncate = new TaskletTruncateDonnees(this.dataSource);

        return stepBuilderFactory.get("taskletTruncate")
                .tasklet(this.taskletTruncate).build();
    }


    @Bean
    public Step stepInserts(ItemReader<Donnee> reader,ItemWriter<Donnee> writer) {

        Step insertSteps = stepBuilderFactory.get("stepInserts")
                .<Donnee, Donnee> chunk(10)
                .reader(reader)
                .writer(writer)
                .build();

        return insertSteps;
    }
    // end::jobstep[1]

}

PROBLEM
When I use @ContextConfiguration("/Spring-Test-Context.xml"), test works !!
when I use @Bean in my BatchConfiguration, batch works !!
When I use @ContextConfiguration("/Spring-Batch-Context.xml") batch fails ...
Of course the xml are the same
Here is Eclispe output : the username is WRONG it is SA, why ?
The usernaeme is correct for the JUnit test and for the Batch using @Bean ...

2014-07-31 16:28:32.487  INFO 4300 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@786c4ad7: startup date [Thu Jul 31 16:28:32 CEST 2014]; root of context hierarchy
2014-07-31 16:28:33.673  WARN 4300 --- [           main] o.s.c.a.ConfigurationClassEnhancer       : @Bean method ScopeConfiguration.stepScope is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean Javadoc for complete details
2014-07-31 16:28:33.688  WARN 4300 --- [           main] o.s.c.a.ConfigurationClassEnhancer       : @Bean method ScopeConfiguration.jobScope is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean Javadoc for complete details
2014-07-31 16:28:33.939  INFO 4300 --- [           main] o.s.j.d.e.EmbeddedDatabaseFactory        : Creating embedded database 'testdb'
2014-07-31 16:28:35.109  INFO 4300 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executing SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql]
2014-07-31 16:28:35.141  INFO 4300 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executed SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql] in 32 ms.
2014-07-31 16:28:35.328  INFO 4300 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-07-31 16:28:35.360  INFO 4300 --- [           main] o.s.b.a.b.JobLauncherCommandLineRunner   : Running default command line with: []
2014-07-31 16:28:35.375  INFO 4300 --- [           main] o.s.b.c.r.s.JobRepositoryFactoryBean     : No database type set, using meta data indicating: HSQL
2014-07-31 16:28:35.547  INFO 4300 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : No TaskExecutor has been set, defaulting to synchronous executor.
2014-07-31 16:28:35.656  INFO 4300 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{run.id=1}]
2014-07-31 16:28:35.687  INFO 4300 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [taskletTruncate]
truncateDonnees DataSource= 
org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy@3b54c1f8
Username=
SA
2014-07-31 16:28:35.718  INFO 4300 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
2014-07-31 16:28:35.859  INFO 4300 --- [           main] o.s.jdbc.support.SQLErrorCodesFactory    : SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
2014-07-31 16:28:35.874 ERROR 4300 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step taskletTruncate in job job1

org.springframework.jdbc.UncategorizedSQLException: StatementCallback; uncategorized SQLException for SQL [truncate table "schema_batch_01"."table_batch_donnees_massives"]; SQL state [3F000]; error code [-4850]; invalid schema name: schema_batch_01; nested exception is java.sql.SQLException: invalid schema name: schema_batch_01
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)

Any IDEA ?


Solution

  • Dont use @ContextConfiguration for your main configuration files. It is for integration testing. That's why it's in the test jar. See the api

    @ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests.

    Instead use @ImportResource

    this annotation provides functionality similar to the element in Spring XML. It is typically used when designing @Configuration classes to be bootstrapped by AnnotationConfigApplicationContext, but where some XML functionality such as namespaces is still necessary.

    Doing this you also need to make sure you don't have a circular reference, meaning you don't have a component-scan in the xml file that points to the java config class. Also if you do have this, and you need to take it out, make sure you have a @ComponentScan in the java config file that brings back any (if at all) component lost from taking out that xml component scan.

    With all that said, I have a feeling this may be your issue, based on the three scenarios and which one failed and which one succeeded. I've done some simple testing where the same problem occurs - not the exact same, but problems with beans not being read.