Thursday, August 19, 2021

Spring Batch Processing With List of Objects in batchUpdate() Method

In the post Spring Batch Processing Using JDBCTemplate batchUpdate() Method we have already seen how you can use batchUpdate() method with BatchPreparedStatementSetter interface for processing similar queries as batch. In this post we’ll see another option for batch processing in Spring where list of objects is passed.


Spring Batch processing with a List of objects

For batch processing with Spring both the JdbcTemplate and the NamedParameterJdbcTemplate provides an alternate way to use batchUpdate() method. Instead of implementing a special batch interface BatchPreparedStatementSetter, you provide all parameter values in the call as a list. Spring framework loops over these values and uses an internal prepared statement setter.

If you are using NamedParameterJdbcTemplate then you provide an array of SqlParameterSource, one entry for each member of the batch. To create this array you can use SqlParameterSourceUtils.createBatch method.

If you are using JdbcTemplate then you need to provide an Object array.

Let’s see example of batch processing in Spring with both JdbcTemplate and NamedParameterJdbcTemplate to make it clearer. Project structure used is same as used in this post Spring Batch Processing Using JDBCTemplate batchUpdate() Method, so please refer it for setting the project structure.

Spring batch processing using JdbcTemplate

Java classes needed for Spring batch processing example are as follows-

  1. Employee Bean class (Employee.java)
  2. DAO interface (EmployeeDAO.java)
  3. DAO interface implementation class (EmployeeDAOImpl.java)

Employee.java

public class Employee {
 private int empId;
 private String empName;
 private int age;
 public Employee(String empName, int age){
  this.empName = empName;
  this.age = age;
 }
 public int getEmpId() {
  return empId;
 }
 public void setEmpId(int empId) {
  this.empId = empId;
 }
 public String getEmpName() {
  return empName;
 }
 public void setEmpName(String empName) {
  this.empName = empName;
 }
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
}

EmployeeDAO.java

public interface EmployeeDAO {
    public int[] batchInsert(final List<Employee> employees);
}

EmployeeDAOImpl.java

import java.util.ArrayList;
import java.util.List;
import org.netjs.dao.EmployeeDAO;
import org.netjs.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class EmployeeDAOImpl implements EmployeeDAO{
  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Override
  public int[] batchInsert(final List<Employee> employees) {
    final String INSERT_EMP_QUERY = "insert into employee (name, age) values (?, ?)";
    List<Object[]> empList = new ArrayList<Object[]>();
    for(Employee emp : employees) {
      Object[] values = new Object[] {emp.getEmpName(), emp.getAge()};
      empList.add(values);
    }
    return this.jdbcTemplate.batchUpdate(INSERT_EMP_QUERY, empList);                
  } 
}

As you can see in the class an Object[] array is created with the values for each Employee and that is passed as parameter in batchUpdate() method. Spring framework will do the work of looping over these values and use an internal prepared statement setter to set values in the query.

XML Configuration (appContext.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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">
    
    <context:component-scan base-package="org.netjs.DAOImpl" />
    <!--  For reading properties files --> 
    <context:property-placeholder location="classpath:config/db.properties" />
    <!-- Data Source configuration --> 
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value = "${db.driverClassName}" />
        <property name="url" value = "${db.url}" />
        <property name="username" value = "${db.username}" />
        <property name="password" value = "${db.password}" />
        <property name="initialSize" value = "${pool.initialSize}" />
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
        <property name="dataSource" ref="dataSource"></property>  
    </bean> 
</beans>

Spring batch processing using NamedParameterJdbcTemplate

Example using NamedParameterJdbcTemplate where an array of SqlParameterSource is passed as parameter in batchUpdate() method.

import java.util.List;
import org.netjs.dao.EmployeeDAO;
import org.netjs.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.stereotype.Repository;
@Repository
public class EmployeeDAOImpl implements EmployeeDAO{
  @Autowired
  private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

  @Override
  public int[] batchInsert(final List<Employee> employees) {
    final String INSERT_EMP_QUERY = "insert into employee (name, age) values (:empName, :age)";
    return this.namedParameterJdbcTemplate.batchUpdate(INSERT_EMP_QUERY, SqlParameterSourceUtils.createBatch(employees));                
  } 
}

XML Configuration (appContext.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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">
    
    <context:component-scan base-package="org.netjs.DAOImpl" />
    <!--  For reading properties files --> 
    <context:property-placeholder location="classpath:config/db.properties" />
    <!-- Data Source configuration --> 
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value = "${db.driverClassName}" />
        <property name="url" value = "${db.url}" />
        <property name="username" value = "${db.username}" />
        <property name="password" value = "${db.password}" />
        <property name="initialSize" value = "${pool.initialSize}" />
    </bean>
    <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">  
        <constructor-arg ref="dataSource"></constructor-arg>  
    </bean> 
</beans>

Test class

You can use the following code in order to test the batch insertion of rows in DB table.

public class App {
  public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext
         ("appcontext.xml");
    EmployeeDAO empDAO = context.getBean("employeeDAOImpl", EmployeeDAOImpl.class);
    List<Employee> empList = createEmpList();
    int[] rows = empDAO.batchInsert(empList);
    System.out.println("Number of rows inserted- " + rows.length);
  }
    
  private static List<Employee> createEmpList(){
    Employee emp1 = new Employee("Ben", 25);
    Employee emp2 = new Employee("Virat", 29);
    Employee emp3 = new Employee("Joe", 26);
    List<Employee> empList= new ArrayList<Employee>();
    empList.add(emp1);
    empList.add(emp2);
    empList.add(emp3);
    return empList;
  }
}

Passing parameters as Map in NamedParameterJdbcTemplate

If you want to pass parameters as Map the way it is generally done for NamedParameterJdbcTemplate then batchUpdate() method does provide an overloaded variant where array of Maps can be passed. In that case EmployeeDAOImpl class can be written as following.

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.netjs.dao.EmployeeDAO;
import org.netjs.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class EmployeeDAOImpl implements EmployeeDAO{
  @Autowired
  private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

  @Override
  public int[] batchInsert(final List<Employee> employees) {
    final String INSERT_EMP_QUERY = "insert into employee (name, age) values (:name, :age)";
    // Map array
    Map<String, Object>[] paramMap = new HashMap[employees.size()];
    for(int i = 0; i < employees.size(); i++) {
      Employee emp = employees.get(i);
      Map<String, Object> tempMap = new HashMap<String, Object>();
      tempMap.put("name", emp.getEmpName());
      tempMap.put("age", emp.getAge());
      paramMap[i] = tempMap;
    }      
    return this.namedParameterJdbcTemplate.batchUpdate(INSERT_EMP_QUERY, paramMap);                
  } 
}

That's all for this topic Spring Batch Processing With List of Objects. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Transaction Attributes - Propagation And Isolation Level Settings
  2. Spring Component Scan to Automatically Discover Beans
  3. Autowiring in Spring Using @Autowired and @Inject Annotations
  4. How to Read Properties File in Spring Framework
  5. Batch Processing in Java JDBC - Insert, Update Queries as a Batch

You may also like-

  1. Spring Constructor Based Dependency Injection
  2. Spring MessageSource Internationalization (i18n) Support
  3. @Conditional Annotation in Spring
  4. ApplicationContextAware And BeanNameAware Interfaces in Spring Framework
  5. Difference Between ArrayList And CopyOnWriteArrayList in Java
  6. Difference Between Comparable and Comparator in Java
  7. Ternary Operator in Java With Examples
  8. Data Compression in Hadoop