I have a method annotated with #Transaction in my business layer . It inserts into two different entities.
#Transaction
void method (){
try{
service1.insertOne();
service2.insertTwo();
}
catch(Exception ex) {
// log exception
}
}
Both methods throw a custom exception when the validation fails.
The problem I am facing is if insertOne is successful and insertTwo throws a validation exception, the transaction is not being rolled back. i.e data in insertOne is being inserted.
Such case appears just because you catch generic exception in your method,
so that "transaction aspect" doesn't know anything about your validation error.
In this case, if exception isn't thrown, transaction commits as successfull
Related
I'm using Spring batch to write a batch process and I'm having issues handling the exceptions.
I have a reader that fetches items from a database with an specific state. The reader passes the item to the processor step that can launch the exception MyException.class. When this exception is thrown I want to skip the item that caused that exception and continue reading the next one.
The issue here is that I need to change the state of that item in the database so it's not fetched again by the reader.
This is what I tried:
return this.stepBuilderFactory.get("name")
.<Input, Output>chunk(1)
.reader(reader())
.processor(processor())
.faultTolerant()
.skipPolicy(skipPolicy())
.writer(writer())
.build();
In my SkipPolicy class I have the next code:
public boolean shouldSkip(Throwable throwable, int skipCount) throws SkipLimitExceededException {
if (throwable instanceof MyException.class) {
// log the issue
// update the item that caused the exception in database so the reader doesn't return it again
return true;
}
return false;
}
With this code the exception is skipped and my reader is called again, however the SkipPolicy didn't commit the change or did a rollback, so the reader fetches the item and tries to process it again.
I also tried with an ExceptionHandler:
return this.stepBuilderFactory.get("name")
.<Input, Output>chunk(1)
.reader(reader())
.processor(processor())
.faultTolerant()
.skip(MyException.class)
.exceptionHandler(myExceptionHandler())
.writer(writer())
.build();
In my ExceptionHandler class I have the next code:
public void handleException(RepeatContext context, Throwable throwable) throws Throwable {
if (throwable.getCause() instanceof MyException.class) {
// log the issue
// update the item that caused the exception in database so the reader doesn't return it again
} else {
throw throwable;
}
}
With this solution the state is changed in the database, however it doesn't call the reader, instead it calls the method process of the processor() again, getting in an infinite loop.
I imagine I can use a listener in my step to handle the exceptions, but I don't like that solution because I will have to clone a lot of code asumming this exception could be launched in different steps/processors of my code.
What am I doing wrong?
EDIT: After a lot of tests and using different listeners like SkipListener, I couldn't achieve what I wanted, Spring Batch is always doing a rollback of my UPDATE.
Debugging this is what I found:
Once my listener is invoked and I update my item, the program enters the method write in the class FaultTolerantChunkProcessor (line #327).
This method will try the next code (copied from github):
try {
doWrite(outputs.getItems());
} catch (Exception e) {
status = BatchMetrics.STATUS_FAILURE;
if (rollbackClassifier.classify(e)) {
throw e;
}
/*
* If the exception is marked as no-rollback, we need to
* override that, otherwise there's no way to write the
* rest of the chunk or to honour the skip listener
* contract.
*/
throw new ForceRollbackForWriteSkipException(
"Force rollback on skippable exception so that skipped item can be located.", e);
}
The method doWrite (line #151) inside the class SimpleChunkProcessor will try to write the list of output items, however, in my case the list is empty, so in the line #159 (method writeItems) will launch an IndexOutOfBoundException, causing the ForceRollbackForWriteSkipException and doing the rollback I'm suffering.
If I override the class FaultTolerantChunkProcessor and I avoid writing the items if the list is empty, then everything works as intended, the update is commited and the program skips the error and calls the reader again.
I don't know if this is actually a bug or it's caused by something I'm doing wrong in my code.
A SkipListener is better suited to your use case than an ExceptionHandler in my opinion, as it gives you access to the item that caused the exception. With the exception handler, you need to carry the item in the exception or the repeat context.
Moreover, the skip listener allows you to know in which phase the exception happened (ie in read, process or write), while with the exception handler you need to find a way to detect that yourself. If the skipping code is the same for all phases, you can call the same method that updates the item's status in all the methods of the listener.
I have some repository classes added as scoped service and I retrieve a particular repository using:
var rep = HttpContext.RequestServices.GetService(typeof(interface));
That's ok... but when I call:
rep.Save();
Inside, in some validations, I throw Exceptions... And the problem is that I can't catch this Exception in the call, like:
try
{
rep.Save();
}
catch (Excpetion ex)
{
// unreachable code
}
The exception also causes a stop of current running project.
I don't want a global error handler, I just want to catch this exception to determine what should I do right after.
In DI:
svc.AddScoped<IExtratoRepository, ExtratoRepository>();
exception in action
This scenario using CMT is working:
Stateless session bean with CMT, one method annotated with #TransactionAttribute(TransactionAttributeType.MANDATORY). Within this method, a record is written into a RDBMS using an XA data source and plain JDBC.
The stand-alone client (separate JVM, command-line Java application) is getting a UserTransaction from the application server (by JNDI lookup),
starts the transaction, and calls the EJB.
If the client commits the UserTransaction, the record is written into the database.
If the client rollbacks the UserTransaction, the record is not written into the database.
In the PostgreSql log files, one can see the prepared transaction with BEGIN, and COMMIT or ROLLBACK
If the client does not start a transaction before calling the EJB, a javax.ejb.EJBTransactionRequiredException is thrown (as expected, TransactionAttributeType.MANDATORY).
Now I switch from CMT to BMT
Again, if the client does not start a transaction before calling the EJB, a javax.ejb.EJBTransactionRequiredException is thrown (as expected, TransactionAttributeType.MANDATORY).
If I call sessionContext.getUserTransaction().getStatus(), it always reports Status.STATUS_NO_TRANSACTION.
The record is always written into the database, if the client calls commit or rollback.
In the PostgreSql log files, there are no prepared transactions, just plain insert commands.
The source of the EJB:
#Remote(DemoIfc.class)
#Stateless(name = "DemoBmt")
#TransactionManagement(TransactionManagementType.BEAN)
public class DemoBmt implements DemoIfc {
#Resource
private SessionContext sessionContext;
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public String ping(final String s) throws SystemException {
try {
System.out.println("TX: status: "
+ this.sessionContext.getUserTransaction().getStatus());
} catch (Exception e) {
System.out.println("TX: status: " + e.getMessage());
}
try {
writeIntoDb();
if (s.startsWith("crash")) {
throw new SystemException("Simulated crash");
}
return s.toUpperCase();
} catch (NamingException e) {
throw new SystemException(e.getMessage());
} catch (SQLException e) {
throw new SystemException(e.getMessage());
}
}
}
The client's source:
final UserTransaction ut = (UserTransaction) initialContext
.lookup("UserTransaction");
try {
ut.begin();
System.out.println(demo.ping("crash: DemoBmt with UT"));
ut.commit();
} catch (Exception ex) {
System.out.println("Expected rollback");
ut.rollback();
}
I am using JBoss 6.0.0 final.
How can I properly propagate the client-side UserTransaction into the EJB with BMT?
BMT beans cannot participate in an existing transaction
From EJB 3.1 spec.:
13.6.1 Bean-Managed Transaction Demarcation
The container must manage client invocations to an enterprise bean
instance with bean-managed transaction demarcation as follows. When a
client invokes a business method via one of the enterprise bean’s
client views, the container suspends any transaction that may be
associated with the client request....
In my sql stored procedure, i do some insertion and updating which in some scenarios throws Primary Key or unique key violation.
When I try to execute this procedure from ADO.net, .net application also throws that exception and let me know that something wrong had happen.
But when I try to execute this procedure from EF, it just executes. Neither it show anything nor update anything.
How should I handle or notify user that something wrong had happen?
Ado.Net code is
SqlConnection sqlConnection = new SqlConnection(#"data source=database01; database=test; user id=test; password=test;");
SqlCommand cmd = new SqlCommand("[uspUpdateTest]", sqlConnection);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("RunID", RunID);
cmd.Parameters.AddWithValue("RunCode", RunCode);
sqlConnection.Open();
var str = cmd.ExecuteNonQuery();
Entity Framework Code is
TestEntities context = new TestEntities();
var str=context.UpdateRun(RunID, RunCode);
I am very much sure, you must set some return type(dummy) in your function import. It makes sense most of the time, because if you don't do so, your method name does not appear in intellisense and you will no be able to access it using context.MethodName.
My suggestion for you is, remove the return type of your Function Import and set it to none. Execute your method using ExecuteFunction method of context.
Context.ExecuteFunction(FunctionName,Parameters). It'll definitely throws the exception.
First of all, make sure you're throwing an Exception in your stored procedure which we can catch in our C# code. See - http://social.msdn.microsoft.com/forums/en-US/adodotnetdataproviders/thread/efea444e-6fca-4e29-b100-6f0c5ff64e59 - quote:
If you want RAISERROR to throw a SqlException, you need to set its
severity above 10. Errors with a severity of 10 and below are
informational, and thus don't throw exceptions.
I'll also show you the following code. I have been using this in my MVC controllers when getting data from my service layer using Entity Framework:
try
{
try
{
//Entity Framework/Data operations that could throw the data exception
//...
} catch (DataException dex) //see http://msdn.microsoft.com/en-us/library/system.data.dataexception.aspx
{
//Specifically handle the DataException
//...
}
}
catch (Exception e)
{
//do something (logging?) for the generic exception
throw e;
}
You can put a breakpoint on the last catch if the first catch doesn't trigger to see Exception-type/inner-exception of 'e' and go from there. It is useful to put a breakpoint on the generic exception, as it let's me know when I haven't handled something.
We can use the following way for sql raised error exception from entity framework:
Let's say, we have DBContext. So that
var connection= (SqlConnection)db.Database.Connection;
if (connection != null && connection.State == ConnectionState.Closed)
{
connection.Open();
}
SqlCommand com = new SqlCommand("spname", connection);
com.CommandType = CommandType.StoredProcedure;
com.Parameters.Add(new SqlParameter("#parameter", parameter));
try
{
com.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex.message;
} `
The question here gives quite a nice summary of catching and handling the exceptions gracefully. You have several options after that for rolling back etc.
I am using EclipseLink in my web application, and I am having a hard time gracefully catching and handling Exceptions it generates. I see from this thread what seems to be a similar problem, but I don't see how to work around or fix it.
My code looks like this:
public void persist(Category category) {
try {
utx.begin();
em.persist(category);
utx.commit();
} catch (RollbackException ex) {
// Log something
} catch (HeuristicMixedException ex) {
// Log something
} catch (HeuristicRollbackException ex) {
// Log something
} catch (SecurityException ex) {
// Log something
} catch (IllegalStateException ex) {
// Log something
} catch (NotSupportedException ex) {
// Log something
} catch (SystemException ex) {
// Log something
}
}
When persist() is called with an entity that violates a uniqueness constraint, I get an explosion of exceptions that are caught and logged by the container.
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504):
org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement
was aborted because it would have caused a duplicate key value in a unique or
primary key constraint or unique index identified by 'SQL110911125638570'
defined on 'CATEGORY'.
Error Code: -1
(etc)
I have tried the following:
try {
cc.persist(newCategory);
} catch (PersistenceException eee) {
// Never gets here
System.out.println("MaintCategory.doNewCateogry(): caught: " + eee);
} catch (DatabaseException dbe) {
// Never gets here neither
System.out.println("MaintCategory.doNewCateogry(): caught: " + dbe);
}
I realize that using DataBaseException is not portable, but I need to start somewhere. The exceptions never get caught. Any suggestions?
It looks like I won't get any more activity on this question, so I will post my work-around and leave it at that. A number of web searches haven't found much of anything that is helpful. I would have thought this is a textbook case but none of the tutorials I have found covers it.
As it turns out in this condition with EclipseLink, the Exception you can catch when the SQL constraint is violated is the RollBackException that is the result of the em.commit() call. So I have modified my persist method like this:
public void persist(Category category) throws EntityExistsException {
try {
utx.begin();
em.persist(category);
utx.commit();
} catch (RollbackException ex) {
Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
throw new EntityExistsException(ex);
} catch (HeuristicMixedException ex) {
Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
} catch (HeuristicRollbackException ex) {
Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalStateException ex) {
Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
} catch (NotSupportedException ex) {
Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
} catch (SystemException ex) {
Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
}
}
So the caller catches the EntityExistsException and takes the appropriate action. The log still fills up with the internal exceptions but that can be shut off later.
I realize that this is a bit of an abuse of the intent of the EntityExistsException that is normally only used when an entity ID field is re-used, but for the purposes of the user application it doesn't matter.
If anyone has a better approach please post a new answer or comment.
Edit your persistence.xml adding the following property:
property name="eclipselink.exception-handler" value="your.own.package.path.YourOwnExceptionHandler"
Now create the class YourOwnExceptionHandler (on the correct package). It requires to implement org.eclipse.persistence.exceptions.ExceptionHandler.
Create a non argument constructor and the required method handleException(...).
Inside this method, you can catch the exceptions!
EclipseLink should only be throwing either a PersitenceException or a RollbackException depending on the environment and the order of operations you are calling on the EntityManager.
What is your logging level? It is likely that you are seeing these exceptions logged by EclipseLink but only thrown as causes of the RollbackException.
You can turn off exception logging with the PU property but for diagnostic purposes it is generally better to allow EclipseLink to log the exceptions.
2019-12-18
As its a very well viewed question and I just had a very similar issue with EclipseLink, in a Maven multi module web application running on Weblogic 12c server and using JTA, I am going to post my solution here, hoping to save a couple hours for someone.
In the persistence.xml we are having:
< property name="eclipselink.persistence-context.flush-mode"
value="commit" />
The REST resource class was marked with #Transactional, meaning that the transaction starts at the point when the request has been received by the related method of the resource class, and it ends when this method returns.
JTA used for managing the transactions.
Now, JTA commit time happens to occur AFTER the resource class's method returns (with a response to the REST client).
Which subsequently means that:
Even though you had a very proper setup to catch the Exception, you
cannot, as exceptions like SQLIntegrityConstraintViolationException
occur only AFTER your INSERT/UPDATE/DELETE query
--that has been sitting all this time in your JPA provider cache--,
now finally sent to the database.
Which happens just after the resource class's method returns, and at that point, all the exceptions has been skipped already.
Since no query sent == no exception occured at the time when the execution ran through the try{...}catch(Exception e){...} lines, you were not able to catch it,
but at the end, you will see the exception in the server's log.
Solution:
I had to manually call flush() on EntityManager to force flush and the exception to occur at the proper time and line (basically in the try block) to be able to catch it, handle it, and allow my REST method to return with my intended response.
The final caught exception in the log (I have masked some not related info):
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - x.x.x.v00000000-0000000): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (XXXXX.UNIQUE_KEY_NAME) violated
Related pseudo code:
try {
repository.update(entity);
repository.getEntityManager().flush();
} catch (Exception e ) {
log.info(e.toString());
...
}
I'm using Spring Boot 1.1.9 + EclipseLink 2.5.2. This is the only way I can catch ConstraintViolationException. Note that my handleError(ConstraintViolationException) is a very simple implementation which just returns the first violation it finds.
Note that this code was also required when I switched to Hibernate 4.3.7 and Hibernate Validator 5.1.3.
It seems that adding PersistenceExceptionTranslationPostProcessor exceptionTranslation() to my persistence JavaConfig class also has no effect.
import javax.persistence.RollbackException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
#ControllerAdvice
class GlobalExceptionHandler
{
#ExceptionHandler(TransactionSystemException.class)
public ResponseEntity<Object> handleError(final TransactionSystemException tse)
{
if(tse.getCause() != null && tse.getCause() instanceof RollbackException)
{
final RollbackException re = (RollbackException) tse.getCause();
if(re.getCause() != null && re.getCause() instanceof ConstraintViolationException)
{
return handleError((ConstraintViolationException) re.getCause());
}
}
throw tse;
}
#ExceptionHandler(ConstraintViolationException.class)
#SuppressWarnings("unused")
public ResponseEntity<Object> handleError(final ConstraintViolationException cve)
{
for(final ConstraintViolation<?> v : cve.getConstraintViolations())
{
return new ResponseEntity<Object>(new Object()
{
public String getErrorCode()
{
return "VALIDATION_ERROR";
}
public String getMessage()
{
return v.getMessage();
}
}, HttpStatus.BAD_REQUEST);
}
throw cve;
}
}
I use this.
if (!ejbGuardia.findByPkCompuestaSiExiste(bean.getSipreTmpGuardiaPK())) {
ejbGuardia.persist(bean);
showMessage(ConstantesUtil.MENSAJE_RESPUESTA_CORRECTA, SEVERITY_INFO);
} else {
showMessage("Excel : El registro ya existe. (" + bean.toString() + ") ", SEVERITY_ERROR);
}
and my function from above:
public boolean findByPkCompuestaSiExiste(Object clasePkHija) throws ClassNotFoundException {
if (null != em.find(this.clazz, clasePkHija)) {
return true;
}
return false;
}
With that I dont need to program a validation for each Persist, its common in the my DAO Classes.