Why JDBCTEmplate.batchupdate(sql[]) method not roll back in Spring4 using #transaction annotation? - spring

The below code is not working for rollback when any exception occurs while insertion of records in database.I am using Spring 4 framework and annotation .
*/I am using below code for transaction management and it will not roll back for any exception./
#Transactional(rollbackFor = RuntimeException.class)
public boolean insertBatch(List<String> query) throws SQLException {
boolean flag= false;
try
{
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String[] Sql= query.toArray(new String[query.size()]);
jdbcTemplate.batchUpdate(Sql);
flag=true;
}catch(DataAccessException e )
{
flag=false;
MessageResource.setMessages("Constraints Violation ! CSV data value not matched with database constraints ");
LOGGER.info("CSV file Data not expected as database table structure defination like constraint violation/Data Type lenght/NUll etc for same data value" );
LOGGER.error( "Cause for error: "+ e.getRootCause().getMessage());
LOGGER.debug( "Details explain : "+ e.toString());
throw new RuntimeException("Roll back operation");
//transactionManager.rollback(status);
}
return flag;
}**

Actullay answaer provided by Sir, M.Deinum is below:
Spring uses proxies to apply AOP this will only work for methods called from the outside. Internal method calls don't pass through the proxy hence no transactions and depending on your queries you get one large or multiple smaller commits. Make sure that the outer method (the one called to initiate everything) is transactional. – M. Deinum 14 hours ago

#Transactional(rollbackFor = RuntimeException.class)
This will rollback only if a RuntimeException or a subclass is thrown from the annotated method. If you want to rollback for any Exception (such as SQLException, which is NOT a RuntimeException), you should do:
#Transactional(rollbackFor = Exception.class)
And if you want to try a rollback for whatever error that might happen
#Transactional(rollbackFor = Throwable.class)
Altough in this last case the runtime might be so broken that not even the rollback can complete.

Use Prepared statement from connection object and the do a execute batch object. On the connection object use conn.setAutoCommit(false). Prepeared statement has 4 times better performance than JdbcTemplate for batch insertion of 1000 records.
Reference : JdbcTemplate.batchUpdate() returns 0, on insert error for one item but inserts the remaining item into sql server db despite using #Transactional

Related

springboot manually commit after each call to Stored Procedure

I have a springboot application that needs to iterate over a large number of records and call a stored procedure which inserts some data in a table for each record read.
we cannot use BatchUpdate because it is taking a long time to process thousands of records , I was asked to commit frequently (either after every record or after x records)
I was looking online and I did not see a good example on how to commit manually in springboot while calling a stored procedure in a loop.
I am using SimpleJdbcCall and my code looks like this:
#Transactional(isolation = Isolation.READ_UNCOMMITTED,propagation = Propagation.NOT_SUPPORTED)
public class EventsProcessor
{
#Autowired
#Qualifier("dbDatasource")
DataSource dataSource;
public void process(List<Event> events) throws Exception
{
SimpleJdbcCall dbTemplate = new SimpleJdbcCall(dataSource).withProcedureName("UPDATE_EVENTS").withSchemaName("TEST");
DataSourceUtils.getConnection(dataSource).setAutoCommit(false);
for (Event ev : events)
{
//fill inParams here
outParams = dbTemplate.execute(inParams);
DataSourceUtils.getConnection(dataSource).commit();
}
}
}
I tried without Propagation.NOT_SUPPORTED and with it but same result.
The code is executing the call to the sp and there is not error when it executes commit() , but after commit() if I query the table that the sp inserted the record in it, I don't see the records in the table.
If I remove the setAutocommit(false) and the commit statement and the Propagation.NOT_SUPPORTED , and just let springboot handle transactions, then while it's processing, I can see the records in the table if I do READ UNCOMMITTED, but they will not be committed till the full job ends.
What am I doing wrong that is preventing the commit to happen after each row?
I ended up separating the call to the SP into a different method with
#Transactional(propagation = Propagation.REQUIRES_NEW)
This way when it returns from the method, it commits.

Spring Transactional on method with multiple db operation

I'm little confused. I though #Transactional on method means all operations or none.
Say I have this method:
#Transactional
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Say calculation2() throw exception or my second call to myDao.saveResult goes wrong , what I see is the even though the whole method annotated with #Transactional the saving result after calculation1() call is successful.
That is my first interaction with database saved the records I want but the second one failed but I thought because the method is #Transactinal even the first call to save to database should be rolled back.
Do I miss something?
#Transactional (rollbackFor = Exception.class)
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Try using this as well and throw Exceptions.
It depends on how you handle exceptions and if there are still #Transactional annotated on those internal method calls.
To have "all or nothing" behaviour in fewDbOpeations(), make sure the followings for all the internal method calls :
Do not annotated with #Transactional(propagation=REQUIRES_NEW)
Do not catch the exception inside and not throw out. Throw RuntimeException or Error but not checked Exception (Assume you are using default setting).

