How to handle exceptions using EntityManager in Spring Boot - java

I have dao layer:
#Transactional
public class DatabaseCollectionDao implements IDatabaseCollectionDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public void create(Collection collection) {
entityManager.persist(collection);
}
}
It works correctly but:
When database isn't available I have SocketException.
When database contains a duplicate key I have SQLIntegrityConstraintViolationException
I am trying to try/catch it inside create method:
#Override
public void create(Collection collection) {
try{
entityManager.persist(collection);
} catch (SQLIntegrityConstraintViolationException e){
//do smth
}
}
But Intellij says that It is never thrown.
When I try to try/catch Exception i have UnexpectedRollbackException.
How to handle exceptions using JPA entityManager?
update: An attempt to remove #Transactional gave nothing
P.S. To be sure i tried to try/catch it in higher layers. I don't know what i can try more to solve it.

create customException handler extend ResponseEntityExceptionHandler . #ExceptionHandler(ConstraintViolationException::class) fun handleConstraintViolation(ex: ConstraintViolationException, request: WebRequest): ResponseEntity {} this kotlin snippet u can convert to java easily –

Related

Spring boot #Transactional not rolling back the database inserts

Need some help here, I'm not able to understand why my transactions are not getting rolled back in an event of exception.
I will try to put my code as close to as It is on the project (cannot share on the internet)
This is my Service
#Service
#SL4j
#Transactional(propagation = propagation.SUPPORTS, readOnly=true)
public class PublicationServiceImpl implements PublicationService{
#Autowired
private PartnerRepo partnerRepo;
#Autowired
private FlowRepo flowRepo;
#Autowired
private PubRepo pubRepo;
#Override
#Transactional(propagation = propagation.REQUIRED, rollbackFor=Exception.class)
public int save(Request request) {
try{
int pk_id_partner = partnerRepo.save(request);
int pk_id_flow = flowRepo.save(request);
String publicationCode = generatePubCode(request);
int publicationCode= pubRepo.save(pk_id_partner, pk_id_flow, request);
}
catch(Exception e){
log.error("Exception in saving");
}
return 0;
}
}
This is my Repository (example of 1 , all 3 repos follow same coding standards)
#Repository
#Slf4j
public class PartnerRepo implemets PartnerRepo{
#Autowired
private NamedParamaterJDBCTemplate namedParamaterJDBCTemplate;
//String Declarations .....
private MapSqlParameterSource sqlParameterSource;
#Override
public int save(Request request){
sqlParamatersSource = new MapSqlParameterSource();
//sqlParamatersSource.addValue(.....)
//sqlParamatersSource.addValue(.....)
//sqlParamatersSource.addValue(.....)
return executeQuery();
}
private int executeQuery(){
try{
keyHolder = new GenerateKeyHolder();
namedParamaterJDBCTemplate.update(getInsertQuery(), sqlParamaterSource , kekHolder, new String[]{"pk_id"})
return keyHolder.getKey().intValue();
}catch(Exception e){
log.error("Exception while saving");
return 0;
}
}
}
So the problem is , Consider there is an exception in the method generatePubCode(request); , ideally since I have used #Transactional at class level and method level , The previous 2 repo transactions () should be rolled back right? However it isn't happening, Even After the code is finished execution I can see the records in DB (Postgres DB v10).
Please help figure out this issue , Am I doing something fundamentally wrong ?
Please do let me know in case you need further information that might help here!
P.S: I have tried all permutations of #Transactional , nothing works : ONLY having this in the catch block works! TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
I wonder If its the right approach for a springBoot project
Thanks in advance for the help!
Edit: as per suggestion made the PublicationServiceSaverImpl.save() public
Best reagards,
Bhargav.
There are several things that break proper transactions in Spring
Your service method is private
You are catching and swallowing exceptions
private method
The fact that your PublicationServiceImpl save method is private basically makes the #Transactional on that method useless. As a private method cannot be proxied, no transactions will apply. Even if it would be public it wouldn't work as you are calling the method from within the same object, hence the transactionality of that method applies.
To fix, make your method public and call the save method from an other class (or make the actual method that is calling save have the proper #Transactional.
The fact that is doesn't work is due to the type op AOP being used, by default Spring will use proxies and this is a drawback of using proxy based AOP.
Another solution to make it work with private methods is to switch to full-blown AspectJ with either compile-time or load-time weaving of the classes. Both require additional setup and that can be tedious.
Catch and swallow exceptions
You have in both your repository as well as your service a try/catch block. Each of those catches and swallows the exceptions (they are logged but not re-thrown).
For transactions to work properly it needs to see the exceptions. The fact that you are catching and swallowing them, makes the transaction aspect not see them and instead of doing a rollback, do a commit. For the transaction aspect everything is ok because there was no exception.
To fix, remove either the try/catch or rethrow the exceptions.
Annotations in general never work on methods called from the same class because of how proxies are created in Spring.
It has nothing to do with #Transaction in particular but with the fact that your methods is private and called from the within same object.
Please make the method public and move the #Transactional method in a separate class annoted with #Service and called it from outside of the instance of the class
#Service
public class PublicationServiceSaverImpl {
#Transactional
**public** int save(Request request) {
...
}
}
You must call the save method from outside of the class PublicationServiceSaverImpl, maybe from PublicationServiceImpl.
The method PublicationServiceImpl.save must be public if you want to use #Transactional.
As per Spring Documentation:
When you use transactional proxies with Spring’s standard configuration, you should apply the #Transactional annotation only to methods with public visibility. If you do annotate protected, private, or package-visible methods with the #Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings.
First of all: make your method public.
Second: you have to throw the exception. If you catch and not rethrow it, how do you expect the transactional processing to know that an error occured and then rollback?
You have two options: throw the Exception instead of catching it, or catch, do some further processing and then rethrow it.
So in your repository, just add a throws keyword and then rethrow the exception after the log statement:
public int executeQuery() throws Exception {
try {
keyHolder = new GenerateKeyHolder();
namedParamaterJDBCTemplate.update(getInsertQuery(), sqlParamaterSource, kekHolder, new String[] {
"pk_id"
})
return keyHolder.getKey().intValue();
} catch(Exception e) {
log.error("Exception while saving");
throw e;
}
}
Now, for your service:
Example 1 - use the throws keyword to propagate the checked exception:
#Override
#Transactional(propagation = propagation.REQUIRED, rollbackFor = Exception.class)
public int save(Request request) throws Exception {
int pk_id_partner = partnerRepo.save(request);
int pk_id_flow = flowRepo.save(request);
String publicationCode = generatePubCode(request);
int publicationCode = pubRepo.save(pk_id_partner, pk_id_flow, request);
return 0;
}
Example 2 - catch and rethrow it as an RuntimeException, which is unchecked.
#Override
#Transactional(propagation = propagation.REQUIRED)
public int save(Request request) {
try {
int pk_id_partner = partnerRepo.save(request);
int pk_id_flow = flowRepo.save(request);
String publicationCode = generatePubCode(request);
int publicationCode = pubRepo.save(pk_id_partner, pk_id_flow, request);
} catch(Exception ex) {
throw new RuntimeException(ex);
}
return 0;
}
Note that the second example doesn't need the rollbackFor argument to the #Transactional. By default, a transaction is rolled back if a unchecked exception occurs, so there's no need to explicitly use rollbackFor in cases of RuntimeExceptions.
Another verification that would have to be done if the solution does not work. It is to verify that the database tables allow the rollback. For this, the engine has to be in InnoDB and not in MyISAM and others.
In my case adding #EnableTransactionManagement annotation on Application class resolved the issue
In stead of #Transactional(propagation = propagation.REQUIRED provide #Transactional(propagation = propagation.REQUIRED_NEW
If you use the latter, it will use the parent transaction boundary, which is at class level.
And you don't need explicitly state rollbackFor=Exception.class. By default spring will roll back on exception
And do change private to public
Try this out

TransactionTemplate with rollbackFor

I had several #Transactional private methods in my service and since #Transactional is not recommended over private methods I fixed them using org.springframework.transaction.support.TransactionTemplate. Refactoring simple #Transactional was easy but I have one transactional method with rollbackFor attribute like this:
#Transactional(rollbackFor = ProcessingAlertException.class)
private void processAlert(Alert alert) {
// do something
}
I don't exactly know, how to refactor it. org.springframework.transaction.TransactionStatus doesn't seem to have any suitable method.
Since you already decided to use TransactionTemplate, you can just call TransactionStatus.setRollbackOnly() for the given exception type within your TransactionCallback:
transactionTemplate.execute(transactionStatus -> {
try {
...
} catch (ProcessingAlertException ex) {
transactionStatus.setRollbackOnly();
}
});

Spring transactions with caught Exceptions

Recently, I've been working with Spring boot + spring data jpa + hibernate. I faced one problem with spring transactions. Here is my service class and two questions:
#Transactional
#Service
class MyService {
#Autowired
private MyRepository myRep;
public void method_A() {
try {
method_C();
.....
method_B();
} catch(Exception e) {}
}
public void method_B() {
Entity e = new Entity();
e.set(...);
myRep.save(e);
}
public void method_C() throws Exception {
....
}
}
1.If method method_C() throws an Exception and I want to catch it and log it, the transaction is not rollbacked in method method_B(), because the Exception does not reach Spring framework. So how should I do in order to catch Exceptions from method_C() and at the same time do not lose capability of method method_B() be rollbacked?
2.Consider new method method_A().
public void method_A() {
for(...) {
...
...
method_B();
}
}
I want invoke method_B() in a loop. If an exception occurs in a method_B() I want transaction of method_B() be rollbacked but method_A() should not exit and the loop should continue excuting. How can I achieve this?
I solved my 2 problems this way: created another #Service class and moved method_B() into it. I've annotated this class as #Transactional. Now the method method_A() looks like this:
public void method_A() {
for(...) {
...
try {
anotherService.method_B();
} catch (Exception e) {
logger.error(...);
}
}
}
If RuntimeException occurs in the method_B() method, the exception is propertly logged, transaction of method_B() is rollbacked and the loop continuous. Thanks everybody for responses.
Instead of throwing exceptions do the following. (return error code).
Update: I read your question after posting. if you call method_b from method_A both are under same transaction. Unfortunately you cannot rollback the method_b changes alone. Spring considers it as one transaction if they are all under one service class. (all methods).
One thing you can try is the following.
request to--> Controller() ---> (spring opens transaction) service_method_a(); (spring closes transaction)
Controller() ---> (spring opens transaction) service_method_c(); (spring closes transaction)
Controller() ---> (spring opens transaction) service_method_b(); (spring closes transaction)
return <--
I hope it makes sense
Each of your methods a,b,c throw exceptions if it likes to be rolledback.
Update:
another approach. This one is much better.
If each of your method are in a different service then you can use the following annotations of the spring to run each of the method in a different transaction boundaries
p v serviceA{
#transactional
method_a(){
serviceb.method_b();
}
}
p v serviceB{
#Transactional(propagation=Propagation.REQUIRED)
method_b(){
}
}
more on it here
Spring transactional story here . Read the points below this article. Those are most important when developing the spring transactional app.

Spring MVC: bind an exception handler to particular method

Good day!
I have a #Controller. Some of its methods throw the same exception, but I want to handle this exceptions in different way.
Is there a way how to bind an #ExceptionHandler to a particular method?
You need to use AOP tools like CDI Interceptor or AspectJ to achieve this cross-cutting concerns. A Concern is a term that refers to a part of the system divided on the basis of the functionality.
Basically this type of feature is used to handle logging, security and also handling the errors... which are not part of your business logic...
Like if you want to change the logger for application from log4j to sl4j then you need to go through each and every classes where you have used log4j and change it. But if you have used AOP tools then you only need to go the interceptor class and change the implementation. Something like plug and play and very powerful tool.
Here is a code snippet using JavaEE CDI Interceptor
/*
Creating the interceptor binding
*/
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface BindException {
}
After we have define interceptor binding we need to define interceptor binding implementation
/*
Creating the interceptor implementation
*/
#Interceptor
#BindException
public class ExceptionCDIInterceptor {
#AroundInvoke
public Object methodInterceptor(InvocationContext ctx) throws Exception {
System.out.println("Invoked method " + ctx.getMethod().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// code for Exception handler
}
}
}
Now we only need to apply interceptor to our method
/*
Some Service class where you want to implement the interceptor
*/
#ApplicationScoped
public class Service {
// adding annotation to thisMethodIsBound method to intercept
#BindException
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionCDIInterceptor
}
}
You can achieve same feature using AspectJ also.
/*
Creating the Aspect implementation
*/
#Aspect
public class ExceptionAspectInterceptor {
#Around("execution(* com.package.name.SomeService.thisMethodIsBound.*(..))")
public Object methodInterceptor(ProceedingJoinPoint ctx) throws Throwable {
System.out.println("Invoked method " + ctx.getSignature().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// codes for Exception handler
}
}
}
Now we only need to enable the AspectJ to our application config
/*
Enable the AspectJ in your application
*/
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public SomeService SomeService() {
return new SomeService();
}
}
/*
Some Service class where you want to implement the Aspect
*/
package com.package.name;
public class SomeService {
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionAspectInterceptor
}
}
I have code example in my git repo https://github.com/prameshbhattarai/javaee-exceptionBinding by using CDI interceptor.
Just as an option (obviously, it's not ideal): you can wrap the exception into a custom exception in one of your methods and then catch it in an #ExceptionHandler
void boo() throws WrappingException {
try {
} catch (TargetException e) {
throw new WrappingException(e);
}
}
then
#ExceptionHandler(WrappingException.class)
public void handleWrappingException() {
// handle
}
#ExceptionHandler(TargetException.class)
public void handleTargetException() {
// handle
}
Could you please explain why do you need this? I'm asking out of curiosity, because I've never felt like this is required and here is why:
Exception usually represents a very specific "mistake" - something that went wrong in a very specific way.
Basically, exception represents a mistake, not a flow...
There are two "degrees of freedom" that spring can support out of the box:
Exception parameters. Maybe stuff like error code, which can be declared as a data field of exception itself.
Exception inheritance. Example:
If you have in your system a UserDoesNotExistException and you want to be more specific in a case of say, the system that manages the users that are retired in some flows, you can always create a more specific exception:
class UserRetiredException extends UserDoesNotExistException {...}
Obviously, spring can support both the cases: In ExceptionMapper you have the access to the exception anyway so you can do something like:
handleException(SomeExceptionWithErrorCode ex) {
if(ex.getErrorCode() == "A") {
// do this
}
else if(ex.getErrroCode() == "B") {
// do that
}
}
In the second case you just have different exception mappers for the different types of exceptions.
You can also consider #ControllerAdvice annotation to reuse code or something.
I don't think you can specify a specific #ExceptionHandler for a method, but you can bind an #ExceptionHandler method to a specific Exception.
So if you want to handle all DataIntegrityViolationException one way and all other Exceptions in another you should be able to achieve that with something like this:
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleIntegrityViolation() {
// do stuff for integrity violation here
}
#ExceptionHandler(Exception.class)
public void handleEverythingElse() {
// do stuff for everything else here
}
You can derive sub-exceptions from the common exception thrown by other methods according to how you want to handle them.
Say you have declared the parent exception as ParentException. Derive sub classes like ChildAException extends ParentException, ChildBException extends ParentException etc.
Define a #ControllerAdvice class that catches the ParentException and define the specific behaviors in delegate methods.
#ControllerAdvice
public class ParentExceptionHandler {
#ExceptionHandler(ParentException.class)
public ResponseEntity<Object> handleParentException(ParentException pe) {
if (pe instanceof ChildAException) {
return handleChildAException((ChildAException) pe);
} else if (...) {
...
} else {
// handle parent exception
}
}
private ResponseEntity<Object> handleChildAException(ChildAException cae) {
// handle child A exception
}
}
I just got the same issue like you. So I checked the spring source code for this situation. It seems that spring will search in the #Controller class for any method that is annotated with #ExceptionHandler first, if nothing matched then it will continue to search for all class that is annotated with #ControllerAdvice. So you can just use the strategy below:
MyController with a #ExceptionHandler method:
#RestController
public class MyController {
#RequestMapping("/foo")
public String foo() {
throw new IllegalArgumentException();
}
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Specific handler", HttpStatus.BAD_REQUEST);
}
}
AnotherController without any method annotated with #ExceptionHandler:
#RestController
public class AnotherController {
#RequestMapping("/bar")
public String bar() {
throw new IllegalArgumentException();
}
}
A global #ControllerAdvice class:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Global handler", HttpStatus.BAD_REQUEST);
}
}
Then if you visiting http://ip:port/foo, you will get 400 status code with Specific handler, and 400 status code with Global handler when you visit http://ip:port/bar.
I agree that the inability to map a specific #ExceptionHandler to handle only one specific method in the #RestController should be a very desirable feature.
I tried try{}catch(Exception ex){} and catch no exception. But
exception handler handles it nicely.
Since we are talking about hibernate exceptions, these exceptions are usually thrown at the commit phase of transaction. The problem here is that seems like you have transaction opened right in your controller which is considered as a bad practice.
What you should do is - open transaction in the application layer.
Controller just maps xml/json to incomming RequestDto object.
And then you call the Service to handle the business logic.
The Service(or its method) should be annotated by #Transactional.
#RestController
public class MyController {
#Autowired // but better to use constructor injection
private MyService myService;
public ResponseDto doSomething(RequestDto request) {
try {
myService.doSomething(request);
} catch (DataIntegrityViolationException ex) {
// process exception
}
}
}
#Transactional
class MyService {
public void doSomething() {
// do your processing which uses jpa/hibernate under the hood
}
}
Once you done that, the try catch will start behaving as expected on controller level.
However, I would even go further as DatabaseExeption shouldn't really go that far to controller. The alternative would be to use manual transaction inside of a service and do a try catch there.
Then in the Service layer transform database exception in a more generic exception with all necessary information for controllers to process.
And then you should catch that more generic exception (MyDatabaseAccessException) in the controller and transform as you wish for the sake of a presentation layer.
===
The #ControllerAdvice suggested here is good for a global exception handling across controllers.
The #ExceptionHandler is not suitable for each method unless you wnat to have controller per method. And even after that it can clash with global #ControllerAdvice.
I am not sure why spring doesn't allow #ExceptionHandler at a method level, it would simplify a lot of cases like yours.
My solution is to annotate a method with a marker:
#ExceptionHandler(SomeException.class)
public ResponseEntity<String> handleSomeException(SomeException e, HandlerMethod handlerMethod) {
var marker = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getMethod(), MarkerAnnotation.class);
if (marker != null) return something();
else return somethingElse();
}

