Hibernate : throw exception in #PostUpdate - java

I have this code on an Spring/Hibernate webapp :
Entity :
#Entity
#Table(name = "ARTICLE")
#EntityListeners(ArticleEntityListener.class)
public class ArticleLocaliseBean extends EntiteTracee {
...
Listenner :
public class ArticleLocaliseEntityListener {
#PostUpdate
#PostPersist
private void checkQuantite(ArticleBean article) throws BusinessException {
if (article.getQuantiteStock() < 0) {
throw new BusinessException(exceptionMsg);
}
}
This code called after each update or persist on Article entity.
The problem is when an exception is thrown for negative quantity, hibernate convert the BusinessException on a RuntimeException and perform a rollback of the transaction.
java.lang.RuntimeException: xxx.exceptions.BusinessException: exceptionMsg.
at org.hibernate.ejb.event.ListenerCallback.invoke(ListenerCallback.java:53)
at org.hibernate.ejb.event.EntityCallbackHandler.callback(EntityCallbackHandler.java:94)
at org.hibernate.ejb.event.EntityCallbackHandler.postUpdate(EntityCallbackHandler.java:83)
at org.hibernate.ejb.event.EJB3PostUpdateEventListener.handlePostUpdate(EJB3PostUpdateEventListener.java:70)
at org.hibernate.ejb.event.EJB3PostUpdateEventListener.onPostUpdate(EJB3PostUpdateEventListener.java:62)
at org.hibernate.action.EntityUpdateAction.postUpdate(EntityUpdateAction.java:199)
How can I makes Hibernate throw a checked exception an not a Runtime ? I don't want a rollback of the transaction when an exception is thrown.
Thanks.

The exception is thrown upon call on an API method, such as persist(). These methods however are not declared to throw exceptions (there is no throws clause in their signature). Hibernate has to wrap any checked exception in a runtime exception to respect method's signature.
To avoid rolling back, you can catch the runtime exception and check for its cause.
try {
em.persist(entity);
} catch (RuntimeException e) {
if (e.getCause() instanceof BusinessException) {
// Fix the problem the way you want
} else {
throw e;
}
}

Related

#Transactional not working when i throw exception on next line

I don't understand the below behavior:
I have a method:
#Transactional
public void processRejection(final Path path) {
try {
//some code here
} catch (final Exception e) {
this.handleException(e));
}
}
which calls the below which does a saves an entity which doesn't yet exists in the database:
void handleException(final Throwable e) {
this.filesMonitoringJpaManager.save(someEntityHere);
throw new Exception(...)
}
Now the strange is when I comment the throw new Exception(...) the save works, but when I uncomment throw new Exception(...) then the save doesn't work and I have no clue why?
What strange behavior is that from JPA or Hibernate? Is it something about Java Exception mechanism which I don't understand?
#Transactional is meant to roll back when something goes wrong (an exception is thrown). You're saving an entity in the catch block, but you're rethrowing an exception causing the transactional method to roll back.
But you can specify an exception, that will not cause rollback:
#Transactional(noRollbackFor = {MyException.class})
public void processRejection(final Path path) {
try {
//somecode here whatever
} catch (final Exception e) {
this.handleException(e));
}
}
void handleException(final Throwable e) {
this.filesMonitoringJpaManager.save(someEntityHere);
throw new MyException(...)
}
This works for org.springframework.transaction.annotation.Transactional. If you're using javax.transaction.Transactional, then you can achieve it by using dontRollbackOn property.

Java exception thrown in a "try" block cannot get caught outside of the "try...catch"