Deleting a record then selecting within the same Spring Transaction still returns the deleted record

I have some code within a spring transaction with the isolation level set to SERIALIZABLE. This code does a few things firstly it deletes all records from a table that have a flag set, next it performs a select to ensure invalid records can not be written and finally the new records are written.
The problem is that the select continues to return the records that were deleted if the code is run with the transaction annotation. My understanding is that because we are performing these operations within the same spring transaction that the previous delete operation will be considered when performing the select.
We are using Spring Boot 2.1 and Hibernate 5.2
A summary of the code is shown below:
#HystrixCommand
public void deleteRecord(EntityObj entityObj) {
fooRepository.deleteById(entityObj.getId());
//Below line added as part of debugging but I don't think I should really need it?
fooRepository.flush();
}
public List<EntityObj> findRecordByProperty(final String property) {
return fooRepository.findEntityObjByProperty(property);
}
#Transactional(isolation = Isolation.SERIALIZABLE)
public void debugReadWrite() {
EntitiyObject entitiyObject = new EntityObject();
entitiyObject.setId(1);
deleteRecord(entitiyObject);
List<EntityObj> results = findRecordByProperty("bar");
if (!results.isEmpty()) {
throw new RuntimeException("Should be no results!")
}
}
The transaction has not committed yet, you need to complete the transaction and then find the record.
decorating the deleteRecord with propagation = Propagation.REQUIRES_NEW) should solve the issue
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteRecord(EntityObj entityObj) {
fooRepository.deleteById(entityObj.getId());
// flush not needed fooRepository.flush();
}
A flush is not needed because when deleteRecord completes the translation will be committed.
under the hood
//start transaction
public void deleteRecord(EntityObj entityObj) {
fooRepository.deleteById(entityObj.getId());
}
//commit transaction
Turns out the issue was due to our use of Hystrix. The transaction is started outside of Hystirx and then at a later point goes through a Hystrix command. The Hystrix command is using a threadpool and so the transaction is lost while executing on the new thread from the Hystrix threadpool. See this github issue for more info:
https://github.com/spring-cloud/spring-cloud-netflix/issues/1381

Making a method transactional in Spring

I use a hibernate as JPA provider
#RestController
public class RestController {
private final TestService testService;
#PostMapping(value = "/file/{entityId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void test(#PathVariable #NotNull UUID entityId) {
testService.delete(entityId);
}
}
class TestService {
#AutoWired
EntityRepository repo; // <- Crud repository from Spring Data
public void delete(UUID id2){
//if row not exists with id == id2
throw NoFoundException
// else
//remove from database using repo.
}
}
And how to resolve the following case:
"if row not exists with id == id2 " evaluated to false, because object exists in fact.
Other thread deleted that row.
"remove from database using repo" <- error, there is no such row because it was removed by other thread in the step 2.
You can use #Transactional on your Service methods to ensure your database operations run in a transaction. By default, you can roll back the transaction if you throw a unchecked exception inside the annotated method. You can also specify on which exceptions to rollback using #Transactional's rollbackFor Parameter
Not sure why you've got a delete method that is basically doing exactly the same as the SimpleJpaRepository delete method, so for starters I'd change your code to
repo.delete(entityId)
and get rid of test service.
If you are worried about getting a EmptyResultDataAccessException when there is no data to delete, either catch the exception and ignore it or use pessimistic locking on whatever else is doing deletes, as explained
here
You can use the annotation #Transaction for your service method delete(UUID id2).Default propagation of #Transaction is Propagation.REQUIRED which means that if you get an existing transaction it continues that and if you do not have existing transaction it will create one for you.

Jpa save entity after failed delete

I am trying to remove an entity and if it cannot be removed because of a constraint I want to mark it for logical removal.
This is my code:
#Transactional
public void removeEntity(EntityDto e) {
Entity entity = entityRepository.findOne(e.getId());
try {
entityRepository.delete(e.getId());
entityRepository.flush();
} catch (DataIntegrityViolationException ex) {
logger.debug("Logical removal");
entity.setLogicalRemovalDate(new Date());
entityRepository.save(entity);
}
}
After calling the save() method I get this exception:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.ObjectDeletedException: deleted instance passed to merge
Is there any way I can implement this functionality ?
Thanks.
Not without doing it in a new transaction, with a fresh Hibernate session. The documentation says:
If the Session throws an exception, including any SQLException,
immediately rollback the database transaction, call Session.close()
and discard the Session instance. Certain methods of Session will not
leave the session in a consistent state. No exception thrown by
Hibernate can be treated as recoverable.
You should probably check that there is no other entity referencing the entity to delete before trying to delete it. Or simply always delete it logically, since it seems that it's what you're doing for referenced entities.

Resources