I am developing a Spring batch to get data from a table and into a CSV file. This is the table I am extracting data from:
CREATE TABLE TMP_SYNCHRONIZED_RSLT
(SERIAL_QA NUMBER(20) UNIQUE
, NB_RECENT VARCHAR2(10)
, NB_ARTICLE VARCHAR2(2)
);
I have my reader as follows:
<bean id="serialNumbersReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
<property name="fetchSize" value="${batch.job.fetch.interval}" />
<property name="dataSource" ref="dataSource" />
<property name="rowMapper">
<bean class="org.springframework.jdbc.core.BeanPropertyRowMapper">
<property name="mappedClass" value="ca.org.serialNumbersGenerationBatch.batch.synchronization.dto.ExtractSerialNumbersDto" />
</bean>
</property>
<property name="sql">
<value>
<![CDATA[
SELECT SERIAL_QA, NB_RECENT, NB_ARTICLE from TMP_SYNCHRONIZED_RSLT
]]>
</value>
</property>
</bean>
And my writer:
<bean id="csvWriteSerialNumbers"
class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value=";" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="serialQa, nbRecent, nbArticle" />
</bean>
</property>
</bean>
</property>
<property name="encoding" value="${csv.encoding}" />
<property name="headerCallback">
<bean class="fr.canalplus.cgaweb.batch.common.writer.StringHeaderFooterCallback">
<property name="header" value="SERIAL_QA;NB_RECENT;NB_ARTICLE" />
</bean>
</property>
<property name="resource" value="file:${tmp.dir}/serialNumbers.csv" />
</bean>
my csv.encoding = ISO-8859-1. And I put String in all of my DTO attributes.
This generates weird numbers like 2,49254E+12 instead of 24925418071 in my CSV under SERIAL_QA. Any idea on how to resolve this without changing the column type?
Related
My ThreadPoolTaskExecutor's corePoolSize is 5 and in JdbcPaginingItemReader bean config I have set saveState to false (per documentation it should be set to false if used in a multi-threaded env) and my table have a primary key, which I am using in sortKey attribute of queryProvider yet when task-executor spawns all five threads simultaneously they all are trying to read the data, startAfterValues of JdbcPaginingItemReader is getting messed up. Reader call from each thread is reading the duplicate rows due to the fact that startAfterValues is not thread safe.
How do I overcome this?
Here are my config info.
<job id="myJob" xmlns="http://www.springframework.org/schema/batch" incrementer="jobIncrementer">
<step id="step1">
<tasklet task-executor="myTaskExecutor">
<chunk reader="myReader" writer="myWriter" commit-interval="1000" />
<transaction-attributes isolation="READ_COMMITTED" />
</tasklet>
</step>
</job>
<bean id="myReader" class="org.springframework.batch.item.database.JdbcPagingItemReader" scope="step">
<property name="dataSource" ref="myDataSource" />
<property name="queryProvider">
<bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="selectClause" value="SELECT ID, NAME"/>
<property name="fromClause" value="FROM EMPLOYEE" />
<property name="whereClause" value="where 1=1" />
<property name="sortKey" value="ID"/>
</bean>
</property>
<property name="pageSize" value="1000"/>
<property name="fetchSize" value="1000"/>
<property name="saveState" value="false"/>
<property name="rowMapper">
<bean class="com...MyRowMapper"/>
</property>
</bean>
<bean id="myWriter" class="com...MyItemWriter" scope="step">
<constructor-arg name="jdbcTemplate" ref="jdbcTemplate" />
<constructor-arg name="namedParamJdbcTemplate" ref="namedParamJdbcTemplate" />
<constructor-arg name="endUserID" value="123" />
</bean>
<bean id="myTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10"/>
</bean>
I went through the documentation of Broadleaf to send Email confirmation
Below is my applicationcontext-email.xml file
<?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-4.1.xsd">
<!-- A dummy mail sender has been set to send emails for testing purposes
only To view the emails sent use "DevNull SMTP" (download separately) with
the following setting: Port: 30000 -->
<!-- Broadleaf step 3 -->
<bean id="blServerInfo"
class="org.broadleafcommerce.common.email.service.info.ServerInfo">
<property name="serverName" value="smtp.office365.com" />
<property name="serverPort" value="587" />
</bean>
<bean id="blEmailTemplateResolver"
class="org.thymeleaf.templateresolver.ClassLoaderTemplateResolver">
<property name="prefix" value="emailTemplates/" />
<property name="suffix" value=".html" />
<property name="cacheable" value="${cache.page.templates}" />
<property name="cacheTTLMs" value="${cache.page.templates.ttl}" />
</bean>
<bean id="blEmailTemplateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolvers">
<set>
<ref bean="blEmailTemplateResolver" />
</set>
</property>
<property name="dialects">
<set>
<bean class="org.thymeleaf.spring4.dialect.SpringStandardDialect" />
<ref bean="blDialect" />
</set>
</property>
</bean>
<!-- Broadleaf email step 1 -->
<bean id="blMessageCreator"
class="org.broadleafcommerce.common.email.service.message.ThymeleafMessageCreator">
<constructor-arg ref="blEmailTemplateEngine" />
<constructor-arg ref="blMailSender" />
</bean>
<bean id="blEmailInfo"
class="org.broadleafcommerce.common.email.service.info.EmailInfo">
<property name="fromAddress">
<value>mulaygaurav3#gmail.com</value>
</property>
<property name="sendAsyncPriority">
<value>2</value>
</property>
<property name="sendEmailReliableAsync">
<value>false</value>
</property>
</bean>
<bean id="blMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host">
<value>localhost</value>
</property>
<property name="port">
<value>30000</value>
</property>
<property name="protocol">
<value>smtp</value>
</property>
<property name="username">
<value>gaurav</value>
</property>
<property name="password">
<value>mypassword</value>
</property>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.timeout">25000</prop>
</props>
</property>
</bean>
<bean id="blVelocityEngine"
class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="resourceLoaderPath" value="/WEB-INF/emailTemplates/" />
<property name="velocityProperties">
<value>
resource.loader=file,class
class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
runtime.references.strict = false
</value>
</property>
</bean>
<bean id="blMessageCreator"
class="org.broadleafcommerce.common.email.service.message.VelocityMessageCreator">
<constructor-arg ref="blVelocityEngine" />
<constructor-arg ref="blMailSender" />
<constructor-arg>
<map>
<entry key="number">
<bean class="org.apache.velocity.tools.generic.NumberTool"
scope="prototype" />
</entry>
<entry key="date">
<bean class="org.apache.velocity.tools.generic.ComparisonDateTool"
scope="prototype" />
</entry>
<entry key="list">
<bean class="org.apache.velocity.tools.generic.ListTool"
scope="prototype" />
</entry>
<entry key="math">
<bean class="org.apache.velocity.tools.generic.MathTool"
scope="prototype" />
</entry>
<entry key="iterator">
<bean class="org.apache.velocity.tools.generic.IteratorTool"
scope="prototype" />
</entry>
<entry key="alternator">
<bean class="org.apache.velocity.tools.generic.AlternatorTool"
scope="prototype" />
</entry>
<entry key="sorter">
<bean class="org.apache.velocity.tools.generic.SortTool"
scope="prototype" />
</entry>
<entry key="esc">
<bean class="org.apache.velocity.tools.generic.EscapeTool"
scope="prototype" />
</entry>
<entry key="serverInfo" value-ref="blServerInfo" />
</map>
</constructor-arg>
</bean>
<!-- <bean id="blMessageCreator" class="org.broadleafcommerce.common.email.service.message.NullMessageCreator">
<constructor-arg ref="blMailSender"/> </bean> -->
<bean id="blRegistrationEmailInfo" parent="blEmailInfo">
<property name="subject" value="You have successfully registered!" />
<property name="emailTemplate" value="register-email" />
</bean>
<bean id="blForgotPasswordEmailInfo" parent="blEmailInfo">
<property name="subject" value="Reset password request" />
<property name="emailTemplate" value="resetPassword-email" />
</bean>
<!-- Broadleaf email step 2 -->
<bean id="orderConfirmationEmailInfo" class="org.broadleafcommerce.common.email.service.info.EmailInfo" parent="blEmailInfo" scope="prototype">
<property name="messageBody" value="This is a test email!"/>
<property name="emailType" value="ORDERCONFIRMATION"/>
<property name="subject" value="Thank You For Your Order!"/>
</bean>
</beans>
I have also created an activity as mentioned in the documentation:
package com.mycompany.worklow.emailactivity;
import javax.annotation.Resource;
import org.broadleafcommerce.core.checkout.service.workflow.CheckoutSeed;
import org.broadleafcommerce.core.checkout.service.workflow.CompleteOrderActivity;
import org.broadleafcommerce.core.order.domain.Order;
import org.broadleafcommerce.core.workflow.ProcessContext;
import com.mycompany.service.MyEmailWebService;
enter code here
public class MyCompleteOrderActivity extends CompleteOrderActivity {
#Resource(name="myEmailService")
protected MyEmailWebService myEmailService;
#Override
public ProcessContext execute(ProcessContext context) throws Exception {
CheckoutSeed seed = (CheckoutSeed) context.getSeedData();
Order order = seed.getOrder();
myEmailService.sendOrderConfirmation(order.getSubmitDate(), order.getId().toString(), order.getCustomer().getEmailAddress());
return super.execute(context);
}
}
}
Also added this activity in the applicationcontext-workflow.xml
I am getting this below exxception:
Unable to merge source and patch locations; nested exception is org.broadleafcommerce.common.extensibility.context.merge.exceptions.MergeException: java.lang.NullPointerException
Can anybody explain the step by step procedure to implement the same?
Thanks in advance
You have to beans named blMessageCreator, remove one of them. Assuming you are using version 5 demo site, which uses thymeleaf templates then try removing:
<bean id="blMessageCreator" class="org.broadleafcommerce.common.email.service.message.VelocityMessageCreator">
...
...
</bean>
I read a CSV file as input using spring batch and i have 2 CSV file as output.
The first file contains about 100 lines.
the input file contains 5 colones id,typeProduct and price.
And i have just 2 type of product
i route through all these lines and i write two output files.
For both files a single line containing the type of product and the sum of the prices of all these products which have the same type.
So my need is before writing in the output files. i want to get all lines in a list to make conditions and adding a new attribute to my object for example result if sum> 5000 will take the value good else will take not good for example. and display them in the output line that exists in the file
Here is My product
public class Product {
private Long idt;
private String typeProduct;
private Double price;
private String result;
}
and here is the definition of my job
<batch:job id="exampleMultiWritersJob">
<batch:step id="stepMultiWriters">
<batch:tasklet transaction-manager="txManager">
<batch:chunk reader="exampleFileSourceReader" writer="exampleMultiWriters" commit-interval="10">
<batch:streams>
<batch:stream ref="cnraCosWriter" />
<batch:stream ref="cnraCopWriter" />
<batch:stream ref="rcarCosWriter" />
<batch:stream ref="rcarCopWriter" />
</batch:streams>
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="exampleFileSourceReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" value="file:#{jobParameters['file']}" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<!-- split it -->
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<!-- this is missing -->
<property name="delimiter" value=";"/>
<property name="names" value="idt,productType,price" />
</bean>
</property>
<property name="fieldSetMapper">
<!-- map to an object -->
<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="exampleFileMapper" />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="exampleFileMapper" class="ma.controle.gestion.modele.Product" scope="prototype"/>
And here is the classifier methode
public class ExampleWriterRouteImpl {
#Classifier
public String classify(Product batch){
if(batch.getTypeProduct().equals("Telephone"))
return "tel";
else if(batch.getTypeProduct()).equals("PC")))
return "pc";
return null;
}
}
<bean id="classifier" class="org.springframework.batch.classify.BackToBackPatternClassifier">
<property name="routerDelegate">
<bean class="ma.controle.gestion.springbatch.ExampleWriterRouteImpl" />
</property>
<property name="matcherMap">
<map>
<entry key="tel" value-ref="telWriter" />
<entry key="pc" value-ref="pcWriter" />
</map>
</property>
<bean id="telWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<!-- write to this csv file -->
<property name="resource" value="file:C:/output/tel.csv" />
<property name="shouldDeleteIfExists" value="true" />
<property name="shouldDeleteIfEmpty" value="true" />
<property name="appendAllowed" value="true" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value=";" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="productType,price(Sum of all prdouctType),result" />
</bean>
</property>
</bean>
</property>
<bean id="pcWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<!-- write to this csv file -->
<property name="resource" value="file:C:/output/pc.csv" />
<property name="shouldDeleteIfExists" value="true" />
<property name="shouldDeleteIfEmpty" value="true" />
<property name="appendAllowed" value="true" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value=";" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="productType,price(Sum of all prdouctType),result" />
</bean>
</property>
</bean>
</property>
So i need to make sum of all product which have the same type and have only one single output line that contains product type and the sum of prices and result for each outputs files.
I did not know how to retrieve the list of these objects and retrieve only one line at the end.
Thanks.
i'm trying to read a csv file and write data in 2 output csv file.
I am reading lines from csv file rows and i write thoses lines in two csv output files :
But I want to write product grouped by productName in each output files.
A small example to explain
INPUT FILE
TOSHIBA PC 2000
HP PC 1000
SUMSUNG TEL 500
Nokia TEL 300
OUTPUT FILE tel.csv
TEL 800 (500 + 300 ) the sum of product which have the same type.
OUTPUT FILE pc.csv
PC 3000 ( 2000 + 3000 ) the sum of product which have the same type.
I don't know how to perform that can someone help me ?
Here is my configuration :
<batch:job id="exampleMultiWritersJob">
<batch:step id="stepMultiWriters">
<batch:tasklet transaction-manager="txManager">
<batch:chunk reader="exampleFileSourceReader" writer="exampleMultiWriters" commit-interval="10">
<batch:streams>
<batch:stream ref="telWriter" />
<batch:stream ref="pcWriter" />
</batch:streams>
</batch:chunk>
</batch:tasklet>
</batch:step>
<bean id="exampleFileSourceReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" value="file:#{jobParameters['file']}" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<!-- split it -->
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<!-- this is missing -->
<property name="delimiter" value=";"/>
<property name="names" value="name,productType,price" />
</bean>
</property>
<property name="fieldSetMapper">
<!-- map to an object -->
<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="exampleFileMapper" />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="exampleFileMapper" class="ma.controle.gestion.modele.Product" scope="prototype"/>
<!-- This is to demo MultiWriters -->
<bean id="exampleMultiWriters" class="org.springframework.batch.item.support.ClassifierCompositeItemWriter" scope="step">
<property name="classifier" ref="classifier" />
</bean>
<bean id="classifier" class="org.springframework.batch.classify.BackToBackPatternClassifier">
<property name="routerDelegate">
<bean class="ma.controle.gestion.springbatch.ExampleWriterRouteImpl" />
</property>
<property name="matcherMap">
<map>
<entry key="pc" value-ref="pcWriter" />
<entry key="tel" value-ref="telWriter" />
</map>
</property>
</bean>
<bean id="pcWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<!-- write to this csv file -->
<property name="resource" value="file:C:/output/pc.csv" />
<property name="shouldDeleteIfExists" value="true" />
<property name="shouldDeleteIfEmpty" value="true" />
<property name="appendAllowed" value="true" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value=";" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="productType,price" />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="telWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<!-- write to this csv file -->
<property name="resource" value="file:C:/output/tel.csv" />
<property name="shouldDeleteIfExists" value="true" />
<property name="shouldDeleteIfEmpty" value="true" />
<property name="appendAllowed" value="true" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value=";" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="productType,price" />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="itemProcessor" class="ma.controle.gestion.springbatch.BatchItemProcessor" />
<!-- Optional JobExecutionListener to perform business logic before and after the job -->
<bean id="jobListener" class="ma.controle.gestion.springbatch.BatchJobItemListener" />
And here is the classifier
public class ExampleWriterRouteImpl {
#Classifier
public String classify(Product product){
if(product.getProductType().equals("TEL"))
return "tel";
else if(product.getProductType().equals("PC"))
return "pc";
return null;
}
}
For some reason the ReloadablePropertiesFactoryBean doens't seem to do what I expect. When I change a property in the test.properties file I get a log message saying:
9979 [timer] INFO com.krest.core.properties.ReloadablePropertiesFactoryBean - Reloading Properties...
But for some reason nothing happens. The old property value is still set on my test bean. Does anybody know why this is? I use Spring 3 and my configuration looks like this:
<bean id="configproperties"
class="com.krest.core.properties.ReloadablePropertiesFactoryBean">
<property name="location"
value="classpath:test.properties" />
</bean>
<bean id="propertyConfigurer"
class="com.krest.core.properties.ReloadingPropertyPlaceholderConfigurer">
<property name="properties" ref="configproperties" />
<property name="reloadingPlaceholderPrefix" value="rel{" />
<property name="reloadingPlaceholderSuffix" value="}" />
</bean>
<bean id="mybean" class="nl.mycompany.TestBean">
<property name="message" value="rel{timer-delay}" />
</bean>
<!-- regularly reload property files. -->
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<bean id="reloadProperties"
class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="period" value="1000" />
<property name="runnable">
<bean class="com.krest.core.properties.ReloadConfiguration">
<property name="reconfigurableBeans">
<list>
<ref bean="configproperties" />
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>