I have an EJB Stateless Session Bean, like this:
public void persist(Customer customer,Child child){
try{
em.persist(customer);
Father father = new Father();
father.setChild(child); here child is null
em.persist(father);
}catch(Exception e){
}
}
When the exception (NullPointerException) occurs the transaction is not rolling back and Customer entity is persisted, but when i catch the exception with
public void persist(Customer customer,Child child){
try{
em.persist(customer);
Father father = new Father();
father.setChild(child); here child is null
em.persist(father);
}catch(EJBException e){
}
}
the transaction is rolling back, but i donĀ“t understand why, NullPointerException extends RuntimeException.The docs say a RuntimeException cause a rollback.
In the second example the NullPointerException is not caught, instead, you are catching an EJBException which is other runtime exception class.
As you say, when the Container intercepts the NullPointerException, the transaction is marked for rollback.
The first example catches Exception, wich is a base class (the Java exceptions are hierarchical ),
therefore, any subclass of Exception such as NullPointer or EJBException
is caught. In this case the Container doesn't mark the transaction for rollback.
Related
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;
}
}
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.
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
I have this scenario:
fetch (read and delete) a record from IncomingMessage table
read record content
insert something to some tables
if an error (any exception) occurred in steps 1-3, insert an error-record to OutgoingMessage table
otherwise, insert an success-record to OutgoingMessage table
So steps 1,2,3,4 should be in a transaction, or steps 1,2,3,5
My process starts from here (it is a scheduled task):
public class ReceiveMessagesJob implements ScheduledJob {
// ...
#Override
public void run() {
try {
processMessageMediator.processNextRegistrationMessage();
} catch (Exception e) {
e.printStackTrace();
}
}
// ...
}
My main function (processNextRegistrationMessage) in ProcessMessageMediator:
public class ProcessMessageMediatorImpl implements ProcessMessageMediator {
// ...
#Override
#Transactional
public void processNextRegistrationMessage() throws ProcessIncomingMessageException {
String refrenceId = null;
MessageTypeEnum registrationMessageType = MessageTypeEnum.REGISTRATION;
try {
String messageContent = incomingMessageService.fetchNextMessageContent(registrationMessageType);
if (messageContent == null) {
return;
}
IncomingXmlModel incomingXmlModel = incomingXmlDeserializer.fromXml(messageContent);
refrenceId = incomingXmlModel.getRefrenceId();
if (!StringUtil.hasText(refrenceId)) {
throw new ProcessIncomingMessageException(
"Can not proceed processing incoming-message. refrence-code field is null.");
}
sqlCommandHandlerService.persist(incomingXmlModel);
} catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
// send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,
ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
// send success outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId, ProcessResultStateEnum.SUCCEED.getCode());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
}
private void saveOutgoingMessage(OutgoingXmlModel outgoingXmlModel, MessageTypeEnum messageType)
throws ProcessIncomingMessageException {
String xml = outgoingXmlSerializer.toXml(outgoingXmlModel, messageType);
OutgoingMessageEntity entity = new OutgoingMessageEntity(messageType.getCode(), new Date());
try {
outgoingMessageService.save(entity, xml);
} catch (SaveOutgoingMessageException e) {
throw new ProcessIncomingMessageException("Can not proceed processing incoming-message.", e);
}
}
// ...
}
As i said If any exception occurred in steps 1-3, i want insert an error-record:
catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
//send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
It's SqlCommandHandlerServiceImpl.persist() method:
public class SqlCommandHandlerServiceImpl implements SqlCommandHandlerService {
// ...
#Override
#Transactional
public void persist(IncomingXmlModel incomingXmlModel) {
Collections.sort(incomingXmlModel.getTables());
List<ParametricQuery> queries = generateSqlQueries(incomingXmlModel.getTables());
for (ParametricQuery query : queries) {
queryExecuter.executeQuery(query);
}
}
// ...
}
But when sqlCommandHandlerService.persist() throws exception (here a org.hibernate.exception.ConstraintViolationException exception), after inserting an error-record in OutgoingMessage table, when the transaction want to be committed , i get UnexpectedRollbackException. I can't figure out where is my problem:
Exception in thread "null#0" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at ir.tamin.branch.insuranceregistration.services.schedular.ReceiveMessagesJob$$EnhancerByCGLIB$$63524c6b.run(<generated>)
at ir.asta.wise.core.util.timer.JobScheduler$ScheduledJobThread.run(JobScheduler.java:132)
I'm using hibernate-4.1.0-Final, My database is oracle, and Here is my transaction-manager bean:
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
This is the normal behavior and the reason is that your sqlCommandHandlerService.persist method needs a TX when being executed (because it is marked with #Transactional annotation). But when it is called inside processNextRegistrationMessage, because there is a TX available, the container doesn't create a new one and uses existing TX. So if any exception occurs in sqlCommandHandlerService.persist method, it causes TX to be set to rollBackOnly (even if you catch the exception in the caller and ignore it).
To overcome this you can use propagation levels for transactions. Have a look at this to find out which propagation best suits your requirements.
Update; Read this!
Well after a colleague came to me with a couple of questions about a similar situation, I feel this needs a bit of clarification.
Although propagations solve such issues, you should be VERY careful about using them and do not use them unless you ABSOLUTELY understand what they mean and how they work. You may end up persisting some data and rolling back some others where you don't expect them to work that way and things can go horribly wrong.
EDIT Link to current version of the documentation
The answer of Shyam was right. I already faced with this issue before. It's not a problem, it's a SPRING feature. "Transaction rolled back because it has been marked as rollback-only" is acceptable.
Conclusion
USE REQUIRES_NEW if you want to commit what did you do before exception (Local commit)
USE REQUIRED if you want to commit only when all processes are done (Global commit) And you just need to ignore "Transaction rolled back because it has been marked as rollback-only" exception. But you need to try-catch out side the caller processNextRegistrationMessage() to have a meaning log.
Let's me explain more detail:
Question: How many Transaction we have? Answer: Only one
Because you config the PROPAGATION is PROPAGATION_REQUIRED so that the #Transaction persist() is using the same transaction with the caller-processNextRegistrationMessage(). Actually, when we get an exception, the Spring will set rollBackOnly for the TransactionManager so the Spring will rollback just only one Transaction.
Question: But we have a try-catch outside (), why does it happen this exception?
Answer Because of unique Transaction
When persist() method has an exception
Go to the catch outside
Spring will set the rollBackOnly to true -> it determine we must
rollback the caller (processNextRegistrationMessage) also.
The persist() will rollback itself first.
Throw an UnexpectedRollbackException to inform that, we need to rollback the caller also.
The try-catch in run() will catch UnexpectedRollbackException and print the stack trace
Question: Why we change PROPAGATION to REQUIRES_NEW, it works?
Answer: Because now the processNextRegistrationMessage() and persist() are in the different transaction so that they only rollback their transaction.
Thanks
I wonder what is the best way to catch an OptimisticLockException in JavaEE 6. I have the following EJB:
#Stateless
public class SeminarBooking {
public void bookSeminar(Long seminarId, int numberOfPersons) {
...
//check capacity & do booking
//OptimisticLockException can occur in this method
}
And this is my REST interface:
#Path("/seminars")
#Produces("application/xml")
#Stateless
public class SeminarResource {
#GET
#Path("{id}/book")
public Seminar bookSeminar(#PathParam("id") Long id, #QueryParam("persons") Integer persons) {
try {
seminarBooking.bookSeminar(id, persons);
return seminarBooking.getSeminar(id);
}
catch(Exception e) {
//why is this never called?
logger.error(This will never happen, e);
throw new WebApplicationException(e);
}
}
In the REST interface I catch all Exceptions, furthermore I see the OptimisticLockException if I call the interface from the browser, so why is the catch-Block never executed?
The obvious answer is that the exception in question isn't raised within that try block. Try reading the stack trace to see where it's thrown from. Given that it's related to persistence, it's likely thrown at your transaction boundary rather than from where you think it is.
It's probably never called because the SeminarResource is a transactional EJB. That means that the transaction is committed after its bookSeminar() method has returned, and not before it has returned.
This bean should probably not be transactional. If it weren't, the transaction would start when the SeminarBooking service is called, and commit when it returns. And you would be able to catch the exception that the commit throws.