How to manage 2 DAO methods in a single transaction? - spring

I need to manage 2 Dao methods in a single transaction ,failure of either of them should rollback the other. The calling method is in the service layer. The technology used in Spring and Hibernate native sql query. Is there a way to achieve this?
Calling Method::
#Transactional(propagation= Propagation.REQUIRED)
public String save(AllowFileTypesForm formBeanObj,Hashtable global)
Called Method1::
public boolean deleteData( String strTableName,String strWhereClause) {
Session session = sessionFactory.getCurrentSession();
String strSqlQuery = null;
boolean deleted=false;
strSqlQuery = "DELETE FROM Persons where" + strWhereClause;
try {
Query query=session.createSQLQuery(strSqlQuery);
if (query.executeUpdate() <= 0) {
throw new SQLException("No row deleted from table " +strTableName);
}
else{
deleted=true;
}
}
catch(Exception e){
e.printStackTrace();
}
return deleted;
}
Similar to this method there is another method which deletes data from some other table.

It will not rollback because you are catching the exceptions.
Spring's transaction management works through exceptions exiting the transaction boundary which are detected by the #Transactional AOP.
If you catch a sql exception for logging or something you must rethrow or throw a new exception to kick off rollback.

Add #Transactional on the service method from where you are calling those DAO methods. This article nicely sums up how it works.

Related

Failure in inner #Transactional(readOnly = false, rollbackFor = Exception.class) rolls back entire outside persistence