I am using my own personalized java exception DaoException (inherited from java.lang.Exception) that I throw inside of a try block inside of a method. The problem is that though I only catch other types of exceptions (such as java.sql.SQLException), it seems that the DaoException cannot get outside of the block as well. When I use my method elsewhere, I cannot catch my DaoException, as if it never was thrown.
I have no compilation or execution errors, so I am quite confused, especially because when throwing my DaoException outside of the try block, it works normally.
I could not find anyone who had the same problem, so I really hope to find some help here.
Here is the structure of my method:
public Member getById(int id) throws DaoException
{
...
try
{
...
if (id<0){
throw new DaoException("Indice de membre invalide");}
...
}
catch(SQLException e)
{
throw new DaoException("[SQLException]"+e.getLocalizedMessage());
}
finally
{
return member;
}
}
And here is my DaoException, which is quite basical:
public class DaoException extends Exception
{
public DaoException(String message)
{
super(message);
}
}
You have a return in your finally block. That means you're throwing away the exception when it happens so that you can return instead.
If you want the exception to survive, don't return in the finally block.

EjbConetxt setRollbackOnly vs ApplicationException rollback true

I have a method which throws an application exception with rollback true. I wonder if I have to explicitly call ejbContext.setRollbackOnly() or not?
The docs here says that we need not call the EjbContext's setRollbackOnly when the exception is marked as rollback=true.
In my case I catch a ApplicationException whose rollback attribute is set to true. Then I explicitly call ejbContext.setRollbackOnly() because I throw another exception in the catch block and this exception will be propagated to the client. Below is my code snippet
try {
....
} catch (XYZDirectoryException e) { // ApplicationException marked as rollback=true
ejbContext.setRollbackOnly(); // Is this necessary?
// log exception trace
throw new ServerInternalException("Something bad happened. Please try again or contact administrator. Check logs for more details"); // ApplicationException marked as rollback=false
}
An exception will not cause a rollback if it's thrown and caught inside a EJB transactional method. It has just to be thrown from a EJB transactional method in order to be caught by the transactional proxy around the EJB instance, otherwise, the container is completely unaware of the exception and thus won't rollback:
public void someEjbTransactionalMethod() {
// will cause a rollback
throw new XYZDirectoryException();
}
public void someOtheEjbTransactionalMethod() {
// will NOT cause a rollback
try {
throw new XYZDirectoryException();
}
catch (XYZDirectoryException) {
...
}
}
So, in your case, since the exception doesn't cross the boundary of any transactional EJB method, the container won't be aware of this exception, and won't rollback, unless you throw another "rollback=true" exception from the catch block, or explicitely mark the transaction as rollbackOnly.

change method to Runtime Exception in java

I have a method, in which right now I am handling the exception using the try catch method.
I have a custom exception method to handle the error.
Now I have to change this exception handling to runtime exception.
Code:
public class AppException extends RuntimeException {
private static final long serialVersionUID = -8674749112864599715L;
public AppException() {
}
public AppException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public AppException(String message, Throwable cause) {
super(message, cause);
}
public AppException(String message) {
super(message);
}
public AppException(Throwable cause) {
super(cause);
}
}
Method which handled using try catch.
#Transactional(readOnly = false)
#Override
public String save(StagingDocument stagingData)
throws AppException {
String enrichObjectId = null;
try {
EnrichDocument document = getEnrichDocument(stagingData);
EnrichDocument enrichPayload = enrichStagingDocumentRepository
.save(document);
enrichObjectId = enrichPayload.getId().toString();
} catch (Exception e) {
logger.error("EXCEPTION IN SAVETOENRICHDOCUMENT METHOD: " + e);
throw new AppException (e.getMessage(), e.getCause());
}
return enrichObjectId;
}
Here in the above method is the implementation when AppException extends the Exception class.
Now I need to change the save method according to runtime exception handling.
Question:
How can I change this method,without using try catch method?
If try catch is not there how the exception is get handled?
RuntimeExceptions don't have to be declared in the method signature, and if you want to remove the try/catch blocks as well - you can do it:
#Transactional(readOnly = false)
#Override
public String save(StagingDocument stagingData) {
String enrichObjectId = null;
EnrichDocument document = getEnrichDocument(stagingData);
EnrichDocument enrichPayload = enrichStagingDocumentRepository
.save(document);
enrichObjectId = enrichPayload.getId().toString();
return enrichObjectId;
}
When there's no try/catch - the exception doesn't get "handled" it is rather getting cascaded until either a higher level handles it or until the highest level exists the program (with the RuntimeException).
If you don't use a try-catch block, the original Exception would not be caught. If it is a RuntimeException, you will get no compilation errors as RuntimeExeption does not need to be caught. If that Exception occurs, it will simply be thrown from the save() method (it will be delegated).
If the original Exception is not a RuntimeException and you don't want to use a try-catch block, you may declare the save() method to throw that Exception but in this case it will obviously be not an instance of AppException but the original exception itself.
Btw, if you create an AppException, its cause should be e and not e.getCause(). If you pass e.getCause() as the cause of your AppException, then e itself will be lost. You might also want to add a custom error message and not use the original exception's message.
Summary: if you want your save() method to throw an instance of AppException if an Exception is encountered inside, you can't do that without a try-catch block, you have to catch it (the Exception encountered inside) and wrap it in a new AppException like you did.
Runtime exceptions need not be caught. If you are using AppException as a checked exception, still you need not put try/catch since you have already handled it using throws in the method signature. The method which is calling save() will have to handle the AppException.

