I am using a org.springframework.data.jpa.repository.JpaRepository save method to save an entity. I figure that save method comes from CrudRepository interface. I am calling this save method from a service class.
try {
studentLog = studentsLogRepository.save(studentLog);
} catch (DataIntegrityViolationException dive) {
LOGGER.warn("Constraint violation occurred. Cannot insert the same record twice.", dive);
}
But the problem is DataIntegrityViolationException is not getting caugh in the catch block. Instead I see in the log following.
java.sql.BatchUpdateException: ORA-00001: unique constraint (QA_VPP.UX_TVPPC_TRAN_LOG_1) violated
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10296) ~[ojdbc6-11.2.0.2.0.jar:11.2.0.2.0]
at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:216) ~[ojdbc6-11.2.0.2.0.jar:11.2.0.2.0]
at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297) ~[commons-dbcp-1.3.jar:1.3]
at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297) ~[commons-dbcp-1.3.jar:1.3]
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70) ~[hibernate-core-3.6.7.Final.jar:3.6.7.Final]...
and
2016-12-28 10:13:52,655 LL="DEBUG" CR="1_1482920032_407_357_l73q069_VPP" RE="1482920032407" DE="1" TR="tomcat-http--12" LN="o.s.o.j.JpaTransactionManager" Initiating transaction rollback after commit exception
org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:321) ~[spring-orm-3.2.9.RELEASE.jar:3.2.9.RELEASE]
at org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:121) ~[spring-orm-3.2.9.RELEASE.jar:3.2.9.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517) ~[spring-orm-3.2.9.RELEASE.jar:3.2.9.RELEASE]
at
I even tried catching ConstraintViolationException which did not work. I thought spring data wraps data layer exception to DataAccessException, in this case its subclass DataIntegrityViolationException. Then why is the exception is not getting caught? My service method is not annotated with #Transactional
What is happening is that when calling the method .save(), under the hood Spring is telling the EntityManager to persist the entity, which will be only making it managed and persistent within the persistence context, however changes in your entity may not be immediately synchronized with the DB state (depending on the flush mode of your configuration), so either exceptions are not thrown yet, until flush or commit commands are issued. So you can try to call .saveAndFlush() to enforce DB synchronization. This way exceptions will be thrown immediately.
To further comprehend how Spring handles transaction management, I'll leave a useful link: Transaction Management
exception thrown from Spring exception translation mechanism when dealing with lower level persistence exceptions.
so untill the transaction commits not done , so need to catch outside the scope
Example TODO
Service Class
studentLog = studentsLogRepository.save(studentLog);
Catch at controller class
try {
call student service
} catch (DataIntegrityViolationException dive) {
LOGGER.warn("Constraint violation occurred. Cannot insert the same record twice.", dive);
or Use Global Exception Handler to do the same using controller advice
Related
Currently we are facing a spring transaction related issue in our application.
As you can see that in deleteRecord() we are doing a DB operation. But in the next line
a business exception is thrown.
Expected Behavior(As per my knowledge) :
The DB operation should be rolled back as exception is thrown from the next line
Actual Behavior :
Its not getting rolled back. Data is getting inserted to the table
Question :
Why transaction is not getting rolled back ? I dont think its because of the catch block
because deleteRecord() will be executed in new transaction. Please correct me if I am wrong
Code:
class A {
void myMethod() {
for(int i=0 ; i<count ; i++) {
try {
deleteRecord();
} catch(Exception e) {
log.error("Exception caught");
}
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
deleteRecord() throws Exception{
line 1 : deleting record
line 2 : Throwing business exception
}
}
The Spring documentation says the following:
While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.
And
In its default configuration, the Spring Framework’s transaction
infrastructure code only marks a transaction for rollback in the case
of runtime, unchecked exceptions; that is, when the thrown exception
is an instance or subclass of RuntimeException. ( Errors will also -
by default - result in a rollback). Checked exceptions that are thrown
from a transactional method do not result in rollback in the default
configuration
see in 16.5.3: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
This says that the default behavior of the transaction will only rollback for RuntimeExceptions. If you have a own business exception (could be a checked excpetion), you have to explicitly name the exception class the transaction should rollback for:
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = YOUREXCEPTION.class)
change to
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
It is because of #Transaction method call by the method within the same class, does not work if you do not configured Spring to use AspectJ.
Spring #Transaction method call by the method within the same class, does not work?
I'm using Spring and Hibernate in one of the applications that I'm working on and I've got a problem with handling of transactions.
I've got a service class that loads some entities from the database, modifies some of their values and then (when everything is valid) commits these changes to the database. If the new values are invalid (which I can only check after setting them) I do not want to persist the changes. To prevent Spring/Hibernate from saving the changes I throw an exception in the method. This however results in the following error:
Could not commit JPA transaction: Transaction marked as rollbackOnly
And this is the service:
#Service
class MyService {
#Transactional(rollbackFor = MyCustomException.class)
public void doSth() throws MyCustomException {
//load entities from database
//modify some of their values
//check if they are valid
if(invalid) { //if they arent valid, throw an exception
throw new MyCustomException();
}
}
}
And this is how I invoke it:
class ServiceUser {
#Autowired
private MyService myService;
public void method() {
try {
myService.doSth();
} catch (MyCustomException e) {
// ...
}
}
}
What I'd expect to happen: No changes to the database and no exception visible to the user.
What happens: No changes to the database but the app crashes with:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction;
nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
It's correctly setting the transaction to rollbackOnly but why is the rollback crashing with an exception?
My guess is that ServiceUser.method() is itself transactional. It shouldn't be. Here's the reason why.
Here's what happens when a call is made to your ServiceUser.method() method:
the transactional interceptor intercepts the method call, and starts a transaction, because no transaction is already active
the method is called
the method calls MyService.doSth()
the transactional interceptor intercepts the method call, sees that a transaction is already active, and doesn't do anything
doSth() is executed and throws an exception
the transactional interceptor intercepts the exception, marks the transaction as rollbackOnly, and propagates the exception
ServiceUser.method() catches the exception and returns
the transactional interceptor, since it has started the transaction, tries to commit it. But Hibernate refuses to do it because the transaction is marked as rollbackOnly, so Hibernate throws an exception. The transaction interceptor signals it to the caller by throwing an exception wrapping the hibernate exception.
Now if ServiceUser.method() is not transactional, here's what happens:
the method is called
the method calls MyService.doSth()
the transactional interceptor intercepts the method call, sees that no transaction is already active, and thus starts a transaction
doSth() is executed and throws an exception
the transactional interceptor intercepts the exception. Since it has started the transaction, and since an exception has been thrown, it rollbacks the transaction, and propagates the exception
ServiceUser.method() catches the exception and returns
Could not commit JPA transaction: Transaction marked as rollbackOnly
This exception occurs when you invoke nested methods/services also marked as #Transactional. JB Nizet explained the mechanism in detail. I'd like to add some scenarios when it happens as well as some ways to avoid it.
Suppose we have two Spring services: Service1 and Service2. From our program we call Service1.method1() which in turn calls Service2.method2():
class Service1 {
#Transactional
public void method1() {
try {
...
service2.method2();
...
} catch (Exception e) {
...
}
}
}
class Service2 {
#Transactional
public void method2() {
...
throw new SomeException();
...
}
}
SomeException is unchecked (extends RuntimeException) unless stated otherwise.
Scenarios:
Transaction marked for rollback by exception thrown out of method2. This is our default case explained by JB Nizet.
Annotating method2 as #Transactional(readOnly = true) still marks transaction for rollback (exception thrown when exiting from method1).
Annotating both method1 and method2 as #Transactional(readOnly = true) still marks transaction for rollback (exception thrown when exiting from method1).
Annotating method2 with #Transactional(noRollbackFor = SomeException) prevents marking transaction for rollback (no exception thrown when exiting from method1).
Suppose method2 belongs to Service1. Invoking it from method1 does not go through Spring's proxy, i.e. Spring is unaware of SomeException thrown out of method2. Transaction is not marked for rollback in this case.
Suppose method2 is not annotated with #Transactional. Invoking it from method1 does go through Spring's proxy, but Spring pays no attention to exceptions thrown. Transaction is not marked for rollback in this case.
Annotating method2 with #Transactional(propagation = Propagation.REQUIRES_NEW) makes method2 start new transaction. That second transaction is marked for rollback upon exit from method2 but original transaction is unaffected in this case (no exception thrown when exiting from method1).
In case SomeException is checked (does not extend RuntimeException), Spring by default does not mark transaction for rollback when intercepting checked exceptions (no exception thrown when exiting from method1).
See all scenarios tested in this gist.
For those who can't (or don't want to) setup a debugger to track down the original exception which was causing the rollback-flag to get set, you can just add a bunch of debug statements throughout your code to find the lines of code which trigger the rollback-only flag:
logger.debug("Is rollbackOnly: " + TransactionAspectSupport.currentTransactionStatus().isRollbackOnly());
Adding this throughout the code allowed me to narrow down the root cause, by numbering the debug statements and looking to see where the above method goes from returning "false" to "true".
As explained #Yaroslav Stavnichiy if a service is marked as transactional spring tries to handle transaction itself. If any exception occurs then a rollback operation performed. If in your scenario ServiceUser.method() is not performing any transactional operation you can use #Transactional.TxType annotation. 'NEVER' option is used to manage that method outside transactional context.
Transactional.TxType reference doc is here.
Save sub object first and then call final repository save method.
#PostMapping("/save")
public String save(#ModelAttribute("shortcode") #Valid Shortcode shortcode, BindingResult result) {
Shortcode existingShortcode = shortcodeService.findByShortcode(shortcode.getShortcode());
if (existingShortcode != null) {
result.rejectValue(shortcode.getShortcode(), "This shortode is already created.");
}
if (result.hasErrors()) {
return "redirect:/shortcode/create";
}
**shortcode.setUser(userService.findByUsername(shortcode.getUser().getUsername()));**
shortcodeService.save(shortcode);
return "redirect:/shortcode/create?success";
}
My Spring/Java web application has #Transactional services that can touch the database:
#Transactional
public class AbstractDBService { ... }
Desired functionality is for any uncaught throwable that propagates up beyond the service layer to cause a rollback. Was a bit surprised this isn't the default behaviour but after a bit of googling tried:
#Transactional(rollbackFor = Exception.class)
This seems to work except when an exception is deliberately swallowed and not rethrown. (The particular case is when an entity is not found. Guess this could be redesigned to not throw an Exception but expect there will inevitably be others - e.g. one that springs to mind is an InterruptedException when using Thread.sleep()). Then Spring complains:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is
javax.persistence.RollbackException: Transaction marked as
rollbackOnly
...truncated..
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:58)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
Am I missing something here?... Is there a way to tell Spring to rollback on all uncaught throwables?
If you want to rollback on all uncaught Throwables, you can specify that in the annotation:
#Transactional(rollbackFor = Throwable.class)
By default Spring doesn't rollback for Error subclasses, probably because it seems doubtful once an Error is thrown that the JVM will be in a good enough state to do anything about it anyway, at that point the transaction can just time out. (If you try to rollback when an OutOfMemoryError is raised, the most likely outcome is another OutOfMemoryError.) So you may not gain much with this.
When you mention the case of swallowing an exception, there's no way Spring can be expected to know about it because the exception is not finding its way to Spring's proxy (which is implementing the transactional functionality). This is what happens in your RollbackException example, Hibernate has figured out the transaction needs to rollback but Spring didn't get the memo because somebody ate the exception. So Spring isn't rolling the transaction back, it thinks everything is ok and tries to commit, but the commit fails due to Hibernate having marked the transaction rollback-only.
The answer is to not swallow those exceptions but let them be thrown; making them unchecked is supposed to make it easier for you to do the right thing. There should be an exception handler set up to receive exceptions thrown from the controllers, most exceptions thrown at any level of the application can be caught there and logged.
I'm using Spring and Hibernate in one of the applications that I'm working on and I've got a problem with handling of transactions.
I've got a service class that loads some entities from the database, modifies some of their values and then (when everything is valid) commits these changes to the database. If the new values are invalid (which I can only check after setting them) I do not want to persist the changes. To prevent Spring/Hibernate from saving the changes I throw an exception in the method. This however results in the following error:
Could not commit JPA transaction: Transaction marked as rollbackOnly
And this is the service:
#Service
class MyService {
#Transactional(rollbackFor = MyCustomException.class)
public void doSth() throws MyCustomException {
//load entities from database
//modify some of their values
//check if they are valid
if(invalid) { //if they arent valid, throw an exception
throw new MyCustomException();
}
}
}
And this is how I invoke it:
class ServiceUser {
#Autowired
private MyService myService;
public void method() {
try {
myService.doSth();
} catch (MyCustomException e) {
// ...
}
}
}
What I'd expect to happen: No changes to the database and no exception visible to the user.
What happens: No changes to the database but the app crashes with:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction;
nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
It's correctly setting the transaction to rollbackOnly but why is the rollback crashing with an exception?
My guess is that ServiceUser.method() is itself transactional. It shouldn't be. Here's the reason why.
Here's what happens when a call is made to your ServiceUser.method() method:
the transactional interceptor intercepts the method call, and starts a transaction, because no transaction is already active
the method is called
the method calls MyService.doSth()
the transactional interceptor intercepts the method call, sees that a transaction is already active, and doesn't do anything
doSth() is executed and throws an exception
the transactional interceptor intercepts the exception, marks the transaction as rollbackOnly, and propagates the exception
ServiceUser.method() catches the exception and returns
the transactional interceptor, since it has started the transaction, tries to commit it. But Hibernate refuses to do it because the transaction is marked as rollbackOnly, so Hibernate throws an exception. The transaction interceptor signals it to the caller by throwing an exception wrapping the hibernate exception.
Now if ServiceUser.method() is not transactional, here's what happens:
the method is called
the method calls MyService.doSth()
the transactional interceptor intercepts the method call, sees that no transaction is already active, and thus starts a transaction
doSth() is executed and throws an exception
the transactional interceptor intercepts the exception. Since it has started the transaction, and since an exception has been thrown, it rollbacks the transaction, and propagates the exception
ServiceUser.method() catches the exception and returns
Could not commit JPA transaction: Transaction marked as rollbackOnly
This exception occurs when you invoke nested methods/services also marked as #Transactional. JB Nizet explained the mechanism in detail. I'd like to add some scenarios when it happens as well as some ways to avoid it.
Suppose we have two Spring services: Service1 and Service2. From our program we call Service1.method1() which in turn calls Service2.method2():
class Service1 {
#Transactional
public void method1() {
try {
...
service2.method2();
...
} catch (Exception e) {
...
}
}
}
class Service2 {
#Transactional
public void method2() {
...
throw new SomeException();
...
}
}
SomeException is unchecked (extends RuntimeException) unless stated otherwise.
Scenarios:
Transaction marked for rollback by exception thrown out of method2. This is our default case explained by JB Nizet.
Annotating method2 as #Transactional(readOnly = true) still marks transaction for rollback (exception thrown when exiting from method1).
Annotating both method1 and method2 as #Transactional(readOnly = true) still marks transaction for rollback (exception thrown when exiting from method1).
Annotating method2 with #Transactional(noRollbackFor = SomeException) prevents marking transaction for rollback (no exception thrown when exiting from method1).
Suppose method2 belongs to Service1. Invoking it from method1 does not go through Spring's proxy, i.e. Spring is unaware of SomeException thrown out of method2. Transaction is not marked for rollback in this case.
Suppose method2 is not annotated with #Transactional. Invoking it from method1 does go through Spring's proxy, but Spring pays no attention to exceptions thrown. Transaction is not marked for rollback in this case.
Annotating method2 with #Transactional(propagation = Propagation.REQUIRES_NEW) makes method2 start new transaction. That second transaction is marked for rollback upon exit from method2 but original transaction is unaffected in this case (no exception thrown when exiting from method1).
In case SomeException is checked (does not extend RuntimeException), Spring by default does not mark transaction for rollback when intercepting checked exceptions (no exception thrown when exiting from method1).
See all scenarios tested in this gist.
For those who can't (or don't want to) setup a debugger to track down the original exception which was causing the rollback-flag to get set, you can just add a bunch of debug statements throughout your code to find the lines of code which trigger the rollback-only flag:
logger.debug("Is rollbackOnly: " + TransactionAspectSupport.currentTransactionStatus().isRollbackOnly());
Adding this throughout the code allowed me to narrow down the root cause, by numbering the debug statements and looking to see where the above method goes from returning "false" to "true".
As explained #Yaroslav Stavnichiy if a service is marked as transactional spring tries to handle transaction itself. If any exception occurs then a rollback operation performed. If in your scenario ServiceUser.method() is not performing any transactional operation you can use #Transactional.TxType annotation. 'NEVER' option is used to manage that method outside transactional context.
Transactional.TxType reference doc is here.
Save sub object first and then call final repository save method.
#PostMapping("/save")
public String save(#ModelAttribute("shortcode") #Valid Shortcode shortcode, BindingResult result) {
Shortcode existingShortcode = shortcodeService.findByShortcode(shortcode.getShortcode());
if (existingShortcode != null) {
result.rejectValue(shortcode.getShortcode(), "This shortode is already created.");
}
if (result.hasErrors()) {
return "redirect:/shortcode/create";
}
**shortcode.setUser(userService.findByUsername(shortcode.getUser().getUsername()));**
shortcodeService.save(shortcode);
return "redirect:/shortcode/create?success";
}
All I know about this exception is from Spring's documentation and some forum posts with frostrated developers pasting huge stack traces, and no replies.
From Spring's documentation:
Thrown when an attempt to commit a transaction resulted in an unexpected rollback
I want to understand once and for all
Exactly what causes it?
Where did the rollback occur? in the App Server code or in the Database?
Was it caused due to a specific underlying exception (e.g. something from java.sql.*)?
Is it related to Hibernate? Is it related to Spring Transaction Manager (non JTA in my case)?
How to avoid it? is there any best practice to avoid it?
How to debug it? it seems to be hard to reproduce, any proven ways to troubleshoot it?
I found this to be answering the rest of question: https://jira.springsource.org/browse/SPR-3452
I guess we need to differentiate
between 'logical' transaction scopes
and 'physical' transactions here...
What PROPAGATION_REQUIRED creates is a
logical transaction scope for each
method that it gets applied to. Each
such logical transaction scope can
individually decide on rollback-only
status, with an outer transaction
scope being logically independent from
the inner transaction scope. Of
course, in case of standard
PROPAGATION_REQUIRED behavior, they
will be mapped to the same physical
transaction. So a rollback-only marker
set in the inner transaction scope
does affect the outer transaction's
chance to actually commit. However,
since the outer transaction scope did
not decide on a rollback itself, the
rollback (silently triggered by the
inner transaction scope) comes
unexpected at that level - which is
why an UnexpectedRollbackException
gets thrown.
PROPAGATION_REQUIRES_NEW, in contrast,
uses a completely independent
transaction for each affected
transaction scope. In that case, the
underlying physical transactions will
be different and hence can commit or
rollback independently, with an outer
transaction not affected by an inner
transaction's rollback status.
PROPAGATION_NESTED is different again
in that it uses a single physical
transaction with multiple savepoints
that it can roll back to. Such partial
rollbacks allow an inner transaction
scope to trigger a rollback for its
scope, with the outer transaction
being able to continue the physical
transaction despite some operations
having been rolled back. This is
typically mapped onto JDBC savepoints,
so will only work with JDBC resource
transactions (Spring's
DataSourceTransactionManager).
To complete the discussion:
UnexpectedRollbackException may also
be thrown without the application ever
having set a rollback-only marker
itself. Instead, the transaction
infrastructure may have decided that
the only possible outcome is a
rollback, due to constraints in the
current transaction state. This is
particularly relevant with XA
transactions.
As I suggested above, throwing an
exception at the inner transaction
scope, then catching that exception at
the outer scope and translating it
into a silent setRollbackOnly call
there should work for your scenario. A
caller of the outer transaction will
never see an exception then. Since you
only worry about such silent rollbacks
because of special requirements
imposed by a caller, I would even
argue that the correct architectural
solution is to use exceptions within
the service layer, and to translate
those exceptions into silent rollbacks
at the service facade level (right
before returning to that special
caller).
Since your problem is possibly not
only about rollback exceptions, but
rather about any exceptions thrown
from your service layer, you could
even use standard exception-driven
rollbacks all the way throughout you
service layer, and then catch and log
such exceptions once the transaction
has already completed, in some
adapting service facade that
translates your service layer's
exceptions into UI-specific error
states.
Juergen
Scroll a little more back in the log (or increase it's buffer-size) and you will see what exactly caused the exception.
If it happens not to be there, check the getMostSpecificCause() and getRootCause() methods of UnexpectedRollbackException- they might be useful.
Well I can tell you how to reproduce the UnexpectedRollbackException. I was working on my project, and I got this UnexpectedRollbackException in following situation. I am having controller, service and dao layers in my project.
What I did is in my service layer class,
class SomeServiceClass {
void outerTransaction() {
// some lines of code
innerTransaction();
//AbstractPlatformTransactionManager tries to commit but UnexpectedRollbackException is thrown, not here but in controller layer class that uses SomeServiceClass.
}
void innerTransaction() {
try {
// someDaoMethod or someDaoOperation that throws exception
} catch(Exception e) {
// when exception is caught, transaction is rolled back, outer transaction does not know about it.
// I got this point where inner transaction gets rolled back when I set HibernateTransactionManager.setFailEarlyOnGlobalRollbackOnly(true)
// FYI : use of following second dao access is wrong,
try {
// again some dao operation
} catch(Exception e1) {
throw e2;
}
}
}
}
I had this problem with Spring Framework 4 + Java 1.8 for this exception.
I solved it.
😊
I know that runtime exceptions rollbacks.
But in my case, project do not use runtime exceptions.
For example, we have code.
#Transactional
#Service
UserFacadeIml implements UserFacade
{
#Autowired
private UserService userService;
//throws UnexpectedRollbackException
//instead NotRuntimeEx
#Override
public void saveUser(User user)
throws NotRuntimeEx
{
return userService.saveUser(user);
}
}
I solved it, when add "Transactional(rollBackFor=NotRuntimeEx.class)" for facade.
It works, because Spring use proxy for facade and proxy for service.
When transactional rollback, proxy of service send context to facade.
But facade proxy does not understand, why transaction rollback.
#Transactional(rollBackFor=NotRuntimeEx.class)
#Service
UserServiceIml implements UserService
{
#Override
public void saveUser(User user)
throws NotRuntimeEx
{
throw new NotRuntimeEx(user);
}
}