Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

This is a simple Spring Batch project. This implementation will read from a database table and write to a database table via JPA.

Maven Dependencies

The Maven dependency for Spring Batch is:

<dependency>
    <groupId>org.springframework.batch</groupId>
    <artifactId>spring-batch-core</artifactId>
    <version>${spring.batch.version}</version>
</dependency>

Please note that Spring Batch does use the Spring Framework. Spring Batch 3.0.6 corresponds to Spring 4.0.5. Attempting to use a newer version of Spring will cause runtime errors.

You will also need dependencies for communicating with the database.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.0.7.Final</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>

Below is my entire pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.javaninja</groupId>
    <artifactId>spring-batch-jpa</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--This is the version of Spring that Spring Batch uses-->
        <spring.version>4.0.5.RELEASE</spring.version>
        <spring.batch.version>3.0.6.RELEASE</spring.batch.version>
        <slf4j.version>1.7.13</slf4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-core</artifactId>
            <version>${spring.batch.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.0.7.Final</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

        <!--
            Logging
        -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <!--
            Testing
        -->
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <version>${spring.batch.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Spring and Spring Batch Context

Job Configuration

You will need the standard JobRepository, JobLauncher and TransactionManager (See Database/JPA below).

<!--This repository is only really intended for use in testing and rapid prototyping-->
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"
      p:jobRepository-ref="jobRepository"/>

JpaPagingItemReader

The reading of data from the database via JPA is handled by a JpaPagingItemReader. The JpaPagingItemReader requires the following:

  • EntityManagerFactory – manages the entities (see JPA configuration below).
  • queryString – defines the query used to read the entities. In this case, it reads all entities of a given type.

The configuration for the JpaPagingItemReader is:

<!-- ItemReader which reads data from the database -->
<bean id="itemReader" class="org.springframework.batch.item.database.JpaPagingItemReader"
      p:entityManagerFactory-ref="entityManagerFactory"
      p:queryString="SELECT s FROM CamAffiliateEntity s"/>

JpaItemWriter

The writing of the JPA data is handled by JpaItemWriter. The JpaItemWriter also requires an EntityManagerFactory (see JPA configuration below).

The configuration for the JpaItemWriter is:

<!-- ItemWriter which writes the data to the database -->
<bean id="itemWriter" class="org.springframework.batch.item.database.JpaItemWriter"
    p:entityManagerFactory-ref="entityManagerFactory"/>

JPA Configuration

You will also need the following to configure the Jpa-support to read the records from the database:

  • EntityManagerFactory – manages the entities.
  • JpaVendorAdapter – exposes vendor-specific JPA properties.
  • DataSource – manages database connections.
  • TransactionManager – manages transactions.

Spring Batch requires a transaction manager and so does Spring’s JPA support. In this case, I’m using the same transaction manager for both. I’m not sure if this is correct, but it appears to work.

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
      p:driverClassName="com.mysql.jdbc.Driver"
      p:url="jdbc:mysql://database.javaninja.com/batch?zeroDateTimeBehavior=convertToNull"
      p:username="javaninja"
      p:password="javaninja"/>
<!-- JPA EntityManagerFactory configuration -->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
      p:dataSource-ref="dataSource"
      p:packagesToScan="com.javaninja.batch"
      p:jpaVendorAdapter-ref="jpaVendorAdapter"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>

The entire Spring context is below:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="classpath:META-INF/spring/batchContext.xml"/>

    <context:component-scan base-package="com.javaninja.batch"/>


    <!--This repository is only really intended for use in testing and rapid prototyping-->
    <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>

    <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"
          p:jobRepository-ref="jobRepository"/>



    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
          p:driverClassName="com.mysql.jdbc.Driver"
          p:url="jdbc:mysql://10.20.13.53/cam?zeroDateTimeBehavior=convertToNull"
          p:username="usr_ig_dev"
          p:password="igaming_developer"/>

    <!-- JPA EntityManagerFactory configuration -->
    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          p:dataSource-ref="dataSource"
          p:packagesToScan="com.javaninja.batch"
          p:jpaVendorAdapter-ref="jpaVendorAdapter"/>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>

    <!-- ItemReader which reads data from the database -->
    <bean id="itemReader" class="org.springframework.batch.item.database.JpaPagingItemReader"
          p:entityManagerFactory-ref="entityManagerFactory"
          p:queryString="SELECT s FROM CamAffiliateEntity s"/>

    <!-- ItemWriter which writes the data to the database -->
    <bean id="itemWriter" class="org.springframework.batch.item.database.JpaItemWriter"
        p:entityManagerFactory-ref="entityManagerFactory"/>

</beans>

Job Configuration

The XML configuration for the JPA batch job is pretty much the same as my other examples.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:batch="http://www.springframework.org/schema/batch"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd">

    <batch:job id="carJob">
        <batch:step id="step1">
            <batch:tasklet>
                <batch:chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
            </batch:tasklet>
        </batch:step>
    </batch:job>

</beans>

The entire project used to write this blog is located on GitHub sheltonn / spring-batch-jpa

February 19th, 2016

Posted In: Java, java ninja, Javaninja, Spring, Spring Batch

Leave a Reply

Your email address will not be published. Required fields are marked *

LinkedIn Auto Publish Powered By : XYZScripts.com