EJB 2.0 CMP : exception scenario in multiple remove calls

I have EJB 2.0 legacy code. It has a session bean:
/**
* #ejb.bean
* name="Sample"
* type="Stateless"
* view-type="both"
* #ejb.transaction
* type="Required"
* #ejb.util
* generate="physical"
*/
public abstract class SampleEJB
implements SessionBean {
public void delete(Long id) {
EJBLocalObject local_o = getEjbLocalObject(id);
invokeDelete(local_o);
}
private void invokeDelete(EJBLocalObject local_o)
throws Exception
{
try
{
...
EJBLocalObject local_another_o = getEjbLocalObject(local_o.getAnotherId());
local_another_o.remove();
...
}
catch (Exception e)
{
// log exception
// throw new exception
}
try
{
...
local_o.remove();
...
}
catch (Exception e)
{
// log exception
// throw new exception
}
}
Sometimes due to issues in database, first remove call is successful. But second remove call fails and throws the exception.
This creates inconsistencies in database. Requirement is, all the remove calls should be successful. If one of the call fails, it should rollback the previous removes. How to handle such scenarios?
In case of BMT we can demarcate transactions for start, commit and rollback. But its CMT, so I am not sure how to handle this situation in EJB2.0. Please let me know what can be done.
#ejb.transaction * type="Required"
Assuming that this means the ejb transaction attribute configured is Requiered, the Container enforces that every call to delete() business method executes
within a transaction.
Therefore, to demarcate the transaction boundary is not a problem, you can be sure that both delete operations execute in the same transaction.
What you need is to mark the transaction for rollback if one delete operation fails.
The easier way to do this is that your business method throws a System exception.
} catch (Exception e) {
//log exception
throw new EJBException();
}
When the Container detects that a System exception (in ejb2.x this is exclusively an exception that extends from RuntimeException class) was thrown,
it automatically marks the transaction for rollback. However, when an Application Exception (an exception that extends from Exception) is thrown,
the Container doesn't change the transaction state.
In your case, it seems to be that delete() throws an Application Exception.
Other alternative is to explicitly marks the transaction for rollback using the SessionContext.setRollbackOnly() method.
//bean atribute
private SessionContext context;
//bean method call by the Container
public void setSessionContext(SessionContet ctx) {
context = ctx;
}
//your first delete code
try {
...
EJBLocalObject local_another_o = getEjbLocalObject(local_o.getAnotherId());
local_another_o.remove();
...
} catch (Exception e) {
context.setRollbackOnly();
//log exception
//throw new Exception
}
//idem for your second delete

Categories