I am looking for a guidance/solution with Spring batch integration. I have a directory to which external application will send xml files. My application should read the file content and move the file to another directory.
The application should be able to process the files in parallel.
Thanks in advance.
You can use Spring Integration ftp / sftp combined with Spring Batch:
1.Spring Integration Ftp Configuration :
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${host.name}" />
<property name="port" value="${host.port}" />
<property name="username" value="${host.username}" />
<property name="password" value="${host.password}" />
<property name="bufferSize" value="100000"/>
</bean>
<int:channel id="ftpChannel" />
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel" remote-directory="/yourremotedirectory/" session-factory="ftpClientFactory" use-temporary-file-name="false" />
2.Create your reader and autowire a service to provide your items if needed :
#Scope("step")
public class MajorItemReader implements InitializingBean{
private List<YourItem> yourItems= null;
#Autowired
private MyService provider;
public YourItem read() {
if ((yourItems!= null) && (yourItems.size() != 0)) {
return yourItems.remove(0);
}
return null;
}
//Reading Items from Service
private void reloadItems() {
this.yourItems= new ArrayList<YourItem>();
// use the service to provide your Items
if (yourItems.isEmpty()) {
yourItems= null;
}
}
public MyService getProvider() {
return provider;
}
public void setProvider(MyService provider) {
this.provider = provider;
}
#Override
public void afterPropertiesSet() throws Exception {
reloadItems();
}
}
3. Create Your Own Item Processor
public class MyProcessor implements
ItemProcessor<YourItem, YourItem> {
#Override
public YourItem process(YourItem arg0) throws Exception {
// Apply any logic to your Item before transferring it to the writer
return arg0;
}
}
4. Create Your Own Writer :
public class MyWriter{
#Autowired
#Qualifier("ftpChannel")
private MessageChannel messageChannel;
public void write(YourItem pack) throws IOException {
//create your file and from your Item
File file = new File("the_created_file");
// Sending the file via Spring Integration Ftp Channel
Message<File> message = MessageBuilder.withPayload(file).build();
messageChannel.send(message);
}
5.Batch Configuration :
<bean id="dataSourcee"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="" />
<property name="url" value="" />
<property name="username" value="" />
<property name="password" value="" />
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSourcee" />
<property name="transactionManager" ref="transactionManagerrr" />
<property name="databaseType" value="" />
</bean>
<bean id="transactionManagerrr"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
6.Another ApplicationContext file to Configure Your Job :
<context:annotation-config />
<bean id="provider" class="mypackage.MyService" />
<context:component-scan base-package="mypackage" />
<bean id="myReader" class="mypackage.MyReader"
<property name="provider" ref="provider" />
</bean>
<bean id="myWriter" class="mypackage.MyWriter" />
<bean id="myProcessor" class="mypackage.MyProcessor" />
<bean id="mReader"
class="org.springframework.batch.item.adapter.ItemReaderAdapter">
<property name="targetObject" ref="myReader" />
<property name="targetMethod" value="read" />
</bean>
<bean id="mProcessor"
class="org.springframework.batch.item.adapter.ItemProcessorAdapter">
<property name="targetObject" ref="myProcessor" />
<property name="targetMethod" value="process" />
</bean>
<bean id="mWriter"
class="org.springframework.batch.item.adapter.ItemWriterAdapter">
<property name="targetObject" ref="myWriter" />
<property name="targetMethod" value="write" />
</bean>
<batch:job id="myJob">
<batch:step id="step01">
<batch:tasklet>
<batch:chunk reader="mReader" writer="mWriter"
processor="mProcessor" commit-interval="1">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="myRunScheduler" class="mypackage.MyJobLauncher" />
<task:scheduled-tasks>
<task:scheduled ref="myJobLauncher" method="run"
cron="0 0/5 * * * ?" />
<!-- this will maker the job runs every 5 minutes -->
</task:scheduled-tasks>
7.Finally Configure A launcher to launch your job :
public class MyJobLauncher {
#Autowired
private JobLauncher jobLauncher;
#Autowired
#Qualifier("myJob")
private Job job;
public void run() {
try {
String dateParam = new Date().toString();
JobParameters param = new JobParametersBuilder().addString("date",
dateParam).toJobParameters();
JobExecution execution = jobLauncher.run(job, param);
execution.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
Related
How can i handle exceptions from Spring context .
Consider the context
<bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource">
<property name="URL" value="jdbc:oracle:thin:#localhost:1521/xe" />
<property name="user" value="abc" />
<property name="password" value="abc" />
</bean>
<bean id="abc" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="default" />
<property name="persistenceXmlLocation" value="classpath*:META-INF/abc-persistence.xml" />
</bean>
And am loading context in main method
public static void main(String[] args) {
ApplicationContext applicationContext = null;
try {
applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/test.xml");
} catch (Exception e) {
System.out.println("Exception caugtht " + e);
}
}
Lets say database is down .now when i load context exceptions are not caught in catch block. Is there any way to handle this ?
I've been trying to deploy my project on my tomcat server but apparently, every time I try to start the server, I get this error in my catalina logs:
Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'mnpTranslationServiceImpl' defined in URL [file:/opt/tomcat/webapps/axis2/WEB-INF/springjdbc.xml]:
Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'mnpTranslationDAO' of bean class [com.e_horizon.jdbc.mnpTranslation.mnpTranslationServiceImpl]:
Bean property 'mnpTranslationDAO' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
here is my bean.xml which i named springjdbc.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://192.168.XX.X1:3306/msdp" />
<property name="user" value="root" />
<property name="password" value="ehorizon" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="500" />
<property name="numHelperThreads" value="5" />
<property name="initialPoolSize" value="2" />
<property name="autoCommitOnClose" value="true" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="maxIdleTime" value="1200" />
<property name="acquireRetryAttempts" value="2" />
</bean>
<bean id="dataSourceHD" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://192.168.XX.X2:3306/hd" />
<property name="user" value="teligent" />
<property name="password" value="teligent" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="500" />
<property name="numHelperThreads" value="5" />
<property name="initialPoolSize" value="2" />
<property name="autoCommitOnClose" value="true" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="maxIdleTime" value="1200" />
<property name="acquireRetryAttempts" value="2" />
</bean>
<bean id="mpp2SubscribersDAO" class="com.e_horizon.jdbc.mpp2Subscriber.Mpp2SubscribersDAO">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="mpp2SubscribersService"
class="com.e_horizon.jdbc.mpp2Subscriber.Mpp2SubscribersServiceImpl">
<property name="mpp2SubscribersDAO">
<ref bean="mpp2SubscribersDAO" />
</property>
</bean>
<bean id="mnpTranslationDAO" class="com.e_horizon.jdbc.mnpTranslation.mnpTranslationDAO">
<property name="dataSource">
<ref bean="dataSourceHD" />
</property>
</bean>
<bean id="mnpTranslationServiceImpl" class="com.e_horizon.jdbc.mnpTranslation.mnpTranslationServiceImpl">
<property name="mnpTranslationDAO">
<ref bean="mnpTranslationDAO" />
</property>
</bean>
`
Here's the DAO file which the error says as not writable:
package com.e_horizon.jdbc.mnpTranslation;
import java.util.HashMap;
import java.util.Map;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import com.e_horizon.www.jdbc.common.BaseDAO;
public class mnpTranslationDAO extends BaseDAO {
private SimpleJdbcInsert jdbcCall;
public static String TABLE = "MNP_TRANSLATION";
public static String FIELD_MSISDN = "msisdn";
public static String FIELD_ROUTING_NUMBER = "routing_number";
public static String FIELD_LAST_UPDATE = "last_update";
public static String FIELD_ACTION = "action";
protected RowMapper getObjectMapper () {
return new mnpTranslationMapper();
}
public void save (mnpTranslation mnp) {
this.jdbcCall = this.getSimpleJdbcInsert().withTableName(TABLE);
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put(FIELD_MSISDN, mnp.getMsisdn());
parameters.put(FIELD_ROUTING_NUMBER, mnp.getRouting_number());
parameters.put(FIELD_LAST_UPDATE, mnp.getLast_update());
parameters.put(FIELD_ACTION, mnp.getAction());
jdbcCall.execute(parameters);
}
}
Adding my model which contains the setters and getters (mnpTranslation.java):
package com.e_horizon.jdbc.mnpTranslation;
import java.sql.Timestamp;
public class mnpTranslation {
private String msisdn;
private String routing_number;
private Timestamp last_update;
private String action;
public void setMsisdn (String msisdn) {
this.msisdn = msisdn;
}
public String getMsisdn () {
return this.msisdn;
}
public void setRouting_number (String routing_number) {
this.routing_number = routing_number;
}
public String getRouting_number () {
return this.routing_number;
}
public void setLast_update (Timestamp last_update) {
this.last_update = last_update;
}
public Timestamp getLast_update () {
return this.last_update;
}
public void setAction (String action) {
this.action = action;
}
public String getAction () {
return this.action;
}
}
[edit] I'm adding the rest of my java files so you could see more what I'm dealing with right now. Thanks a lot for checking this.
mnpTranslationService.java:
package com.e_horizon.jdbc.mnpTranslation;
public abstract interface mnpTranslationService {
public abstract void save (mnpTranslation mnp);
}
mnpTranslationServiceImpl.java:
package com.e_horizon.jdbc.mnpTranslation;
public class mnpTranslationServiceImpl {
private mnpTranslationDAO mnpTranslationDAO;
public void save (mnpTranslation mnp) {
this.mnpTranslationDAO.save(mnp);
}
}
mnpTranslationMapper.java:
package com.e_horizon.jdbc.mnpTranslation;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class mnpTranslationMapper implements RowMapper {
public Object mapRow (ResultSet result, int dex) throws SQLException {
mnpTranslation mnp = new mnpTranslation();
mnp.setMsisdn(result.getString("msisdn"));
mnp.setRouting_number(result.getString("routing_number"));
mnp.setLast_update(result.getTimestamp("last_update"));
mnp.setAction(result.getString("action"));
return mnp;
}
}
Please let me know if there's anything else you need to see. I'd gladly post it right away. Any help will be much appreciated.
The error message states that the problem is with your mnpTranslationServiceImpl class.
See error message
Bean property 'mnpTranslationDAO' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
I am facing problem in multi data source connection with spring, one of the transaction is working, but other one is not working. please help.
***********Data Source 1 start************
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="database1.ds" />
<property name="persistenceUnitName" value="database1" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="persistenceXmlLocation" value="classpath:jpa-persistence.xml" />
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="ORACLE" />
<property name="showSql" value="false" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />
***********Data Source 1 end************
***********Data Source 2 start************
<bean id="entityManagerFactory2"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="database2.ds" />
<property name="persistenceUnitName" value="database2" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter2" />
<property name="persistenceXmlLocation" value="classpath:jpa-persistence.xml" />
</bean>
<bean id="jpaVendorAdapter2
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="ORACLE" />
<property name="showSql" value="false" />
</bean>
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory2" />
***********Data Source 2 end************
<context:component-scan base-package="com.wmi.lakshya" annotation-config="true" scope-resolver="org.springframework.context.annotation.Jsr330ScopeMetadataResolver"/>
<tx:annotation-driven />
<cache:annotation-driven />
Entity Manager class
#Repository
#Scope("singleton")
public class EntityManagerImpl implements EntityManagerGenerator {
#PersistenceContext(unitName = "database1",type=PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#PersistenceContext(unitName = "database2",type=PersistenceContextType.EXTENDED)
private EntityManager entityManager2;
#Override
public EntityManager getEntityManager() {
try {
if (isUser1()) {
return entityManager;
}
return entityManager2;
} catch (Exception e) {
return entityManager;
}
}
}
#Transactional(value ="transactionManager")
public Query createNativeQuery(String sqlString) {
return entityManager.createNativeQuery(sqlString);
}
#Transactional(value ="transactionManager2")
public Query createNativeQuery1(String sqlString) {
return entityManager1.createNativeQuery(sqlString);
}
javax.persistence.TransactionRequiredException: Executing an
update/delete query
#Component
#Scope("prototype")
public class UserDAO extends AbstractDAO<User> {
/**
*
*/
private static final Logger LOGGER = Logger.getLogger(UserDAO.class);
/**
*
*/
protected UserDAO() {
super(User.class);
}
public void resetFailAttempts(String userName){
// em is entity manager class
em.createNativeQuery("update OMS_USER_MASTER set fail_attempts = 0 where login_id = :loginId ")
.setParameter(QueryParameters.LOGIN_ID, userName)
.executeUpdate();
}
}
Datasource is switched based on the condition, when connecting with datasource 1 is working, but not for datasource2.
I have two transaction managers defined in my context file as follows
<tx:annotation-driven transaction-manager="firstTransactionManager" />
<bean id="secondDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="---" />
<property name="url" value="datasource1" />
<property name="username" value="----" />
<property name="password" value="----" />
</bean>
<bean id="firstTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="firstDataSource" />
<qualifier value="firstTxManager"/>
</bean>
<bean id="secondTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="secondDataSource" />
<qualifier value="secondTxManager"/>
</bean>
<bean id="firstDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="---" />
<property name="url" value="datasource2" />
<property name="username" value="---" />
<property name="password" value="---" />
</bean>
And I my class definitions are as follows
#Transactional("firstTransactionManager")
public class JdbcMain {
#Autowired
private static DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public static void main(String args[]){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
TransactionExample example = (TransactionExample) ctx.getBean("transExample");
example.create();
}
}
And my example class is as follows:
#Transactional("secondTransactionManager")
public void create(DataSource dataSource2) {
try {
this.jdbcTemplate = new JdbcTemplate(dataSource2);
String sql = "insert into testtable values (?,?)";
getJdbcTemplate().update(sql,1,"1244343");
String marksSql="insert into testtable values (?,?)";
int i=2/0; //added to depict roll back behaviour of the transaction when exception occurs
getJdbcTemplate().update(marksSql,2,"sujay");
System.out.println("transaction committed");
} catch (RuntimeException e) {
throw e;
}
}
But the second transaction manager doesn't seem to work and the transaction is not rolled back (The first insert is executed). Can you provide me an idea.
I'm using spring batch to perform some calcul, in a reader I have to get a large data to be treated in a processor / writer, and this process takes a lot of (RAM).
So I tried to split the step using the partitioner like below :
<batch:step id="MyStep.master" >
<partition step="MyStep" partitioner="MyPartitioner">
<handler grid-size="1" task-executor="TaskExecutor" />
</partition>
</batch:step>
<batch:step id="MyStep" >
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="MyReader" processor="MyProcessor"
writer="MyWriter" commit-interval="1000" skip-limit="1000">
<batch:skippable-exception-classes>
<batch:include class="...FunctionalException" />
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>
<bean id="MyPartitioner" class="...MyPartitioner" scope="step"/>
<bean id="TaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
<bean name="MyReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
SELECT...
]]>
</value>
</property>
<property name="rowMapper" ref="MyRowMapper" />
</bean>
<bean id="MyRowMapper" class="...MyRowMapper" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver"/>
<property name="jdbcUrl" value="jdbc:postgresql://${database.host}/${database.name}"/>
<property name="user" value="${database.user}"/>
<property name="password" value="${database.password}"/>
<property name="acquireIncrement" value="1" />
<property name="autoCommitOnClose" value="true" />
<property name="minPoolSize" value="${min.pool.size}" /> <!-- min.pool.size=5 -->
<property name="maxPoolSize" value="${max.pool.size}" /> <!-- max.pool.size=15 -->
</bean>
But in vain the partitioning takes a lot of memory too, because the steps (slaves) are executed in parallel, what I want to do is to split the step and execute the thread successively (not in parallel) to reduce the memory usage (RAM), is that possible?
The question it's a bit old, so I'm not sure if this will be helpful now, probably you solved it yourself.
If you don't have a problem with the row execution order the solution would be to query your db in your partitioner bean, then pass to each partitions all the information to split in portions your table/s (start_key, end_key) this will reduce the ram usage (A LOT).
Some warnings:
Be aware that both the query used by partitioner bean and the reader must have the same "order by"
partitioned readers must be scope="step"
Don't use this method if you need to process the rows in a precise order
To tune the RAM try different combination of gridSize and taskExecutor maxCoreSize (that's why my gridSize is a jobParams)
Here an example:
XML Configuration:
<?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:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- JOB -->
<batch:job id="printPdf" job-repository="jobRepository"
restartable="false">
<batch:step id="MyStep">
<batch:partition step="MyStep.template"
partitioner="myPartitioner" handler="partitionHandler">
</batch:partition>
</batch:step>
</batch:job>
<!-- Partitioner -->
<bean id="myPartitioner" class="foo.MyPartitioner"
scope="step">
<property name="jdbcTemplate" ref="myJdbcTemplate" />
<property name="sql"
value="Select ...." />
<property name="rowMap">
<bean
class="foo.MyPartitionHandlerRowMapper" />
</property>
<property name="preparedStatementSetter">
<bean
class="org.springframework.batch.core.resource.ListPreparedStatementSetter">
<property name="parameters">
<list>
<value>#{jobParameters['param1']}</value>
</list>
</property>
</bean>
</property>
</bean>
<bean id="partitionHandler" scope="step"
class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
<property name="taskExecutor" ref="customTaskExecutor" />
<property name="gridSize" value="#{jobParameters['gridSize']}" />
<property name="step" ref="MyStep.template" />
</bean>
<bean id="customTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="8" />
<property name="maxPoolSize" value="8" />
<property name="waitForTasksToCompleteOnShutdown" value="true" />
<property name="awaitTerminationSeconds" value="120" />
</bean>
<batch:step id="MyStep.tempate">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk commit-interval="2500" reader="myReader"
processor="myProcessor" writer="myWriter" skip-limit="2500">
<batch:skippable-exception-classes>
<batch:include class="...FunctionalException" />
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>
<!-- Beans -->
<!-- Processors -->
<bean id="myProcessor" class="foo.MyProcessor"
scope="step">
</bean>
<bean id="classLoaderVerifier"
class="it.addvalue.pkjwd.services.genbean.GenericStockKeysForNoDuplicate" />
<!-- Readers -->
<bean id="myReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="myDataSouce" />
<property name="sql"
value="select ... from ... where ID >= ? and ID <= ?" />
<property name="rowMapper">
<bean class="foo.MyReaderPartitionedRowMapper" />
</property>
<property name="preparedStatementSetter">
<bean
class="org.springframework.batch.core.resource.ListPreparedStatementSetter">
<property name="parameters">
<list>
<value>#{stepExecutionContext['START_ID']}</value>
<value>#{stepExecutionContext['END_ID']}</value>
</list>
</property>
</bean>
</property>
</bean>
<!-- Writers -->
<bean id="myWriter"
class="org.springframework.batch.item.database.JdbcBatchItemWriter">
<property name="assertUpdates" value="false" />
<property name="itemPreparedStatementSetter">
<bean class="foo.MyWriterStatementSetters" />
</property>
<property name="sql"
value="insert ..." />
<property name="dataSource" ref="myDataSouce" />
</bean>
</beans>
Your Partitioner Bean will look like this:
package foo;
import foo.model.MyTable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
public class MyPartitioner implements Partitioner
{
private JdbcTemplate jdbcTemplate;
private RowMapper<foo.model.MyTable> rowMap;
private String sql;
private PreparedStatementSetter preparedStatementSetter;
public JdbcTemplate getJdbcTemplate()
{
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate)
{
this.jdbcTemplate = jdbcTemplate;
}
public RowMapper<foo.model.MyTable> getRowMap()
{
return rowMap;
}
public void setRowMap(RowMapper<PkjwdPolizzePartition> rowMap)
{
this.rowMap = rowMap;
}
public String getSql()
{
return sql;
}
public void setSql(String sql)
{
this.sql = sql;
}
public PreparedStatementSetter getPreparedStatementSetter()
{
return preparedStatementSetter;
}
public void setPreparedStatementSetter(PreparedStatementSetter preparedStatementSetter)
{
this.preparedStatementSetter = preparedStatementSetter;
}
#Override
public Map<String, ExecutionContext> partition(int gridSize)
{
Map<String, ExecutionContext> map = new HashMap<String, ExecutionContext>();
try
{
List<PkjwdPolizzePartition> lstMyRows = jdbcTemplate.query(sql, preparedStatementSetter ,rowMap);
if ( lstMyRows.size() > 0 )
{
int total = lstMyRows.size();
int rowsPerPartition = total / gridSize;
int leftovers = total % gridSize;
total = lstMyRows.size() - 1;
int startPos = 0;
int endPos = rowsPerPartition - 1;
int i = 0;
while (endPos <= (total))
{
ExecutionContext context = new ExecutionContext();
if ( endPos + leftovers == total )
{
endPos = total;
}
else if ( endPos >= (total) )
{
endPos = total;
}
context.put("START_ID", lstMyRows.get(startPos).getId());
context.put("END_ID", lstMyRows.get(endPos).getId());
map.put("PART_" + StringUtils.leftPad("" + i, ("" + gridSize).length(), '0'), context);
i++;
startPos = endPos + 1;
endPos = endPos + rowsPerPartition;
}
}
}
catch ( Exception e )
{
e.printStackTrace();
}
return map;
}
}