views:

47

answers:

2

Hi,

I have a DAO where I need to catch an unique constraint exception. To do this, the only working solution is to flush my EntityManager after the persist. Only then I come into a catch block where I have to filter out the exception. And, my DAO method needs to be wrapped in a transaction (REQUIRES_NEW) otherwise I have the RollBackException.

Am I doing something wrong?

try {
        em.persist(myObject);
        em.flush();
    } catch (PersistenceException ex) {
        if (ex.getCause() != null) {
            String cause = ex.getCause().toString();
            if (cause != null) {
                if (cause.contains("org.hibernate.exception.ConstraintViolationException")) {
                    logger
                            .error("org.hibernate.exception.ConstraintViolationException: possible unique constraint failure on name");
                    throw ex;
                }
            }
        }
    }
A: 

There is nothing wrong, I suppose. In fact Hibernate implementation doesn't work without flush(), unlike Toplink Essential.

A better approach would be,

try {
        em.persist(myObject);
        em.flush();
    } catch (PersistenceException ex) {
         if (ex instanceof ConstraintViolationException) {
             logger.error(ex.getMessage());
             throw ex;
         }
    }

Tip: Its not a good idea to log and throw as well, IMO. Because, then the calling method need to take care of Exception too, and it might end up logging it again. That would clutter the log and make it difficult to read. It will also grow in size.

Adeel Ansari
About the instanceof: I can't use that one because that would put a dependency on Hibernate which we don't want (for now).
Peter Verhoye
@Peter Verhoye: But don't you think in future if you change the implementation to something else, even then your current code would stop working as expected. And sometimes its more dangerous then a broken code.
Adeel Ansari
+1  A: 

Am I doing something wrong?

EntityManager#persist() won't trigger an immediate insert (unless you are using an IDENTITY strategy). So if you want to actually write the in memory changes to the database and get a chance to catch a constraint violation, you have to flush() "manually" (although even doing so doesn't strictly guarantee anything, your database could be configured to use deferred constraints).

In other words, what you're doing is IMO just the right way to go.


I have a service method with a @Transactional on it (REQUIRED) called addNewObject(). In this method some stuff happens that might throw an exception (and thus rollback the transaction). One of these calls is a call to the DAO method addObject. This one might fail because of the unique constraint. Now, if I do flush, the object will be persisted if there is no unique violation. However, within the service there might still be something else that causes the method to throw an exception.

I think that you are confusing flush and commit. If something goes wrong outside addObject and throws an unrecoverable exception (but inside the same transaction), the whole transaction will be rolled back, including the INSERT statements corresponding to the persist() calls. To sum up, flush != commit.

Pascal Thivent
The problem with flushing is that your object gets persisted and thus any rollback that needs to be done (because for example the service calling the dao needs to rollback) is moot. Or am I making a fool of myself :-)
Peter Verhoye
@Peter Can you please clarify your problem, I don't get it (are you looking for a solution to check database constraints violations without touching the database?). And please also add details about the transactional concerns, that's something I didn't understood in the initial question.
Pascal Thivent
@Pascal: I have a service method with a @Transactional on it (REQUIRED) called addNewObject(). In this method some stuff happens that might throw an exception (and thus rollback the transaction). One of these calls is a call to the DAO method addObject. This one might fail because of the unique constraint. Now, if I do flush, the object will be persisted if there is no unique violation. However, within the service there might still be something else that causes the method to throw an exception.
Peter Verhoye
@Peter Let's see if my update makes things more clear.
Pascal Thivent
Yep, it made me look back over my code and cleared out some stuff. Now everything works as should. Thanks!
Peter Verhoye
@Peter You're welcome.
Pascal Thivent