I am novice in spring Transaction Management, I am using it in batch insert but the batch processing is not working, its committing line by line when autoCommit is true, and if autoCommit is false then the data is not inserting in D.B.
below is the insert function.
#Override
#Transactional(propagation= Propagation.REQUIRED, rollbackFor=Exception.class)
public void insertInviteContactMsisdn(final long ettId, final List<String> msisdnList, final List<String> allreadyMsisdnList) {
try {
String qr = "insert into InviteContactMsisdn (id,ettIdAparty,msisdn,status) values(0,?,?,?)";
getJdbcTemplate().batchUpdate(qr, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setLong(1, ettId);
ps.setString(2, msisdnList.get(i));
ps.setBoolean(3, allreadyMsisdnList.contains(msisdnList.get(i)));
}
#Override
public int getBatchSize() {
return msisdnList.size();
}
});
}catch(DataAccessException dataAccessException) {
log.error("error in processing insertInviteContactMsisdn ettId:"+ettId);
throw dataAccessException;
}
}
also the dataSource configuration:-
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.pass}" />
<property name="removeAbandoned" value="true"/>
<property name="initialSize" value="1" />
<property name="maxActive" value="1" />
<property name="defaultAutoCommit" value="false" />
Thanks.
I know I am doing something seriously wrong.
when I using programmatic Transaction Management the same problem occurring.
#Override
public void insertInviteContactMsisdn(final long ettId, final List<String> msisdnList, final List<String> allreadyMsisdnList) {
// TODO Auto-generated method stub
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
String qr = "insert into InviteContactMsisdn (id,ettIdAparty,msisdn,status) values(0,?,?,?)";
jdbcTemplateObject.batchUpdate(qr, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setLong(1, ettId);
ps.setString(2, msisdnList.get(i));
ps.setBoolean(3, allreadyMsisdnList.contains(msisdnList.get(i)));
}
#Override
public int getBatchSize() {
return msisdnList.size();
}
});
transactionManager.commit(transactionStatus);
}catch(DataAccessException dataAccessException) {
log.error("error in processing insertInviteContactMsisdn ettId:"+ettId);
transactionManager.rollback(transactionStatus);
throw dataAccessException;
}
}
Related
I developed the application using spring transactions and insert records in the table.
I'm explicitly throwing the exception in DAO class but spring is inserting the record into the table rather than roll back the transaction.
I have created the two applications as below . In Case 1 record is inserted into table even though exception is thrown . But In Case 2 no record is inserted and spring roll back the transaction successfully. Can you explain me the difference between these two applications.
Case 1:
Item.java
public class Item {
int itemNo;
String itemName;
String itemType;
String itemSize;
public int getItemNo() {
return itemNo;
}
public void setItemNo(int itemNo) {
this.itemNo = itemNo;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getItemType() {
return itemType;
}
public void setItemType(String itemType) {
this.itemType = itemType;
}
public String getItemSize() {
return itemSize;
}
public void setItemSize(String itemSize) {
this.itemSize = itemSize;
}
}
ItemDao
#Service
public class ItemDao {
#Autowired
JdbcTemplate jdbcTemplate ;
void insert(Item item){
jdbcTemplate.update("insert into item_test(itemno, itemtype,itemsize,itemname) values (?,?,?,?)", new Object[]{item.getItemNo(),item.getItemType(),item.getItemSize(),item.getItemName()});
int a=2/0;
}
}
ItemService.java
#Service
public class ItemService {
#Autowired
ItemDao itemDao;
#Transactional
public void insert(Item item){
try{
itemDao.insert(item);
}
catch(Exception e){
e.printStackTrace();
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ct = new ClassPathXmlApplicationContext("spring.xml");
ItemService itemService = ct.getBean("itemService", ItemService.class);
Item item = new Item();
item.setItemNo(1234);
item.setItemName("sofa");
item.setItemSize("4");
item.setItemType("furniture");
itemService.insert(item);
}
}
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<context:component-scan base-package="com.spring.springtransaction" />
<!-- Creating TransactionManager Bean, since JDBC we are creating of type
DataSourceTransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- MySQL DB DataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#locahost:1521:xe)))" />
<property name="username" value="system" />
<property name="password" value="system" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="itemService" class="com.spring.springtransaction.ItemService" />
</beans>
Case 2:
ItemService.java
#Service
public class ItemService {
#Autowired
ItemDao itemDao;
#Autowired
ItemManger itemManger;
#Transactional
public void insert(Item item){
try{
itemManger.insert(item);
}
catch(Exception e){
e.printStackTrace();
}
}
}
ItemManger.java
#Service
public class ItemManger {
#Autowired
ItemDao itemDao;
#Transactional
public void insert(Item item){
itemDao.insert(item);
}
}
ItemDao.java
#Service
public class ItemDao {
#Autowired
JdbcTemplate jdbcTemplate ;
void insert(Item item){
jdbcTemplate.update("insert into item_test(itemno, itemtype,itemsize,itemname) values (?,?,?,?)", new Object[]{item.getItemNo(),item.getItemType(),item.getItemSize(),item.getItemName()});
int a=2/0;
}
}
Annotate you ItemDao as #Repository instead of #Service
You should execute unit test with spring Transactional context instead of main , for example using TestNG:
#ContextConfiguration(classes = {ConfigurationClass.class})
#ActiveProfiles({"test"})
public class TestItemDAO extends AbstractTransactionalTestNGSpringContextTests {
#Autowired
private ItemDao dao;
#Test
public void testItemDao() {
dao.insert(item);
}
}
Remove the try block, you are trying to handle the exception so this is reason why RollbackException is not cutting the transaction stream.
I am new to Spring and learning the transaction concepts. Unable to get the #Transactional to work.
Use Case:
Data insert of Employee and Employee details should rollback when getEmployee() throws RunTimeException. But the rollback is not happening.
I am using Oracle database 11g and spring 4.3.1.RELEASE. Below is the standalone java code am running.
Code
public static void main( String[] args )
{
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("spring-bean.xml");
ctx.registerShutdownHook();
Employee emp = new Employee("149518", "Mickey", "Mouse", "15 years", "tennis");
IEmployee empIntfc = (IEmployee)ctx.getBean("empService");
try {
empIntfc.createEmployee(emp);
empIntfc.createEmployeeDetails(emp);
//Below will throw RunTime Exception
empIntfc.getEmployee(2);
}catch (Exception e ) {
e.printStackTrace();
} finally {
ctx.close();
}
}
EmployeeService.java
public class EmployeeService implements IEmployee {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
jdbcTemplate = new JdbcTemplate(this.dataSource);
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
#Override
#Transactional
public int createEmployee(Employee emp) {
String sql1 = "INSERT INTO TEST_T1(EMP_ID, EMP_FNAME, EMP_LNAME) values
(?,?,?)";
return getJdbcTemplate().update(sql1, emp.getEmpId(),
emp.getEmpFirstName(), emp.getEmpLastName());
}
#Override
#Transactional
public int createEmployeeDetails(Employee emp) {
String sql = "INSERT INTO TEST_T2(EMP_ID, EXP, SKILLS) values (?,?,?)";
return getJdbcTemplate().update(sql, emp.getEmpId(), emp.getExp(),
emp.getSkills());
}
#Override
#Transactional(readOnly = true, noRollbackFor=RuntimeException.class)
public Employee getEmployee(int empId) {
throw new RuntimeException("Intentional runtime exception");
}
}
spring-bean.xml
<beans xmlns="http://www.springframework.org/schema/beans">
<context:annotation-config/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#//xxxx:1521/xxxx"/>
<property name="username" value="user"/>
<property name="password" value="user"/>
</bean>
<bean id="empService" class="com.service.EmployeeService">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
Your main method is not transactional ... means : entering 'createEmployee' creates a new transaction and commits it, 'createEmployeeDetails' creates a new transaction and commits it .
I use JpaRepository to save data, but the hibernate.show_sql shows "select" and won't save data. Following is my service:
#Autowired
private UserRepository userRepository;
#PostConstruct
public void init() {
User admin = new User();
admin.setDisplayName("admin");
admin.setEmailAddress("admin#admin");
admin.setPassword("admin___");
admin.setRegisteredAt(new Date());
admin.setLastAccessAt(new Date());
admin.setUuid(UUID.randomUUID().toString());
try {
System.out.println("before save");
userRepository.save(admin);
System.out.println("after save");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
The output looks like this:
========before save======
Hibernate: select user0_.uuid as uuid1_0_0_, user0_.display_name as display_2_0_0_, user0_.email_address as email_ad3_0_0_, user0_.last_access_at as last_acc4_0_0_, user0_.password as password5_0_0_, user0_.registered_at as register6_0_0_ from User user0_ where user0_.uuid=?
========after save=======
Following is my applicationContext.xml:
<context:component-scan base-package="test">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/helloworld" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
<bean id="myEmf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="test.entity"></property>
<property name="dataSource" ref="myDataSource" />
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource" />
</bean>
<jpa:repositories base-package="test.repository"
entity-manager-factory-ref="myEmf" transaction-manager-ref="transactionManager"></jpa:repositories>
Attached is my class generated by JPA Tools:
#Entity
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String uuid;
#Column(name="display_name")
private String displayName;
#Column(name="email_address")
private String emailAddress;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="last_access_at")
private Date lastAccessAt;
private String password;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="registered_at")
private Date registeredAt;
public User() {
}
public String getUuid() {
return this.uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getDisplayName() {
return this.displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getEmailAddress() {
return this.emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public Date getLastAccessAt() {
return this.lastAccessAt;
}
public void setLastAccessAt(Date lastAccessAt) {
this.lastAccessAt = lastAccessAt;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getRegisteredAt() {
return this.registeredAt;
}
public void setRegisteredAt(Date registeredAt) {
this.registeredAt = registeredAt;
}
}
Since you're using JPA, the transaction manager should be a JpaTransactionManager, not a DataSourceTransactionManager.
When I am prining data in console with , it is working.
But when I am trying to write data in output text file, it is not working but there is no error.
Can anyone help me that what is issue here??
Bellow is my coding:-
Java Class:
It should be used during writting in file.
import org.springframework.batch.item.file.transform.FieldExtractor;
import com.poc.customer.vo.Person;
public class PersonFieldSetExtractor implements FieldExtractor<Person> {
#Override
public Object[] extract(Person person) {
return new Object[] { person.getSrcId(), person.getSrcCode(),
person.getFirstName(), person.getLastName(),
person.getPincode(), person.getStreet()};
}
}
Java Class:
This will be used after reading from file and during bean population.
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
import com.poc.customer.vo.Person;
public class PersonFieldSetMapper implements FieldSetMapper<Person> {
#Override
public Person mapFieldSet(FieldSet fieldSet) throws BindException {
Person person = new Person();
person.setSrcId(fieldSet.readString(0));
person.setSrcCode(fieldSet.readString(1));
person.setFirstName(fieldSet.readString(2));
person.setLastName(fieldSet.readString(3));
person.setPincode(fieldSet.readString(4));
//person.setStreet(fieldSet.readString(5));
return person;
}
}
public class Person implements Serializable, Comparable<Person> {
#Override
public int compareTo(Person arg0) {
return this.firstName.compareTo(arg0.getFirstName());
}
private String srcId;
private String srcCode;
private String firstName;
private String lastName;
private String pincode;
private String street;
public String getSrcId() {
return srcId;
}
public void setSrcId(String srcId) {
this.srcId = srcId;
}
public String getSrcCode() {
return srcCode;
}
public void setSrcCode(String srcCode) {
this.srcCode = srcCode;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPincode() {
return pincode;
}
public void setPincode(String pincode) {
this.pincode = pincode;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
Spring Configuration:-
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<batch:job id="personJob">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="txtFileItemReader" writer="txtFileItemWriter" commit-interval="1">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="txtFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<bean id="txtFileItemReader" class="com.poc.batch.util.FlatFileReader">
<!-- Read a csv file -->
<property name="resource" value="classpath:com/cts/poc/batch/resource/Test_Data.txt" />
<property name="lineMapper" ref="lineMapper" />
</bean>
<bean id="lineMapper" class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="fieldSetMapper" ref="fieldSetMapper" />
<property name="lineTokenizer" ref="lineTokenizer" />
</bean>
<bean id="lineTokenizer" class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value="|"/>
</bean>
<bean id="fieldSetMapper" class="com.poc.batch.util.PersonFieldSetMapper"/>
<!-- Writer to write output txt file -->
<bean id="txtFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:com/cts/poc/batch/resource/Test_Out_Data.txt" />
<property name="appendAllowed" value="true" />
<property name="lineAggregator" ref="lineAggregator"/>
</bean>
<bean id="lineAggregator" class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="|" />
<property name="fieldExtractor" ref="personFieldSetExtractor" />
</bean>
<bean id="personFieldSetExtractor" class="com.poc.batch.util.PersonFieldSetExtractor"/>
</beans>
org.springframework.batch.item.file.MultiResourceItemWriter saves currentResourceItemCount to executionContext, but inside the update function, which is always called before write function and as result when I am getting always currentResourceItemCount equal to 0.
What I want to achieve is to get the value of how many items have bean written to the file and put it to the footer.
public class FooterCallback extends StepExecutionListenerSupport implements FlatFileFooterCallback {
private StepExecution stepExecution;
#Override
public void writeFooter(Writer writer) throws IOException {
Integer itemCount = stepExecution.getExecutionContext().getInt("MultiResourceItemWriter.resource.item.count");
writer.write("F;" + itemCount );
}
}
Config is:
<bean id="csvGenerateWriter" class="org.springframework.batch.item.file.MultiResourceItemWriter" scope="step">
<property name="resource" value="file:#{jobExecutionContext[outputPath]}#{jobExecutionContext[outputFile]}"/>
<property name="itemCountLimitPerResource" value="3" />
<property name="delegate" ref="delegateWriter" />
</bean>
<bean id="delegateWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="footerCallback" ref="footerCallback" />
</bean>
but with the above code, I am always getting 0 in itemCount. Is is a bug or a feature? Why update furntion in MultiResourceItemWriter is not called after the write?
Create a custom writer with delegation; also this writer will be responsible for footer and should take care of written items count.
public class FooterWriter implements ResourceAwareItemWriterItemStream<Item>,FlatFileFooterCallback {
ResourceAwareItemWriterItemStream<Item> delegate;
int count;
public void write(List<? extends Item> items) throws Exception {
count += items.size();
delegate.write(items);
}
public void setDelegate(ResourceAwareItemWriterItemStream<Item> delegate) {
this.delegate = delegate;
}
public void writeFooter(Writer writer) throws IOException {
writer.write("F;" + count);
}
public void open(ExecutionContext executionContext) throws ItemStreamException {
count = executionContext.getInt("fwc", 0);
delegate.open(executionContext);
}
public void update(ExecutionContext executionContext) throws ItemStreamException {
executionContext.putInt("fwc", count);
delegate.update(executionContext);
}
public void close() throws ItemStreamException {
// call close() before reset count because close() calls writeFooter()!
delegate.close();
this.count = 0;
}
public void setResource(Resource resource) {
delegate.setResource(resource);
}
}
<bean id="csvGenerateWriter" class="org.springframework.batch.item.file.MultiResourceItemWriter" scope="step">
<property name="resource" value="file:output.txt"/>
<property name="itemCountLimitPerResource" value="3" />
<property name="delegate" ref="delegateWriter" />
</bean>
<bean id="flatFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="footerCallback" ref="delegateWriter" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
</property>
</bean>
<bean id="delegateWriter" class="footercallback.FooterWriter">
<property name="delegate" ref="flatFileItemWriter" />
</bean>