I am using JpaRepository from Spring Data JPA framework. I have a snippet of code below:
#Repository
public interface PresetFolderRepository extends JpaRepository<PresetFolder, Integer>{
#Modifying
#Transactional
#Query("update PresetFolder pf set pf.parentId = :parentId where pf.id = :id")
int updateParentId(#Param("id") int id, #Param("parentId") int parentId);
}
When I invoke this method:
#Autowired PresetFolderRepository repo;
repo.updateParentId(1,2);
public void test(){
Do I have to surround it with a try-catch? How can I know if the self-defined method 'updateParentId' has try-catch implementation in it?
Thanks!
EDIT:
My concern is, if my database went down, does this method catch the exception.
Repositories will always tell you something if a problem happens (i.e. they never swallow exceptions). You'll always get a runtime exception if that's the case.
And you should probably not catch such an exception either, except at the very top of the call stack, where you have the possibility to display an error message to the end user.
No you don't need it to surround by try-catch block. Most of the Spring-Data repositories throw runtime exceptions. With respect to your concern, if the database is down then a runtime exception is generated and you can catch it at the controller level(if you are writing a web application).
The test case would throw out an error for unreachable host, if you are executing the test case when the DB is down.
Related
I am autowiring service in controller. And in service, I have a scenario where I need to throw an exception and DB changes also. So, I tried #Async
#Transactional
public void verifyOtp(OtpDto otpDto)
...
if(xyz){
deactivateOtp(emp.getId());
throw new ServException("Mobile no requested is already assigned", "error-code");
}
}
#Async
#Transactional //with or without
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
This is not creating new thread. But, if I gives at repo, it works
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Async
#Transactional
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
First of all check that the service is wrapped into proxy (you can place a breakpoint in controller and see the reference to the service, it will be with proxy). Otherwise there is something wrong with the configuration and #Transactional/#Async won't work till you fix that.
Now, assuming this is not an issue, there is an issue in the code:
When the controller calls service.verifyOtp it goes to the proxy (to handle the transaction) and then to your implementation.
But when it reaches your implementation and you call the method that belongs to the same impl, it doesn't pass through the proxy again, instead it directly goes to the deactivateOtp as if there is no spring at all here. Of course, #Async doesn't work.
In terms of resolution:
Consider using self injection if you work with spring 4.3+. Read this thread for more information.
Alternatively, refactor your code so that the deactivateOtp will be a public method of another class. In this case the call won't be "internal" anymore, it will path through Proxy hence the #Async will work.
This is discussed many times.
Basically, Spring AOP will not intercept local method call
(in your case call deactivateOtp() within the same class)
You can read more about this behavior here: Understanding AOP proxies
Highlight:
The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
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).
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.
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.