diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java index b9dcfc2ec..13d899e63 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java @@ -19,6 +19,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.sql.DataSource; @@ -55,6 +56,7 @@ import org.springframework.util.Assert; * * @author Dave Syer * @author Thomas Risberg + * @author Michael Minella * @since 2.0 */ public class JdbcBatchItemWriter implements ItemWriter, InitializingBean { @@ -106,7 +108,8 @@ public class JdbcBatchItemWriter implements ItemWriter, InitializingBean { /** * Public setter for the {@link ItemSqlParameterSourceProvider}. * @param itemSqlParameterSourceProvider the {@link ItemSqlParameterSourceProvider} to - * set. This is required when using named parameters for the SQL statement. + * set. This is required when using named parameters for the SQL statement and the type + * to be written does not implement {@link Map}. */ public void setItemSqlParameterSourceProvider(ItemSqlParameterSourceProvider itemSqlParameterSourceProvider) { this.itemSqlParameterSourceProvider = itemSqlParameterSourceProvider; @@ -147,10 +150,7 @@ public class JdbcBatchItemWriter implements ItemWriter, InitializingBean { } usingNamedParameters = true; } - if (usingNamedParameters) { - Assert.notNull(itemSqlParameterSourceProvider, "Using SQL statement with named parameters requires an ItemSqlParameterSourceProvider"); - } - else { + if (!usingNamedParameters) { Assert.notNull(itemPreparedStatementSetter, "Using SQL statement with '?' placeholders requires an ItemPreparedStatementSetter"); } } @@ -159,6 +159,7 @@ public class JdbcBatchItemWriter implements ItemWriter, InitializingBean { * @see org.springframework.batch.item.ItemWriter#write(java.util.List) */ @Override + @SuppressWarnings({"unchecked", "rawtypes"}) public void write(final List items) throws Exception { if (!items.isEmpty()) { @@ -170,12 +171,16 @@ public class JdbcBatchItemWriter implements ItemWriter, InitializingBean { int[] updateCounts = null; if (usingNamedParameters) { - SqlParameterSource[] batchArgs = new SqlParameterSource[items.size()]; - int i = 0; - for (T item : items) { - batchArgs[i++] = itemSqlParameterSourceProvider.createSqlParameterSource(item); + if(items.get(0) instanceof Map) { + updateCounts = namedParameterJdbcTemplate.batchUpdate(sql, items.toArray(new Map[0])); + } else { + SqlParameterSource[] batchArgs = new SqlParameterSource[items.size()]; + int i = 0; + for (T item : items) { + batchArgs[i++] = itemSqlParameterSourceProvider.createSqlParameterSource(item); + } + updateCounts = namedParameterJdbcTemplate.batchUpdate(sql, batchArgs); } - updateCounts = namedParameterJdbcTemplate.batchUpdate(sql, batchArgs); } else { updateCounts = (int[]) namedParameterJdbcTemplate.getJdbcOperations().execute(sql, new PreparedStatementCallback() { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcBatchItemWriterNamedParameterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcBatchItemWriterNamedParameterTests.java index c3d6c4f74..7163a631b 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcBatchItemWriterNamedParameterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcBatchItemWriterNamedParameterTests.java @@ -24,11 +24,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -37,13 +40,15 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource; /** * @author Thomas Risberg * @author Will Schipp + * @author Michael Minella */ +@SuppressWarnings({"rawtypes", "serial", "unchecked"}) public class JdbcBatchItemWriterNamedParameterTests { - private JdbcBatchItemWriter writer = new JdbcBatchItemWriter(); + private JdbcBatchItemWriter writer = new JdbcBatchItemWriter(); + + private NamedParameterJdbcOperations namedParameterJdbcOperations; - private NamedParameterJdbcOperations namedParameterJdbcOperations; - private String sql = "update foo set bar = :bar where id = :id"; @SuppressWarnings("unused") @@ -55,7 +60,7 @@ public class JdbcBatchItemWriterNamedParameterTests { this.id = 1L; this.bar = bar; } - + public Long getId() { return id; } @@ -71,12 +76,12 @@ public class JdbcBatchItemWriterNamedParameterTests { public void setBar(String bar) { this.bar = bar; } - + } @Before public void setUp() throws Exception { - namedParameterJdbcOperations = mock(NamedParameterJdbcOperations.class); + namedParameterJdbcOperations = mock(NamedParameterJdbcOperations.class); writer.setSql(sql); writer.setJdbcTemplate(namedParameterJdbcOperations); writer.setItemSqlParameterSourceProvider( @@ -113,22 +118,27 @@ public class JdbcBatchItemWriterNamedParameterTests { assertTrue("Message does not contain 'sql'.", message.indexOf("sql") >= 0); } writer.setSql("select * from foo where id = :id"); - try { - writer.afterPropertiesSet(); - fail("Expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) { - // expected - String message = e.getMessage(); - assertTrue("Message does not contain 'ItemSqlParameterSourceProvider'.", message.indexOf("ItemSqlParameterSourceProvider") >= 0); - } - writer.setItemSqlParameterSourceProvider( - new BeanPropertyItemSqlParameterSourceProvider()); + writer.afterPropertiesSet(); } @Test public void testWriteAndFlush() throws Exception { + writer.setItemSqlParameterSourceProvider(null); + ArgumentCaptor captor = ArgumentCaptor.forClass(Map[].class); + + when(namedParameterJdbcOperations.batchUpdate(eq(sql), + captor.capture())) + .thenReturn(new int[] {1}); + writer.write(Collections.singletonList(new HashMap() {{put("foo", "bar");}})); + + assertEquals(1, captor.getValue().length); + Map results = captor.getValue()[0]; + assertEquals("bar", results.get("foo")); + } + + @Test + public void testWriteAndFlushMap() throws Exception { when(namedParameterJdbcOperations.batchUpdate(eq(sql), eqSqlParameterSourceArray(new SqlParameterSource[] {new BeanPropertySqlParameterSource(new Foo("bar"))}))) .thenReturn(new int[] {1}); @@ -168,42 +178,42 @@ public class JdbcBatchItemWriterNamedParameterTests { public static SqlParameterSource[] eqSqlParameterSourceArray(SqlParameterSource[] in) { argThat(new SqlParameterSourceArrayEquals(in)); - return null; + return null; } public static class SqlParameterSourceArrayEquals extends BaseMatcher { - private SqlParameterSource[] expected; + private SqlParameterSource[] expected; - public SqlParameterSourceArrayEquals(SqlParameterSource[] expected) { - this.expected = expected; - } + public SqlParameterSourceArrayEquals(SqlParameterSource[] expected) { + this.expected = expected; + } - @Override - public boolean matches(Object actual) { - if (!(actual instanceof SqlParameterSource[])) { - return false; - } - SqlParameterSource[] actualArray = (SqlParameterSource[])actual; - if (expected.length != actualArray.length) { - return false; - } - for (int i = 0; i < expected.length; i++) { - if (!expected[i].getClass().equals(actualArray[i].getClass())) { - return false; - } - } - return true; - } + @Override + public boolean matches(Object actual) { + if (!(actual instanceof SqlParameterSource[])) { + return false; + } + SqlParameterSource[] actualArray = (SqlParameterSource[])actual; + if (expected.length != actualArray.length) { + return false; + } + for (int i = 0; i < expected.length; i++) { + if (!expected[i].getClass().equals(actualArray[i].getClass())) { + return false; + } + } + return true; + } @Override public void describeTo(Description description) { - description.appendText("eqSqlParameterSourceArray("); + description.appendText("eqSqlParameterSourceArray("); description.appendText(expected.getClass().getName()); description.appendText(" with length \""); description.appendValue(expected.length); - description.appendText("\")"); + description.appendText("\")"); } - } + } }