Removed iBatis based components
This commit removes the iBatis based ItemReaders and ItemWriter. They were depricated in the 3.0 line in favor of the MyBatis natively provided ItemReader and ItemWriter implementations. This removes those depricated components. Resolves BATCH-2591
This commit is contained in:
@@ -75,7 +75,6 @@ allprojects {
|
||||
hibernateVersion = '5.2.2.Final'
|
||||
hibernateValidatorVersion = '5.3.0.CR1'
|
||||
hsqldbVersion = '2.3.3'
|
||||
ibatisVersion = '2.3.4.726'
|
||||
jackson2Version = "2.8.6" //?
|
||||
javaMailVersion = '1.5.6'
|
||||
javaxBatchApiVersion = '1.0'
|
||||
@@ -341,7 +340,6 @@ project('spring-batch-infrastructure') {
|
||||
}
|
||||
optional "org.hibernate:hibernate-validator:$hibernateValidatorVersion"
|
||||
optional "javax.transaction:javax.transaction-api:$javaxTransactionVersion"
|
||||
optional "org.apache.ibatis:ibatis-sqlmap:$ibatisVersion"
|
||||
optional "javax.mail:javax.mail-api:$javaMailVersion"
|
||||
optional "javax.batch:javax.batch-api:$javaxBatchApiVersion"
|
||||
compile("org.springframework:spring-oxm:$springVersion") { dep ->
|
||||
@@ -461,7 +459,6 @@ project('spring-batch-infrastructure-tests') {
|
||||
testCompile "org.mockito:mockito-core:$mockitoVersion"
|
||||
|
||||
optional "org.slf4j:slf4j-log4j12:$slf4jVersion"
|
||||
optional "org.apache.ibatis:ibatis-sqlmap:$ibatisVersion"
|
||||
compile("org.hibernate:hibernate-core:$hibernateVersion") { dep ->
|
||||
optional dep
|
||||
exclude group: 'org.jboss.spec.javax.transaction', module: 'jboss-transaction-api_1.1_spec'
|
||||
@@ -573,7 +570,6 @@ project('spring-batch-samples') {
|
||||
exclude group: 'org.jboss.spec.javax.transaction', module: 'jboss-transaction-api_1.1_spec'
|
||||
}
|
||||
compile "javax.transaction:javax.transaction-api:$javaxTransactionVersion"
|
||||
compile "org.apache.ibatis:ibatis-sqlmap:$ibatisVersion"
|
||||
compile "org.springframework:spring-aop:$springVersion"
|
||||
compile("org.springframework:spring-oxm:$springVersion") {
|
||||
exclude group: 'commons-lang', module: 'commons-lang'
|
||||
|
||||
@@ -268,15 +268,6 @@
|
||||
<profiles>
|
||||
</profiles>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[ibatis]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
<profiles>
|
||||
</profiles>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[partition]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.item.sample.Foo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = "JdbcPagingItemReaderCommonTests-context.xml")
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IbatisPagingItemReaderAsyncTests {
|
||||
|
||||
/**
|
||||
* The number of items to read
|
||||
*/
|
||||
private static final int ITEM_COUNT = 1000;
|
||||
|
||||
/**
|
||||
* The number of threads to create
|
||||
*/
|
||||
private static final int THREAD_COUNT = 10;
|
||||
|
||||
private static Log logger = LogFactory.getLog(IbatisPagingItemReaderAsyncTests.class);
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
private int maxId;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
Assert.notNull(dataSource);
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
maxId = jdbcTemplate.queryForObject("SELECT MAX(ID) from T_FOOS", Integer.class);
|
||||
for (int i = ITEM_COUNT; i > maxId; i--) {
|
||||
jdbcTemplate.update("INSERT into T_FOOS (ID,NAME,VALUE) values (?, ?, ?)", i, "foo" + i, i);
|
||||
}
|
||||
assertEquals(ITEM_COUNT, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
jdbcTemplate.update("DELETE from T_FOOS where ID>?", maxId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsyncReader() throws Throwable {
|
||||
List<Throwable> throwables = new ArrayList<Throwable>();
|
||||
int max = 10;
|
||||
for (int i = 0; i < max; i++) {
|
||||
try {
|
||||
logger.info("Testing asynch reader, iteration="+i);
|
||||
doTest();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throwables.add(e);
|
||||
}
|
||||
}
|
||||
if (!throwables.isEmpty()) {
|
||||
throw new IllegalStateException(String.format("Failed %d out of %d", throwables.size(), max), throwables
|
||||
.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws InterruptedException
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
private void doTest() throws Exception, InterruptedException, ExecutionException {
|
||||
final IbatisPagingItemReader<Foo> reader = getItemReader();
|
||||
reader.setDataSource(dataSource);
|
||||
CompletionService<List<Foo>> completionService = new ExecutorCompletionService<List<Foo>>(Executors
|
||||
.newFixedThreadPool(THREAD_COUNT));
|
||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||
completionService.submit(new Callable<List<Foo>>() {
|
||||
@Override
|
||||
public List<Foo> call() throws Exception {
|
||||
List<Foo> list = new ArrayList<Foo>();
|
||||
Foo next = null;
|
||||
do {
|
||||
next = reader.read();
|
||||
Thread.sleep(10L); // try to make it fairer
|
||||
logger.debug("Reading item: " + next);
|
||||
if (next != null) {
|
||||
list.add(next);
|
||||
}
|
||||
} while (next != null);
|
||||
return list;
|
||||
}
|
||||
});
|
||||
}
|
||||
int count = 0;
|
||||
Set<Foo> results = new HashSet<Foo>();
|
||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||
List<Foo> items = completionService.take().get();
|
||||
count += items.size();
|
||||
logger.debug("Finished items count: " + items.size());
|
||||
logger.debug("Finished items: " + items);
|
||||
assertNotNull(items);
|
||||
results.addAll(items);
|
||||
}
|
||||
assertEquals(ITEM_COUNT, count);
|
||||
assertEquals(ITEM_COUNT, results.size());
|
||||
reader.close();
|
||||
}
|
||||
|
||||
private IbatisPagingItemReader<Foo> getItemReader() throws Exception {
|
||||
SqlMapClient sqlMapClient = createSqlMapClient();
|
||||
|
||||
IbatisPagingItemReader<Foo> reader = new IbatisPagingItemReader<Foo>();
|
||||
if ("postgres".equals(System.getProperty("ENVIRONMENT"))) {
|
||||
reader.setQueryId("getPagedFoosPostgres");
|
||||
} else if ("oracle".equals(System.getProperty("ENVIRONMENT"))) {
|
||||
reader.setQueryId("getPagedFoosOracle");
|
||||
} else {
|
||||
reader.setQueryId("getPagedFoos");
|
||||
}
|
||||
reader.setPageSize(2);
|
||||
reader.setSqlMapClient(sqlMapClient);
|
||||
reader.setSaveState(true);
|
||||
|
||||
reader.afterPropertiesSet();
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
private SqlMapClient createSqlMapClient() throws Exception {
|
||||
return SqlMapClientBuilder.buildSqlMapClient(new ClassPathResource("ibatis-config.xml", getClass()).getInputStream());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
|
||||
|
||||
<sqlMapConfig>
|
||||
<!-- lazy loading is enabled by default, but it is emphasized here - if it was set
|
||||
to false, input source would load all data into memory! -->
|
||||
<settings lazyLoadingEnabled="true"/>
|
||||
|
||||
<sqlMap resource="org/springframework/batch/item/database/ibatis-foo.xml" />
|
||||
|
||||
</sqlMapConfig>
|
||||
@@ -1,57 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
|
||||
|
||||
<sqlMap namespace="Foo">
|
||||
|
||||
<resultMap id="fooResult" class="org.springframework.batch.item.sample.Foo">
|
||||
<result property="id" column="ID" />
|
||||
<result property="name" column="NAME" />
|
||||
<result property="value" column="VALUE" />
|
||||
</resultMap>
|
||||
|
||||
<select id="getAllFooIds" resultClass="int">
|
||||
select ID from T_FOOS
|
||||
</select>
|
||||
|
||||
<select id="getFooById" parameterClass="int" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS where ID = #value#
|
||||
</select>
|
||||
|
||||
<select id="getAllFoos" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS
|
||||
</select>
|
||||
|
||||
<select id="getPagedFoos" resultMap="fooResult">
|
||||
select LIMIT #_skiprows# #_pagesize# ID, NAME, VALUE from T_FOOS order by ID
|
||||
</select>
|
||||
|
||||
<select id="getPagedFoosPostgres" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS order by ID LIMIT #_pagesize# OFFSET #_skiprows#
|
||||
</select>
|
||||
|
||||
<select id="getPagedFoosOracle" resultMap="fooResult">
|
||||
select * from (select ID, NAME, VALUE, ROWNUM as tmp from T_FOOS order by ID) where tmp > #_skiprows# and tmp<=(#_skiprows#+#_pagesize#)
|
||||
</select>
|
||||
|
||||
<select id="getAllFooIdsRestart" resultClass="int">
|
||||
select ID from T_FOOS where ID > #id#
|
||||
</select>
|
||||
|
||||
<select id="getNoFoos" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS where ID = -1
|
||||
</select>
|
||||
|
||||
<insert id="insertFoo" parameterClass="org.springframework.batch.item.sample.Foo">
|
||||
insert INTO T_WRITE_FOOS (ID, NAME, VALUE) VALUES (#id#, #name#, #value#)
|
||||
</insert>
|
||||
|
||||
<update id="updateFoo" parameterClass="org.springframework.batch.item.sample.Foo">
|
||||
update T_WRITE_FOOS set NAME = #name#, VALUE = #value# where ID = #id#
|
||||
</update>
|
||||
|
||||
<delete id="deleteFoo" parameterClass="org.springframework.batch.item.sample.Foo">
|
||||
delete from T_WRITE_FOOS where ID = #id#
|
||||
</delete>
|
||||
|
||||
</sqlMap>
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
import com.ibatis.sqlmap.client.SqlMapSession;
|
||||
import com.ibatis.sqlmap.engine.execution.BatchException;
|
||||
import com.ibatis.sqlmap.engine.execution.BatchResult;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
||||
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
|
||||
import org.springframework.jdbc.support.SQLExceptionTranslator;
|
||||
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link ItemWriter} that uses the batching features from
|
||||
* SqlMapClient to execute a batch of statements for all items
|
||||
* provided.<br>
|
||||
*
|
||||
* The user must provide an iBATIS statement id that points to the SQL statement defined
|
||||
* in the iBATIS SqlMap configuration.<br>
|
||||
*
|
||||
* It is expected that {@link #write(List)} is called inside a transaction.<br>
|
||||
*
|
||||
* The writer is thread-safe after its properties are set (normal singleton
|
||||
* behavior), so it can be used to write in multiple concurrent transactions.<br>
|
||||
*
|
||||
* <em>Note:</em> This reader was refactored as part of Spring Batch 3.0 to use the iBatis
|
||||
* APIs directly instead of using Spring's SqlMapClientTemplate as part of the upgrade to
|
||||
* support Spring 4.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Michael Minella
|
||||
* @since 2.0
|
||||
* @deprecated as of Spring Batch 3.0, in favor of the native Spring Batch support
|
||||
* in the MyBatis follow-up project (http://mybatis.github.io/spring/)
|
||||
*/
|
||||
@Deprecated
|
||||
public class IbatisBatchItemWriter<T> implements ItemWriter<T>, InitializingBean {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(IbatisBatchItemWriter.class);
|
||||
|
||||
private String statementId;
|
||||
|
||||
private boolean assertUpdates = true;
|
||||
|
||||
private SqlMapClient sqlMapClient;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the flag that determines whether an assertion is made
|
||||
* that all items cause at least one row to be updated.
|
||||
*
|
||||
* @param assertUpdates the flag to set. Defaults to true;
|
||||
*/
|
||||
public void setAssertUpdates(boolean assertUpdates) {
|
||||
this.assertUpdates = assertUpdates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for {@link SqlMapClient} for injection purposes.
|
||||
*
|
||||
* @param sqlMapClient the SqlMapClient
|
||||
*/
|
||||
public void setSqlMapClient(SqlMapClient sqlMapClient) {
|
||||
this.sqlMapClient = sqlMapClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the statement id identifying the statement in the SqlMap
|
||||
* configuration file.
|
||||
*
|
||||
* @param statementId the id for the statement
|
||||
*/
|
||||
public void setStatementId(String statementId) {
|
||||
this.statementId = statementId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check mandatory properties - there must be an SqlMapClient and a statementId.
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(sqlMapClient, "A SqlMapClient is required.");
|
||||
Assert.notNull(statementId, "A statementId is required.");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemWriter#write(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public void write(final List<? extends T> items) {
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing batch with " + items.size() + " items.");
|
||||
}
|
||||
|
||||
List<BatchResult> results = execute(items);
|
||||
|
||||
if (assertUpdates) {
|
||||
if (results.size() != 1) {
|
||||
throw new InvalidDataAccessResourceUsageException("Batch execution returned invalid results. " +
|
||||
"Expected 1 but number of BatchResult objects returned was " + results.size());
|
||||
}
|
||||
|
||||
int[] updateCounts = results.get(0).getUpdateCounts();
|
||||
|
||||
for (int i = 0; i < updateCounts.length; i++) {
|
||||
int value = updateCounts[i];
|
||||
if (value == 0) {
|
||||
throw new EmptyResultDataAccessException("Item " + i + " of " + updateCounts.length
|
||||
+ " did not update any rows: [" + items.get(i) + "]", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<BatchResult> execute(final List<? extends T> items) {
|
||||
// We always need to use a SqlMapSession, as we need to pass a Spring-managed
|
||||
// Connection (potentially transactional) in. This shouldn't be necessary if
|
||||
// we run against a TransactionAwareDataSourceProxy underneath, but unfortunately
|
||||
// we still need it to make iBATIS batch execution work properly: If iBATIS
|
||||
// doesn't recognize an existing transaction, it automatically executes the
|
||||
// batch for every single statement...
|
||||
|
||||
SqlMapSession session = this.sqlMapClient.openSession();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");
|
||||
}
|
||||
Connection ibatisCon = null;
|
||||
|
||||
try {
|
||||
Connection springCon = null;
|
||||
boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);
|
||||
|
||||
// Obtain JDBC Connection to operate on...
|
||||
try {
|
||||
ibatisCon = session.getCurrentConnection();
|
||||
if (ibatisCon == null) {
|
||||
springCon = (transactionAware ?
|
||||
dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
|
||||
session.setUserConnection(springCon);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
|
||||
}
|
||||
|
||||
// Execute given callback...
|
||||
try {
|
||||
session.startBatch();
|
||||
for (T item : items) {
|
||||
session.update(statementId, item);
|
||||
}
|
||||
try {
|
||||
return session.executeBatchDetailed();
|
||||
} catch (BatchException e) {
|
||||
throw e.getBatchUpdateException();
|
||||
}
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
SQLExceptionTranslator sqlStateSQLExceptionTranslator;
|
||||
|
||||
if(dataSource != null) {
|
||||
sqlStateSQLExceptionTranslator = new SQLStateSQLExceptionTranslator();
|
||||
} else {
|
||||
sqlStateSQLExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
|
||||
}
|
||||
|
||||
throw sqlStateSQLExceptionTranslator.translate("SqlMapClient operation", null, ex);
|
||||
}
|
||||
|
||||
// Processing finished - potentially session still to be closed.
|
||||
}
|
||||
finally {
|
||||
// Only close SqlMapSession if we know we've actually opened it
|
||||
// at the present level.
|
||||
if (ibatisCon == null) {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
import com.ibatis.sqlmap.client.SqlMapSession;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
||||
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
|
||||
import org.springframework.jdbc.support.SQLExceptionTranslator;
|
||||
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@link org.springframework.batch.item.ItemReader} for reading database
|
||||
* records using iBATIS in a paging fashion.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It executes the query specified as the {@link #setQueryId(String)} to
|
||||
* retrieve requested data. The query is executed using paged requests of a size
|
||||
* specified in {@link #setPageSize(int)}. Additional pages are requested when
|
||||
* needed as {@link #read()} method is called, returning an object corresponding
|
||||
* to current position. Some standard query parameters are provided by the
|
||||
* reader and the SQL in the named query must use some or all of these parameters
|
||||
* (depending on the SQL variant) to construct a result set of the required
|
||||
* size. The parameters are:</p>
|
||||
* <ul>
|
||||
* <li><code>_page</code>: the page number to be read (starting at 0)</li>
|
||||
* <li><code>_pagesize</code>: the size of the pages, i.e. the number of rows to
|
||||
* return</li>
|
||||
* <li><code>_skiprows</code>: the product of <code>_page</code> and
|
||||
* <code>_pagesize</code></li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Failure to write the correct platform-specific SQL often results in an
|
||||
* infinite loop in the reader because it keeps asking for the next page and
|
||||
* gets the same result set over and over.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The performance of the paging depends on the iBATIS implementation.
|
||||
* Setting a fairly large page size and using a commit interval that matches the
|
||||
* page size should provide better performance.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The implementation is thread-safe in between calls to
|
||||
* {@link #open(ExecutionContext)}, but remember to use
|
||||
* <code>saveState=false</code> if used in a multi-threaded client (no restart
|
||||
* available).
|
||||
* </p>
|
||||
*
|
||||
* <p><em>Note:</em> This reader was refactored as part of Spring Batch 3.0 to use the iBatis
|
||||
* APIs directly instead of using Spring's SqlMapClientTemplate as part of the upgrade to
|
||||
* support Spring 4.</p>
|
||||
|
||||
* @author Thomas Risberg
|
||||
* @author Dave Syer
|
||||
* @author Michael Minella
|
||||
* @since 2.0
|
||||
* @deprecated as of Spring Batch 3.0, in favor of the native Spring Batch support
|
||||
* in the MyBatis follow-up project (http://mybatis.github.io/spring/)
|
||||
*/
|
||||
@Deprecated
|
||||
public class IbatisPagingItemReader<T> extends AbstractPagingItemReader<T> {
|
||||
|
||||
private SqlMapClient sqlMapClient;
|
||||
|
||||
private String queryId;
|
||||
|
||||
private Map<String, Object> parameterValues;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
public IbatisPagingItemReader() {
|
||||
setName(ClassUtils.getShortName(IbatisPagingItemReader.class));
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public void setSqlMapClient(SqlMapClient sqlMapClient) {
|
||||
this.sqlMapClient = sqlMapClient;
|
||||
}
|
||||
|
||||
public void setQueryId(String queryId) {
|
||||
this.queryId = queryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parameter values to be used for the query execution.
|
||||
*
|
||||
* @param parameterValues the values keyed by the parameter named used in
|
||||
* the query string.
|
||||
*/
|
||||
public void setParameterValues(Map<String, Object> parameterValues) {
|
||||
this.parameterValues = parameterValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check mandatory properties.
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.afterPropertiesSet();
|
||||
Assert.notNull(sqlMapClient);
|
||||
Assert.notNull(queryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doReadPage() {
|
||||
Map<String, Object> parameters = new HashMap<String, Object>();
|
||||
if (parameterValues != null) {
|
||||
parameters.putAll(parameterValues);
|
||||
}
|
||||
parameters.put("_page", getPage());
|
||||
parameters.put("_pagesize", getPageSize());
|
||||
parameters.put("_skiprows", getPage() * getPageSize());
|
||||
if (results == null) {
|
||||
results = new CopyOnWriteArrayList<T>();
|
||||
}
|
||||
else {
|
||||
results.clear();
|
||||
}
|
||||
results.addAll(execute(parameters));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<T> execute(Map<String, Object> parameters) {
|
||||
// We always need to use a SqlMapSession, as we need to pass a Spring-managed
|
||||
// Connection (potentially transactional) in. This shouldn't be necessary if
|
||||
// we run against a TransactionAwareDataSourceProxy underneath, but unfortunately
|
||||
// we still need it to make iBATIS batch execution work properly: If iBATIS
|
||||
// doesn't recognize an existing transaction, it automatically executes the
|
||||
// batch for every single statement...
|
||||
|
||||
SqlMapSession session = this.sqlMapClient.openSession();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");
|
||||
}
|
||||
Connection ibatisCon = null;
|
||||
|
||||
try {
|
||||
Connection springCon = null;
|
||||
boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);
|
||||
|
||||
// Obtain JDBC Connection to operate on...
|
||||
try {
|
||||
ibatisCon = session.getCurrentConnection();
|
||||
if (ibatisCon == null) {
|
||||
springCon = (transactionAware ?
|
||||
dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
|
||||
session.setUserConnection(springCon);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
|
||||
}
|
||||
|
||||
// Execute given callback...
|
||||
try {
|
||||
return session.queryForList(queryId, parameters);
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
SQLExceptionTranslator sqlStateSQLExceptionTranslator;
|
||||
|
||||
if(dataSource != null) {
|
||||
sqlStateSQLExceptionTranslator = new SQLStateSQLExceptionTranslator();
|
||||
} else {
|
||||
sqlStateSQLExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
|
||||
}
|
||||
|
||||
throw sqlStateSQLExceptionTranslator.translate("SqlMapClient operation", null, ex);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (springCon != null) {
|
||||
if (transactionAware) {
|
||||
springCon.close();
|
||||
}
|
||||
else {
|
||||
DataSourceUtils.doReleaseConnection(springCon, dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Could not close JDBC Connection", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Processing finished - potentially session still to be closed.
|
||||
}
|
||||
finally {
|
||||
// Only close SqlMapSession if we know we've actually opened it
|
||||
// at the present level.
|
||||
if (ibatisCon == null) {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doJumpToPage(int itemIndex) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
import com.ibatis.sqlmap.client.SqlMapSession;
|
||||
import com.ibatis.sqlmap.engine.execution.BatchResult;
|
||||
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
* @author Will Schipp
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IbatisBatchItemWriterTests {
|
||||
|
||||
private IbatisBatchItemWriter<Foo> writer = new IbatisBatchItemWriter<Foo>();
|
||||
|
||||
private DataSource ds;
|
||||
|
||||
private SqlMapClient smc;
|
||||
|
||||
private String statementId = "updateFoo";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private class Foo {
|
||||
private Long id;
|
||||
private String bar;
|
||||
|
||||
public Foo(String bar) {
|
||||
this.id = 1L;
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getBar() {
|
||||
return bar;
|
||||
}
|
||||
|
||||
public void setBar(String bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Foo)) {
|
||||
return false;
|
||||
}
|
||||
Foo compare = (Foo) obj;
|
||||
if (this.bar.equals(compare.getBar())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return bar.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
smc = mock(SqlMapClient.class);
|
||||
ds = mock(DataSource.class);
|
||||
writer.setStatementId(statementId);
|
||||
writer.setSqlMapClient(smc);
|
||||
writer.setDataSource(ds);
|
||||
writer.afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for
|
||||
* {@link org.springframework.batch.item.database.JdbcBatchItemWriter#afterPropertiesSet()}
|
||||
* .
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testAfterPropertiesSet() throws Exception {
|
||||
writer = new IbatisBatchItemWriter<Foo>();
|
||||
try {
|
||||
writer.afterPropertiesSet();
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
String message = e.getMessage();
|
||||
assertTrue("Message does not contain 'SqlMapClient'.", message.indexOf("SqlMapClient") >= 0);
|
||||
}
|
||||
writer.setSqlMapClient(smc);
|
||||
try {
|
||||
writer.afterPropertiesSet();
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
String message = e.getMessage();
|
||||
assertTrue("Message does not contain 'statementId'.", message.indexOf("statementId") >= 0);
|
||||
}
|
||||
writer.setStatementId("statementId");
|
||||
writer.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndFlush() throws Exception {
|
||||
SqlMapSession sms = mock(SqlMapSession.class);
|
||||
when(smc.openSession()).thenReturn(sms);
|
||||
sms.close();
|
||||
when(sms.getCurrentConnection()).thenReturn(null);
|
||||
sms.setUserConnection(null);
|
||||
sms.startBatch();
|
||||
when(sms.update("updateFoo", new Foo("bar"))).thenReturn(-2);
|
||||
List<BatchResult> results = Collections.singletonList(new BatchResult("updateFoo", "update foo"));
|
||||
results.get(0).setUpdateCounts(new int[] {1});
|
||||
when(sms.executeBatchDetailed()).thenReturn(results);
|
||||
writer.write(Collections.singletonList(new Foo("bar")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndFlushWithEmptyUpdate() throws Exception {
|
||||
SqlMapSession sms = mock(SqlMapSession.class);
|
||||
when(smc.openSession()).thenReturn(sms);
|
||||
sms.close();
|
||||
when(sms.getCurrentConnection()).thenReturn(null);
|
||||
sms.setUserConnection(null);
|
||||
sms.startBatch();
|
||||
when(sms.update("updateFoo", new Foo("bar"))).thenReturn(1);
|
||||
List<BatchResult> results = Collections.singletonList(new BatchResult("updateFoo", "update foo"));
|
||||
results.get(0).setUpdateCounts(new int[] {0});
|
||||
when(sms.executeBatchDetailed()).thenReturn(results);
|
||||
try {
|
||||
writer.write(Collections.singletonList(new Foo("bar")));
|
||||
fail("Expected EmptyResultDataAccessException");
|
||||
}
|
||||
catch (EmptyResultDataAccessException e) {
|
||||
// expected
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.indexOf("did not update") >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndFlushWithFailure() throws Exception {
|
||||
final RuntimeException ex = new RuntimeException("ERROR");
|
||||
SqlMapSession sms = mock(SqlMapSession.class);
|
||||
when(smc.openSession()).thenReturn(sms);
|
||||
sms.close();
|
||||
when(sms.getCurrentConnection()).thenReturn(null);
|
||||
sms.setUserConnection(null);
|
||||
sms.startBatch();
|
||||
when(sms.update("updateFoo", new Foo("bar"))).thenThrow(ex);
|
||||
try {
|
||||
writer.write(Collections.singletonList(new Foo("bar")));
|
||||
fail("Expected RuntimeException");
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
assertEquals("ERROR", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.item.sample.Foo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = "JdbcPagingItemReaderCommonTests-context.xml")
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IbatisPagingItemReaderAsyncTests {
|
||||
|
||||
/**
|
||||
* The number of items to read
|
||||
*/
|
||||
private static final int ITEM_COUNT = 10;
|
||||
|
||||
/**
|
||||
* The number of threads to create
|
||||
*/
|
||||
private static final int THREAD_COUNT = 3;
|
||||
|
||||
private static Log logger = LogFactory.getLog(IbatisPagingItemReaderAsyncTests.class);
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
private int maxId;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
maxId = jdbcTemplate.queryForObject("SELECT MAX(ID) from T_FOOS", Integer.class);
|
||||
for (int i = maxId + 1; i <= ITEM_COUNT; i++) {
|
||||
jdbcTemplate.update("INSERT into T_FOOS (ID,NAME,VALUE) values (?, ?, ?)", i, "foo" + i, i);
|
||||
}
|
||||
assertEquals(ITEM_COUNT, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
jdbcTemplate.update("DELETE from T_FOOS where ID>?", maxId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsyncReader() throws Throwable {
|
||||
List<Throwable> throwables = new ArrayList<Throwable>();
|
||||
int max = 10;
|
||||
for (int i = 0; i < max; i++) {
|
||||
try {
|
||||
doTest();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throwables.add(e);
|
||||
}
|
||||
}
|
||||
if (!throwables.isEmpty()) {
|
||||
throw new IllegalStateException(String.format("Failed %d out of %d", throwables.size(), max), throwables
|
||||
.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws InterruptedException
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
private void doTest() throws Exception, InterruptedException, ExecutionException {
|
||||
final IbatisPagingItemReader<Foo> reader = getItemReader();
|
||||
CompletionService<List<Foo>> completionService = new ExecutorCompletionService<List<Foo>>(Executors
|
||||
.newFixedThreadPool(THREAD_COUNT));
|
||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||
completionService.submit(new Callable<List<Foo>>() {
|
||||
@Override
|
||||
public List<Foo> call() throws Exception {
|
||||
List<Foo> list = new ArrayList<Foo>();
|
||||
Foo next = null;
|
||||
do {
|
||||
next = reader.read();
|
||||
Thread.sleep(10L); // try to make it fairer
|
||||
logger.debug("Reading item: " + next);
|
||||
if (next != null) {
|
||||
list.add(next);
|
||||
}
|
||||
} while (next != null);
|
||||
return list;
|
||||
}
|
||||
});
|
||||
}
|
||||
int count = 0;
|
||||
Set<Foo> results = new HashSet<Foo>();
|
||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||
List<Foo> items = completionService.take().get();
|
||||
count += items.size();
|
||||
logger.debug("Finished items count: " + items.size());
|
||||
logger.debug("Finished items: " + items);
|
||||
assertNotNull(items);
|
||||
results.addAll(items);
|
||||
}
|
||||
assertEquals(ITEM_COUNT, count);
|
||||
assertEquals(ITEM_COUNT, results.size());
|
||||
reader.close();
|
||||
}
|
||||
|
||||
private IbatisPagingItemReader<Foo> getItemReader() throws Exception {
|
||||
SqlMapClient sqlMapClient = createSqlMapClient();
|
||||
|
||||
IbatisPagingItemReader<Foo> reader = new IbatisPagingItemReader<Foo>();
|
||||
reader.setQueryId("getPagedFoos");
|
||||
reader.setPageSize(2);
|
||||
reader.setSqlMapClient(sqlMapClient);
|
||||
reader.setSaveState(true);
|
||||
reader.setDataSource(dataSource);
|
||||
|
||||
reader.afterPropertiesSet();
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
private SqlMapClient createSqlMapClient() throws Exception {
|
||||
return SqlMapClientBuilder.buildSqlMapClient(new ClassPathResource("ibatis-config.xml", getClass()).getInputStream());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.sample.Foo;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IbatisPagingItemReaderCommonTests extends AbstractDatabaseItemStreamItemReaderTests {
|
||||
|
||||
@Override
|
||||
protected ItemReader<Foo> getItemReader() throws Exception {
|
||||
SqlMapClient sqlMapClient = createSqlMapClient();
|
||||
|
||||
IbatisPagingItemReader<Foo> reader = new IbatisPagingItemReader<Foo>();
|
||||
reader.setQueryId("getPagedFoos");
|
||||
reader.setPageSize(2);
|
||||
reader.setSqlMapClient(sqlMapClient);
|
||||
reader.setDataSource(getDataSource());
|
||||
reader.setSaveState(true);
|
||||
|
||||
reader.afterPropertiesSet();
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
private SqlMapClient createSqlMapClient() throws Exception {
|
||||
return SqlMapClientBuilder.buildSqlMapClient(new ClassPathResource("ibatis-config.xml", getClass()).getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pointToEmptyInput(ItemReader<Foo> tested) throws Exception {
|
||||
IbatisPagingItemReader<Foo> reader = (IbatisPagingItemReader<Foo>) tested;
|
||||
reader.close();
|
||||
|
||||
reader.setQueryId("getNoFoos");
|
||||
reader.afterPropertiesSet();
|
||||
|
||||
reader.open(new ExecutionContext());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.item.sample.Foo;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = "/org/springframework/batch/item/database/data-source-context.xml")
|
||||
public class IbatisPagingItemReaderParameterTests extends AbstractPagingItemReaderParameterTests {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected AbstractPagingItemReader<Foo> getItemReader() throws Exception {
|
||||
SqlMapClient sqlMapClient = createSqlMapClient();
|
||||
|
||||
IbatisPagingItemReader<Foo> reader = new IbatisPagingItemReader<Foo>();
|
||||
reader.setQueryId("getPagedFoosLimitAndUp");
|
||||
reader.setParameterValues(Collections.<String, Object>singletonMap("limit", 2));
|
||||
reader.setSqlMapClient(sqlMapClient);
|
||||
reader.setDataSource(dataSource);
|
||||
reader.setSaveState(true);
|
||||
|
||||
reader.afterPropertiesSet();
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
private SqlMapClient createSqlMapClient() throws Exception {
|
||||
SqlMapClient client = SqlMapClientBuilder.buildSqlMapClient(new ClassPathResource("ibatis-config.xml", getClass()).getInputStream());
|
||||
return client;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
|
||||
|
||||
<sqlMapConfig>
|
||||
<!-- lazy loading is enabled by default, but it is emphasized here - if it was set
|
||||
to false, input source would load all data into memory! -->
|
||||
<settings lazyLoadingEnabled="true"/>
|
||||
|
||||
<sqlMap resource="org/springframework/batch/item/database/ibatis-foo.xml" />
|
||||
|
||||
</sqlMapConfig>
|
||||
@@ -1,57 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
|
||||
|
||||
<sqlMap namespace="Foo">
|
||||
|
||||
<resultMap id="fooResult" class="org.springframework.batch.item.sample.Foo">
|
||||
<result property="id" column="ID" />
|
||||
<result property="name" column="NAME" />
|
||||
<result property="value" column="VALUE" />
|
||||
</resultMap>
|
||||
|
||||
<select id="getAllFooIds" resultClass="int">
|
||||
select ID from T_FOOS
|
||||
</select>
|
||||
|
||||
<select id="getFooById" parameterClass="int" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS where ID = #value#
|
||||
</select>
|
||||
|
||||
<select id="getAllFoos" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS
|
||||
</select>
|
||||
|
||||
<select id="getPagedFoos" resultMap="fooResult">
|
||||
select LIMIT #_skiprows# #_pagesize# ID, NAME, VALUE from T_FOOS order by ID
|
||||
</select>
|
||||
|
||||
<select id="getPagedFoosLimitAndUp" resultMap="fooResult">
|
||||
select LIMIT #_skiprows# #_pagesize# ID, NAME, VALUE from T_FOOS where VALUE >= #limit# order by ID
|
||||
</select>
|
||||
|
||||
<select id="getFoosLimitAndUp" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS where VALUE >= #limit#
|
||||
</select>
|
||||
|
||||
<select id="getAllFooIdsRestart" resultClass="int">
|
||||
select ID from T_FOOS where ID > #id#
|
||||
</select>
|
||||
|
||||
<select id="getNoFoos" resultMap="fooResult">
|
||||
select ID, NAME, VALUE from T_FOOS where ID = -1
|
||||
</select>
|
||||
|
||||
<insert id="insertFoo" parameterClass="org.springframework.batch.item.sample.Foo">
|
||||
insert INTO T_WRITE_FOOS (ID, NAME, VALUE) VALUES (#id#, #name#, #value#)
|
||||
</insert>
|
||||
|
||||
<update id="updateFoo" parameterClass="org.springframework.batch.item.sample.Foo">
|
||||
update T_WRITE_FOOS set NAME = #name#, VALUE = #value# where ID = #id#
|
||||
</update>
|
||||
|
||||
<delete id="deleteFoo" parameterClass="org.springframework.batch.item.sample.Foo">
|
||||
delete from T_WRITE_FOOS where ID = #id#
|
||||
</delete>
|
||||
|
||||
</sqlMap>
|
||||
@@ -43,7 +43,6 @@
|
||||
<config>src/main/resources/jobs/iosample/hibernate.xml</config>
|
||||
<config>src/main/resources/jobs/hibernateJob.xml</config>
|
||||
<config>src/main/resources/jobs/iosample/jpa.xml</config>
|
||||
<config>src/main/resources/jobs/iosample/ibatis.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/sample/domain/trade/internal/JdbcCustomerDebitDaoTests-context.xml</config>
|
||||
<config>src/main/resources/jobs/loopFlowSample.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/sample/common/StagingItemReaderTests-context.xml</config>
|
||||
@@ -429,19 +428,6 @@
|
||||
<profiles>
|
||||
</profiles>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[ibatis]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
<config>src/main/resources/data-source-context.xml</config>
|
||||
<config>src/main/resources/simple-job-launcher-context.xml</config>
|
||||
<config>src/main/resources/org/springframework/batch/sample/config/common-context.xml</config>
|
||||
<config>src/main/resources/jobs/iosample/ibatis.xml</config>
|
||||
</configs>
|
||||
<profiles>
|
||||
</profiles>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[partition]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
|
||||
@@ -51,7 +51,6 @@ Job/Feature | delimited input | fixed-length input | xml input |
|
||||
:-------------------------- | :-------------: | :----------------: | :-------: | :-------------: | :-------------: | :--------------: | :-----------------: | :--------: | :-------: | :------------: | :--------: | :----------:
|
||||
delimited | x | | | | | | | x | | | |
|
||||
[fixedLength](#fixedLength) | | x | | | | | | | x | | |
|
||||
[ibatis](#ibatis) | | | | | x | | | | | | x |
|
||||
[hibernate](#hibernate) | | | | | x | | | | | | x |
|
||||
[jdbcCursor](#jdbcCursor) | | | | | x | | | | | | x |
|
||||
jpa | | | | x | | | | | | | x |
|
||||
@@ -521,12 +520,6 @@ The output reliability and robustness are improved by the use of
|
||||
need to take control of it so that the skip and retry features
|
||||
provided by Spring Batch can work effectively.
|
||||
|
||||
### [Ibatis Sample](id:ibatis)
|
||||
|
||||
The goal of this sample is to show the use of Ibatis as a query
|
||||
mapping tool. Its features are similar to the Hibernate sample, but
|
||||
it uses Ibatis to drive its input and output.
|
||||
|
||||
### [Infinite Loop Sample](id:infiniteLoop)
|
||||
|
||||
This sample has a single step that is an infinite loop, reading and
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
|
||||
|
||||
<sqlMapConfig>
|
||||
<sqlMap resource="ibatis-customer-credit.xml"/>
|
||||
</sqlMapConfig>
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
|
||||
|
||||
<sqlMap namespace="Customer">
|
||||
<resultMap id="result" class="org.springframework.batch.sample.domain.trade.CustomerCredit">
|
||||
<result property="name" column="NAME" />
|
||||
<result property="credit" column="CREDIT" />
|
||||
</resultMap>
|
||||
|
||||
<select id="getAllCustomerCreditIds" resultClass="int">
|
||||
select ID from CUSTOMER
|
||||
</select>
|
||||
|
||||
<select id="getAllCustomerCredits" resultMap="result">
|
||||
select ID, NAME, CREDIT from CUSTOMER
|
||||
</select>
|
||||
|
||||
<select id="getCustomerCreditById" parameterClass="int" resultMap="result">
|
||||
select NAME, CREDIT from CUSTOMER where ID = #value#
|
||||
</select>
|
||||
|
||||
<update id="updateCredit" parameterClass="org.springframework.batch.sample.domain.trade.CustomerCredit" >
|
||||
update CUSTOMER set CREDIT = #credit# where NAME = #name#
|
||||
</update>
|
||||
</sqlMap>
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="itemReader"
|
||||
class="org.springframework.batch.item.database.IbatisPagingItemReader">
|
||||
<property name="queryId" value="getAllCustomerCredits" />
|
||||
<property name="sqlMapClient" ref="sqlMapClient" />
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
<bean id="itemWriter"
|
||||
class="org.springframework.batch.item.database.IbatisBatchItemWriter">
|
||||
<property name="statementId" value="updateCredit" />
|
||||
<property name="sqlMapClient" ref="sqlMapClient" />
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sqlMapClient" class="com.ibatis.sqlmap.client.SqlMapClientBuilder" factory-method="buildSqlMapClient">
|
||||
<constructor-arg value="ibatis-config.xml"/>
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample.iosample;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.sample.domain.trade.CustomerCredit;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = "/jobs/iosample/ibatis.xml")
|
||||
public class IbatisFunctionalTests extends AbstractIoSampleTests {
|
||||
|
||||
@Override
|
||||
protected void pointReaderToOutput(ItemReader<CustomerCredit> reader) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,15 +73,6 @@
|
||||
<entry align="left">Reads from a paginated HQL query</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">IbatisPagingItemReader</entry>
|
||||
|
||||
<entry align="left">Reads via iBATIS based on a query. Pages
|
||||
through the rows so that large datasets can be read without
|
||||
running out of memory. See HOWTO - Read from a Database. This
|
||||
ItemReader is now deprecated as of Spring Batch 3.0.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">ItemReaderAdapter</entry>
|
||||
|
||||
@@ -236,13 +227,6 @@
|
||||
to another item writer to do the actual writing.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">IbatisBatchItemWriter</entry>
|
||||
|
||||
<entry align="left">Writes items in a batch using the iBatis API's
|
||||
directly. This ItemWriter is deprecated as of Spring Batch 3.0.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">ItemWriterAdapter</entry>
|
||||
|
||||
|
||||
@@ -2270,53 +2270,6 @@ itemReader.close(executionContext);</programlisting>
|
||||
entities read from the database for each query execution.</para>
|
||||
</section>
|
||||
|
||||
<section id="IbatisPagingItemReader">
|
||||
<title>IbatisPagingItemReader</title>
|
||||
|
||||
<note>This reader is deprecated as of Spring Batch 3.0.</note>
|
||||
|
||||
<para>If you use IBATIS for your data access then you can use the
|
||||
<classname>IbatisPagingItemReader</classname> which, as the name
|
||||
indicates, is an implementation of a paging
|
||||
<classname>ItemReader</classname>. IBATIS doesn't have direct support
|
||||
for reading rows in pages but by providing a couple of standard
|
||||
variables you can add paging support to your IBATIS queries.</para>
|
||||
|
||||
<para>Here is an example of a configuration for a
|
||||
<classname>IbatisPagingItemReader</classname> reading CustomerCredits
|
||||
as in the examples above:</para>
|
||||
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.spr...IbatisPagingItemReader">
|
||||
<property name="sqlMapClient" ref="sqlMapClient"/>
|
||||
<property name="queryId" value="getPagedCustomerCredits"/>
|
||||
<property name="pageSize" value="1000"/>
|
||||
</bean></programlisting>
|
||||
|
||||
<para>The <classname>IbatisPagingItemReader</classname> configuration
|
||||
above references an IBATIS query called "getPagedCustomerCredits".
|
||||
Here is an example of what that query should look like for
|
||||
MySQL.</para>
|
||||
|
||||
<programlisting language="xml"><select id="getPagedCustomerCredits" resultMap="customerCreditResult">
|
||||
select id, name, credit from customer order by id asc LIMIT #_skiprows#, #_pagesize#
|
||||
</select></programlisting>
|
||||
|
||||
<para>The <classname>_skiprows</classname> and
|
||||
<classname>_pagesize</classname> variables are provided by the
|
||||
<classname>IbatisPagingItemReader</classname> and there is also a
|
||||
<classname>_page</classname> variable that can be used if necessary.
|
||||
The syntax for the paging queries varies with the database used. Here
|
||||
is an example for Oracle (unfortunately we need to use CDATA for some
|
||||
operators since this belongs in an XML document):</para>
|
||||
|
||||
<programlisting language="xml"><select id="getPagedCustomerCredits" resultMap="customerCreditResult">
|
||||
select * from (
|
||||
select * from (
|
||||
select t.id, t.name, t.credit, ROWNUM ROWNUM_ from customer t order by id
|
||||
)) where ROWNUM_ <![CDATA[ > ]]> ( #_page# * #_pagesize# )
|
||||
) where ROWNUM <![CDATA[ <= ]]> #_pagesize#
|
||||
</select></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="databaseItemWriters">
|
||||
|
||||
Reference in New Issue
Block a user