I have a job method in a class-annotated #Transactional. This job method calls inner methods for persistence of individual records. If I simulate an error in the following inner update() method somewhere in the middle of my result set processing, I see that all successful records before/after this exception do not get saved after job completion. Why is that? All outside persistence should remain, with the exception of the individual record that failed. The inner update alone has rollbackFor.
#Service("mailService")
#Transactional
#EnableScheduling
public class MailServiceImpl implements MailService {
#Override
#Scheduled(cron = "${mail.cron.pubmed.autosynch.job}")
public void autoSynchPubMedJob() {
//... Fetch result set
for (Result r: resultset) {
try {
pubService.updatePublication(r);
} catch (Exception e) {
// Silently log and continue
log.error("Error on record: ", e);
}
}
}
The updatePublication method, this is the one with rollbackFor:
#Override
#Transactional(readOnly = false, rollbackFor = Exception.class)
public void updatePublication(Publication publication) throws Exception {
dao.update1(..);
dao.update2(..);
// Simulate exception for a specific record for testing
if (publication.getId() == 123) {
throw new Exception("Test Exception");
}
}
Result: no successful data persisted at all at the end of job completion. There should be partial persistence (for other successful records).
When I remove this Exception simulation, all data is successfully persisted at the end. Also, all data is persisted if I remove the inner call's rollbackFor.
Probaby because it uses existing transaction. Try opening a new one with propagation = REQUIRES_NEW.
Note: New transaction won't be opened if you call the method from the same service. You should use either self-reference call or extract logic to another #Service.

Transaction propagation in spring #required

#GetMapping("trans")
#Transactional()
public String primaryTrans() {
User u1 = new User(0,"test","test#email.com");
us.save(u1);
User u2 = new User(0,"test1","test1#email.com");
us.save(u2);
secondaryTrans();
return "index";
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
private void secondaryTrans() {
// TODO Auto-generated method stub
User u2 = new User(0,"test2","test3#email.com".repeat(300));
us.save(u2);
}
Here i am manually raising DATA TOO LONG exception from secondary transaction, But it causes primary transaction also rolled back. How can we make sure that primary transaction to be committed irrespective of secondary transaction
In this case, since the second method is called from the same class, the second transaction is most likely not created. Springs transactional support uses AOP proxies to create transactions. The docs contain a description on why this will not work.
The simplest way is to catch the exception thrown from secondaryTrans() method, so just wrap secondaryTrans() into try-catch block:
try {
secondaryTrans();
} catch (Exception e) {
//...
}

SQLIntegrityConstraintViolationException cause Spring Transaction Rollback

Spring 4.1.4
Hibernate 4.2.0
JDK 1.8
My context: I have a Controller calling --> Service --> calling Dao
The business funcionality is to delete ( in 1 to many DB relation) some child ,but not all child .
Then ,after deleting some child I try to delete the Parent and offcourse I got java.sql.SQLIntegrityConstraintViolationException
But the question is why transaction is market for Rollback ? ( in other words why I don't got the deletion of some child ?)
SQLIntegrityConstraintViolationException is a checked exception and stating Spring documentation the behaviour would be the same of EJB : Note that by default, rollback happens for runtime, unchecked exceptions only. The checked exception does not trigger a rollback of the transaction.
I need to remove some child anf trying to remove the parent if possible, if not I need to commit the transaction maintaining the parent and remaining of childs
Note I tried also to specify in Service and Dao methods the Spring Annotation
#Transactional(noRollbackFor = SQLIntegrityConstraintViolationException.class)
To request explicitly the behaviour expected , but not even like this work for me
Controller code method:
public void delete() {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Data deleted.","");
try{
memoTemplateService.delete(memoTemplate);
memoTemplates.remove(memoTemplate);
}
catch (Exception e){
msg=new FacesMessage(FacesMessage.SEVERITY_WARN, "A","B");
}
reset();
FacesContext.getCurrentInstance().addMessage(null, msg);
}
Service method :
#Override
#Transactional(noRollbackFor = {SQLIntegrityConstraintViolationException.class,DBConstraintException.class})
public void delete(MemoTemplate memoTemplate)throws BusinessException {
// deleting some ,not all , child
phaseAndMemoGenerator.deleteMemosForVisibleTimeHorizon(memoTemplate);
try{// some times Template cannot be deleted
memoTemplateDao.delete(memoTemplate);
}
catch (Exception e){
throw new DBConstraintException("Partial Delete", "Template cannot be deleted, Memo in the past are present");
}
}
Dao
#Repository(value = "memoTemplateDao")
public class MemoTemplateDaoImpl extends GenericJpaDaoImpl<MemoTemplate, Long> implements MemoTemplateDao {
#Override
#Transactional(noRollbackFor = SQLIntegrityConstraintViolationException.class)
public void delete(MemoTemplate t) {
super.delete(t);
em.flush();
}
}
Just an Update : it's incredible but I can't catch neither doing the catch in Dao method ,debugger go in catch block but before this still a java.sql.SQLIntegrityConstraintViolationException is fired , incredible !
#Transactional(noRollbackFor = {SQLIntegrityConstraintViolationException.class,PersistenceException.class})
public void tryToDelete(MemoTemplate t)throws Exception {
super.delete(t);
try{
em.flush();
}
catch (Exception e){
throw new Exception("ddddd");
}
}
If there are constraints defined in DB, you won't be able to bypass them by committing without rollback.

Spring #transactional with #async Timeout value is not working

I have created an asynchronous service for a long running stored procedure call. Things work good but the transaction is not getting timed out after the specified value given in the timeout attribute of the transactional annotation..The structure of the code is given below (not the real one...just skeleton...ignore semantics/syntax)
//asynchronous service
#override
#async("myCustomTaskExecutor")
#Transactional(rollbackfor=Exception.class,timeout=600)
public void serviceMethod(){
//repository method is invoked.
repository.callStoredProcedure();
}
//Repository method in the Repository class
#Transactional(rollbackfor=Exception.class,timeout=600)
public void callStoredProcedure(){
//Stored procedure is called from the private method using hibernate doWork implementation.
privateCallmethod();
}
private void privateCallmethod() throws ApplicationException{
Session session = null;
try{
session = entityManager.unwrap(Session.class);
session.doWork(new Work(){
#Override
public void execute(Connection connection) throws SQLException {
OracleCallableStatement statement =null;
try{
//using hibernate 4.x and ref cursors are used...so went on with this approach..
//suggest if there is some better approach.
String sqlString =“{begin storProcName(?,?)}”;
statement = connection.prepareCall(sqlString);
statement.setInt(1,5);
statement.setString(2,“userName5”);
statement.executeUpdate();
}
catch(Exception e){
throw RunTimeException(e.getMessage);
}
finally{
if(statement != null)
statement.close();
}
}
}
});
}
catch(Exception e){
throw ApplicationException(e.getMessage);
}
//Not using Final block to close the session.Is it an issue ?
}
delay is happening in the stored procedure side(Thread.sleep(700) are not used) yet transaction is not timed out...
Questions :
I guess #Transactional is enough on the service method alone...give little bit insight on correct approach of using #Transactional annotation
for this code setup.
Will the #Transactional works for the JDBC calls inside the doWork Interface implementation...is that whats the issue is ?
Some article suggest to use oracle.jdbc.readTimeout or setQueryTimeout in the CallableStatement... Is it the right way to achieve this.
Kindly point out the mistakes and explain the causes
If #Transactional Annotated method is not the entry point to the class, it will not be transactional unless you enable load time weaving (Spring default is Compile time weaving) https://stackoverflow.com/a/17698587/6785908
You should invoke callStoredProcedure() from outside this class, then it will be transactional. If you invoke serviceMethod() which in turn invokes callStoredProcedure(), then it will not be transactional
I used setQueryTimeout() approach to resolve the issue as #Transactional timeout does not work with the hibernate dowork() method...I guess its due to the hibernate work executes in different thread and it low level JDBC methods to invoke the store procedures...
NOTE: This particular application uses very spring 3.x version and hibernate 4.x with JPA 2.0 spec...little outdated versions

Why Grails/Spring transactional behavior not working in this case?

I have a grails (2.5.2) app, with a mysql and a NoSQL interaction. There's a main/principal service method that call 2 other methods:
class mainService {
static transactional = false
NoSQLDataAccessService noSQLDataAccessService
// main/principal method
#Transactional
void save(json){
// (1) creating domain entities from json
addNewDomainEntities(entities)
// (2)
noSQLDataAccessService.set(json)
}
#Transactional
void addNewDomainEntities(entities){
// save the entities in a mysql schema and use save(flush:true)
// because i need the generated id's
}
}
As you can see, this mainService creates new domain entities (1), flushing the session to get the id's. Then, i call other service method (2) that store the json in a NoSQL schema:
class NoSQLDataAccessService(){
static transactional = false
void set(json){
try{
// save the json in a NoSQL schema
} catch(Exception ex){
// if fails, i log the exception and throws it again
throws ex
}
}
}
But, sometimes the noSQLDataAccessService.set() fails by external causes and the entities created before still persist in the mysql db.(this is the problem)
The save method, that contains all this program execution, is marked like #Transactional, so if the noSQLDataAccessService.set() throws an exception, all the changes made should be not applied because the rollback. I'm right?
You probably have to throw an RuntimeException, not an Exception, to force a rollback as per this StackOverflow conversation. Instead of:
throws ex
you might try:
throw new RuntimeException(ex)
Further, I would recommend you be explicit about your transaction isolation. Perhaps something like:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE)

Resources