How to prevent JPA from rolling back transaction?

Methods invoked:
1. Struts Action
2. Service class method (annotated by #Transactional)
3. Xfire webservice call
Everything including struts (DelegatingActionProxy) and transactions is configured with Spring.
Persistence is done with JPA/Hibernate.
Sometimes the webservice will throw an unchecked exception. I catch this exception and throw a checked exception. I don't want the transaction to roll back since the web service exception changes the current state. I have annotated the method like this:
#Transactional(noRollbackFor={XFireRuntimeException.class, Exception.class})
public ActionForward callWS(Order order, ....) throws Exception
(...)
OrderResult orderResult = null;
try {
orderResult = webService.order(product, user)
} catch (XFireRuntimeException xfireRuntimeException) {
order.setFailed(true);
throw new WebServiceOrderFailed(order);
} finally {
persist(order);
}
}
I still get this exception:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
When I try to reproduce this with junit, the transaction isn't marked for roll back and it's still possible to commit the transaction.
How do I make Spring not to roll back the transaction?
Managed to create a test case for this problem:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"file:web/WEB-INF/spring/applicationContext.xml",
"file:web/WEB-INF/spring/services.xml"})
#Transactional
public class DoNotRollBackTest {
#Autowired FakeService fakeService;
#Test
#Rollback(false)
public void testRunXFireException() {
fakeService.doSomeTransactionalStuff();
}
}
FakeService:
#Service
public class FakeService {
#Autowired private EcomService ecomService;
#Autowired private WebService webService;
#Transactional(noRollbackFor={XFireRuntimeException.class})
public void doSomeTransactionalStuff() {
Order order = ecomService.findOrderById(459);
try {
webService.letsThrowAnException();
} catch (XFireRuntimeException e) {
System.err.println("Caugh XFireRuntimeException:" + e.getMessage());
}
order.setBookingType(BookingType.CAR_BOOKING);
ecomService.persist(order);
}
}
WebService:
#Transactional(readOnly = true)
public class WebService {
public void letsThrowAnException() {
throw new XFireRuntimeException("test!");
}
}
This will recreate the rollback-exception.
Then I realized that the transaction is probably being marked as rollbackOnly in WebService.letsThrowAnException since WebService is also transactional. I moved to annotation:
#Transactional(noRollbackFor={XFireRuntimeException.class})
public void letsThrowAnException() {
Now the transaction isn't being rolled back and I can commit the changes to Order.
You must not throw an exception where Spring can see it. In this case, you must not throw WebServiceOrderFailed(). The solution is to split the code into two methods. The first method does the error handling and returns the exception, the outer method creates the transaction.
[EDIT] As for noRollbackFor: Try to replace Exception.class with WebServiceOrderFailed.class.

Categories

Resources