In the service layer, I have some method who have a transactional annotation.
#Transactional
public void process() throws ProcessPaymentException{
try{
.... do some operation
catch (ProcessPaymentException ppe) {
save db problem issue.
}
}
It seem like if there are a issue, there are roll back... and nothing is saved in the db...
ProcessPaymentException extend Exception
Is there a way to rollback the process in the try but do the save in the catch?
Edit
Nested transaction could be a solution if this link is ok
https://www.credera.com/blog/technology-insights/java/common-oversights-utilizing-nested-transactions-spring/
Existing answer of using ControllerAdvise should help in normal setup that incoming requests are coming through Spring MVC (i.e. through a Controller).
For cases that is not, or you do not want to tie your exception handling logic to Spring MVC, here are some alternatives I can think of
(Here I assume you want to rely on declarative transaction control instead of programmatically controlling transactions yourself)
Separate service/component to save error in different transaction.
In short, you can have a separate service, which create its own transaction by propagation REQUIRES_NEW. e.g.
#Service
public class FooService
#Inject
private ErrorAuditService errorAuditService;
#Transactional
public void process() throws ProcessPaymentException{
try{
.... do some operation
catch (ProcessPaymentException ppe) {
errorAuditService.saveErrorAudit(ppe.getErrorText());
throw ppe; // I guess you want to re-throw the exception
}
}
}
#Service
public class ErrorAuditService
#Transactional(propagation=REQUIRES_NEW)
public void saveErrorAudit() {
// save to DB
}
}
One step further, if the error handling it the same for different services, you may create an advise, which will be called when service method throws exception. In that advise, you can save the error in db (using ErrorAuditService), and rethrow the exception.
Because processes of try-catch are wrapped by the same transaction.
The transaction manager do rollback whenever an exception is thrown. So, not thing would be saved.
Is there a way to rollback the process in the try but do the save in the catch?
Yes. Create Exception Handler to save db problem issue after rollback.
this is the idea
#ControllerAdvice
public class HandlerName {
#ExceptionHandler(ProcessPaymentException.class)
public void saveDbIssue(ProcessPaymentException ex) {
// save db problem issue.
}
But it only works if u want to save static data.
Related
I have a simple listener with 3 methods. and a repository with autowired on that. While saving an object from afterWrite it works nicely. but when saving item from onError methods no exception occurs, however it is not saving any data. Thankful for suggestions.
public class WriteListener implements ItemWriteListener{
public void beforeWrite(List items) {
System.out.println("Going to write following items: "+ items.toString());
}
public void onWriteError(Exception exception, List items) {
System.out.println("Error occurred when writing items!");
testRepository.save(items.get(0)); //not working
}
public void afterWrite(List items) {
testRepository.save(items.get(0)); //not nicely and save data
Based on the limited information provided, most likely the cause is the exception itself. The exception would have marked current transaction as dirty thus spring would have rolled it back.
If you still want to store data in your listener despite existing exception, use it in a separate transaction context. Simplest way for that would be to use #Async annotation on your listener and marking it Transactional explicitly to ensure it initiate a new transaction. Check out Spring Event which covers this topic in little bit more depth.
I am doing few hibernate save operations in spring's transactional service class.
My expectation is that by the time method execution finishes hibernate should write data to database.
But hibernate is actually executing those queries only when controller is about to return a response.
My sample code is as follows.
#Controller
public class TestController {
#Autowired
private SaveService saveService;
#RequestMapping(value = "saveData", method = RequestMethod.POST)
public String saveData(HttpServletRequest request, Model model) {
try {
saveService.saveData(object1, object2, object3); // LINE 1
sendEmail(); // LINE 2
//some code here // LINE 3
} catch(Exception e) {
//log exception
//return error message
}
}
}
#Service("saveService")
#Transactional
public class SaveServiceImpl implements SaveService {
#Autowired
private SaveDAO saveDAO;
public void saveData(Object objec1, Object objec2, Object objec3) {
saveDAO.save(object1);
saveDAO.save(object2);
saveDAO.save(object3);
}
}
In above code I am calling SaveService.saveData() method from controller. And if save is successful I want to go ahead and send an email. But if for some reason SaveService.saveData() throws an exception i don't want
to send the email.
When I performed some tests it was observed that even if SaveService.saveData() throws an exception it's not thrown
until the email is sent from controller. I want that if a call to saveService.saveData() at 'LINE 1' in controller
throws an exception the next line that sends email should not get executed.
I wanted to know if this is expected hibernate behavior and if it is what can I do to tell hibernate to execute
queries before exiting service methods.
Thank you.
This behavior is due to hibernate optimizations. Hibernate will wait until the last time possible to execute the sentences.
You can avoid this with session.flush(), Flushing the session forces Hibernate to synchronize the in-memory state of the Session with the database (i.e. to write changes to the database).
The problem here is when an exception occurs, your the variables/objects are not initialized in the catch block and you are accessing it.
As it looks like you have just added a snippet of the code in question, so I guess the variables object1, object2, object3 needs to initalized to null.
for example: object1=null
My Spring application is layered as Bean, Service and DAO. All the #Transactional annotations are in service layer.
This is the pseudo code in one particular scenario.
UserBean.java
saveUser() {
userService.manageUser();
}
UserServiceImpl.java
#Transactional
public void manageUser() {
userDAO.createUser();
userDAO.updateParentUser();
}
UserDAOImpl.java
createUser() {
//insert user record in database
}
updateParentUser() {
//update parent user's database record
}
In my save user test case, the update parent user operation can fail in some cases due to primary key violation which is kind of expected.
As the #Transactional annotation is implemented in service class, this violation exception will be notified in bean class only.
What is the option to get this PK violation notification in my service class?
[Then I can handle it from there in a different business process.]
If I add a new method in service class and call manageUser() from there the #Transactional annotation will not work properly. This is due to the limitation/property of AOP. Spring expects external call to #Transactional methods.
The create/update won't be committed until you return from the #Transactional method. If the create/update is flushed to the database before that then you may get the exception within the method, but in your case it's not being flushed until the commit.
You can force the create/update to be flushed before the commit. You don't say whether you're using Hibernate or JPA, but session.flush() or entityManager.flush() should do the trick.
Use programmatic transaction management and handle exceptions in try catch block
Introduce a delegate class and do manageUser in a transaction there:
#Transactional(REQUIRED)
public void manageUser() {
try{
delegate.manageUser();
} catch (Exception ex ) {
//handle
}
}
And in delegate class
#Transactional(REQUIRES_NEW)
public void manageUser() {
}
Instead of Spring proxy based AOP I moved to AspectJ approach. This gives me the flexibility to make my manageUser() method call from another method of same service class and I can handle the exception there.
I have the following classes:
public interface GarageRepository extends PagingAndSortingRepository<Garage, Integer> {}
public class GarageBO {
private GarageRepository garageRepository;
public void updateGarage(Garage garage) {
try {
garageRepository.save(garage);
} catch (Exception e) {
throw BoozinaExceptions.getCodeException(garage, e);
}
}
}
public class GarageFacade implements GarageService {
private GarageBO garageBO;
#Transactional
public void updateGarage(Garage garage) {
garageBO.updateGarage(garage);
}
}
Supposing that i'm trying to update a garage and an unique violation is throwed.
When i call updateGarage from GarageFacade with the #Transactional annotation, garageRepository doesn't throws any exception.
When i call the same method without the #Transactional annotation, garageRepository throws the unique violation exception and now i can convert the exception using BoozinaExceptions.getCodeException(garage, e);
This behavior happens because when i have the #Transactional annotation, the exception will be throwed when Spring jpa data execute the commit. This happens after GarageBO.updateGarage execution ok ?
But i need to convert the unique violation. How can i do that ?
How to handle spring exception after commit then convert to my exception ?
What you describe is the expected behavior. The transaction is committed after the method ends, the commit leads to the unique constraint violation.
Adding #Transactional to the service methods makes the transaction end after the call to the service method the transaction is committed. Removing it makes the transaction commit after the call to the repository method.
Also why do you have a BO a Facade and a Repository?! Basically the BO and Facade are the same IMHO.
TO fix your your problem have your GarageRepository extend JpaRepository instead of PagingAndSortingRepository and call the saveAndFlush method instead of save. This will execute the sql (not committing the transaction) and trigger a constraint violation exception.
Another solution is to instead of doing a try/catch in your BO create and Aspect which does the conversion. Saves you coding the try/catch each time you need it.
I'm using Glassfish 3.1 with JSF2 and EJB stateless to query and write an Oracle DB. The table the user wants to populate in this web application has a primary key. When the user tries to add a new record the ejb method invoking em.persist is called. Now, if the user tries to add a record that has an already used primary key value, I got an exception in the EJB.
I would like to pop up a message to the user indicating that an error in the database occurred but I can't figure out how the JSF managed bean could catch the EJB exception.
Is there any way?
EJB has the concept of system exceptions and application exceptions.
Runtime exceptions, like EntityExistsException are system exceptions. These will among others cause any transaction to be rolled-ed back and cause the EJB instance bean to be discarded (destroyed). Most importantly for your problem, they will be wrapped in an EJBException.
There is no magic surrounding catching these exceptions. Adjusting the code from Petr above,
the following will just work:
Backing bean:
#EJB
private DAOBean daoBean;
public void savePerson(Entity e) {
try {
daoBean.save(e);
} catch (EJBException e) {
FacesMessage message = new FacesMessage("entity is already exists.");
FacesContext.getCurrentInstance.addMessage(null, message);
}
}
EJB:
private EntityManager em;
public void save(Entity e) {
em.persist(e);
}
Note that you can retrieve the cause of the exception to see if was an EntityExistsException or not (omitted above for brevity).
Since you probably have no need to destroy your EJB instance for this case, a better pattern is to define your own exception that inherits from a RuntimeException and is annotated with the #ApplicationException with the rollback attribute set to true.
E.g.
#ApplicationException(rollback = true)
public class MyException extends RuntimeException {
public MyException(Throwable cause) {
super(cause);
}
}
Wrap your EntityExistsException in your EJB into this exception and throw and catch it.
I strongly advise you NOT to use error codes or boolean success/failure as a result. This is a well-known anti pattern and makes your code incredible error prone.
You can create a custom exception class. Let's say UserException with a enum values of possible exception option.
In you EJB you can define your methods as throwable. If you need throw a exception.
In your JSF-SiteBean you only need to use a simple try/catch.
Is exception from type UserException ... get enum reason ... etc.