Skip to content

Commit

Permalink
Support converting item to parameter object
Browse files Browse the repository at this point in the history
Fixes gh-21
  • Loading branch information
kazuki43zoo committed May 4, 2018
1 parent 0280442 commit c5883dc
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;

Expand Down Expand Up @@ -62,6 +63,8 @@ public class MyBatisBatchItemWriter<T> implements ItemWriter<T>, InitializingBea

private boolean assertUpdates = true;

private Converter<T, ?> itemToParameterConverter = new PassThroughConverter<>();

/**
* Public setter for the flag that determines whether an assertion is made
* that all items cause at least one row to be updated.
Expand Down Expand Up @@ -102,6 +105,18 @@ public void setStatementId(String statementId) {
this.statementId = statementId;
}

/**
* Public setter for a converter that converting item to parameter object.
* <p>
* By default implementation, an item does not convert.
*
* @param itemToParameterConverter a converter that converting item to parameter object
* @since 2.0.0
*/
public void setItemToParameterConverter(Converter<T, ?> itemToParameterConverter) {
this.itemToParameterConverter = itemToParameterConverter;
}

/**
* Check mandatory properties - there must be an SqlSession and a statementId.
*/
Expand All @@ -110,6 +125,7 @@ public void afterPropertiesSet() {
notNull(sqlSessionTemplate, "A SqlSessionFactory or a SqlSessionTemplate is required.");
isTrue(ExecutorType.BATCH == sqlSessionTemplate.getExecutorType(), "SqlSessionTemplate's executor type must be BATCH");
notNull(statementId, "A statementId is required.");
notNull(itemToParameterConverter, "A itemToParameterConverter is required.");
}

/**
Expand All @@ -122,7 +138,7 @@ public void write(final List<? extends T> items) {
LOGGER.debug(() -> "Executing batch with " + items.size() + " items.");

for (T item : items) {
sqlSessionTemplate.update(statementId, item);
sqlSessionTemplate.update(statementId, itemToParameterConverter.convert(item));
}

List<BatchResult> results = sqlSessionTemplate.flushStatements();
Expand All @@ -146,4 +162,13 @@ public void write(final List<? extends T> items) {
}
}

private static class PassThroughConverter<T> implements Converter<T, T> {

@Override
public T convert(T source) {
return source;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.batch.MyBatisBatchItemWriter;
import org.springframework.core.convert.converter.Converter;

/**
* A builder for the {@link MyBatisBatchItemWriter}.
Expand All @@ -32,6 +33,7 @@ public class MyBatisBatchItemWriterBuilder<T> {
private SqlSessionFactory sqlSessionFactory;
private String statementId;
private Boolean assertUpdates;
private Converter<T, ?> itemToParameterConverter;

/**
* Set the {@link SqlSessionTemplate} to be used by writer for database access.
Expand Down Expand Up @@ -83,6 +85,18 @@ public MyBatisBatchItemWriterBuilder<T> assertUpdates(boolean assertUpdates) {
return this;
}

/**
* Set a converter that converting item to parameter object.
*
* @param itemToParameterConverter a converter that converting item to parameter object
* @return this instance for method chaining
* @see MyBatisBatchItemWriter#setItemToParameterConverter(Converter)
*/
public MyBatisBatchItemWriterBuilder<T> itemToParameterConverter(Converter<T, ?> itemToParameterConverter) {
this.itemToParameterConverter = itemToParameterConverter;
return this;
}

/**
* Returns a fully built {@link MyBatisBatchItemWriter}.
*
Expand All @@ -96,6 +110,9 @@ public MyBatisBatchItemWriter<T> build() {
if (this.assertUpdates != null) {
writer.setAssertUpdates(this.assertUpdates);
}
if (this.itemToParameterConverter != null) {
writer.setItemToParameterConverter(this.itemToParameterConverter);
}
return writer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,28 @@
package org.mybatis.spring.batch;

import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.ExecutorType;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.batch.domain.Employee;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;

import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.BDDMockito.*;
Expand Down Expand Up @@ -77,4 +85,50 @@ void testZeroUpdateCountShouldThrowException() {
);
}

@Test
void testItemToParameterConverterIsDefault() {
this.writer.setAssertUpdates(false);
this.writer.setStatementId("updateEmployee");

Employee employee = new Employee();
List<Employee> employees = Collections.singletonList(employee);
writer.write(employees);

Mockito.verify(this.mockSqlSessionTemplate).update("updateEmployee", employee);
}

@Test
void testSetItemToParameterConverter() {
this.writer.setAssertUpdates(false);
this.writer.setStatementId("updateEmployee");
this.writer.setItemToParameterConverter(item -> {
Map<String, Object> parameter = new HashMap<>();
parameter.put("item", item);
parameter.put("now",
LocalDateTime.now(Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault())));
return parameter;
});

Employee employee = new Employee();
List<Employee> employees = Collections.singletonList(employee);
writer.write(employees);

Map<String, Object> parameter = new HashMap<>();
parameter.put("item", employee);
parameter.put("now",
LocalDateTime.now(Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault())));
Mockito.verify(this.mockSqlSessionTemplate).update("updateEmployee", parameter);
}

@Test
void testItemToParameterConverterIsNull() {
given(mockSqlSessionTemplate.getExecutorType()).willReturn(ExecutorType.BATCH);
this.writer.setStatementId("updateEmployee");
writer.setItemToParameterConverter(null);

assertThrows(IllegalArgumentException.class, () -> writer.afterPropertiesSet(),
"A itemToParameterConverter is required.");

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
import org.mybatis.spring.batch.MyBatisBatchItemWriter;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
* Tests for {@link MyBatisBatchItemWriterBuilder}.
Expand Down Expand Up @@ -137,6 +139,38 @@ void testConfigurationAssertUpdatesIsFalse() {

}

@Test
void testConfigurationSetItemToParameterConverter() {

// @formatter:off
MyBatisBatchItemWriter<Foo> itemWriter = new MyBatisBatchItemWriterBuilder<Foo>()
.sqlSessionFactory(this.sqlSessionFactory)
.statementId("updateFoo")
.itemToParameterConverter(item -> {
Map<String, Object> parameter = new HashMap<>();
parameter.put("item", item);
parameter.put("now", LocalDateTime.now(Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault())));
return parameter;
})
.build();
// @formatter:on
itemWriter.afterPropertiesSet();

List<Foo> foos = getFoos();

itemWriter.write(foos);

Map<String, Object> parameter = new HashMap<>();
parameter.put("now",
LocalDateTime.now(Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault())));
parameter.put("item", foos.get(0));
Mockito.verify(this.sqlSession).update("updateFoo", parameter);
parameter.put("item", foos.get(1));
Mockito.verify(this.sqlSession).update("updateFoo", parameter);
parameter.put("item", foos.get(2));
Mockito.verify(this.sqlSession).update("updateFoo", parameter);
}

private List<Foo> getFoos() {
return Arrays.asList(new Foo("foo1"), new Foo("foo2"), new Foo("foo3"));
}
Expand Down

0 comments on commit c5883dc

Please sign in to comment.