• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Liutauras Vilda
  • Paul Clapham
  • paul wheaton
Sheriffs:
  • Tim Cooke
  • Devaka Cooray
  • Rob Spoor
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Tim Moores
  • Carey Brown
  • Mikalai Zaikin
Bartenders:

Not able to catch ConstraintViolationException in Hibernate

 
Ranch Hand
Posts: 42
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I suppose this must be a very basic question.

I relaunch this code which tries to insert an already existing row in a table but the ConstraintViolationException is catched  in the general catch (Exception e) block insted of being catched in the specific catch (ConstraintViolationException e) block as I expected.

I wonder what should I try to catch that specific exception.

My code:


My console output:

Vamos insertar una nueva fila en la tabla Departamentos
nov 18, 2022 1:10:26 A. M. org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 1062, SQLState: 23000
nov 18, 2022 1:10:26 A. M. org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Duplicate entry '3' for key 'departamentos.PRIMARY'
nov 18, 2022 1:10:26 A. M. org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl release
INFO: HHH000010: On release of batch it still contained JDBC statements
org.hibernate.exception.ConstraintViolationException: could not execute statement


 
Fernando Sanchez
Ranch Hand
Posts: 42
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I have seen two things:

1. I was assuming it was the save method which launched the ConstraintViolationException but in fact it is the commit method which launches it
2. I can catch PersistenceException (not ConstraintViolationException)

I wonder if this code would be acceptable:


Which causes this new output:

Vamos insertar una nueva fila en la tabla Departamentos
nov 18, 2022 9:54:59 A. M. org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 1062, SQLState: 23000
nov 18, 2022 9:54:59 A. M. org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Duplicate entry '3' for key 'departamentos.PRIMARY'
nov 18, 2022 9:54:59 A. M. org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl release
INFO: HHH000010: On release of batch it still contained JDBC statements
PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement


 
Saloon Keeper
Posts: 15252
349
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Why are you catching these exceptions at all?
 
Fernando Sanchez
Ranch Hand
Posts: 42
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:Why are you catching these exceptions at all?



I'm just giving my first steps with Hibernate and in different examples I have read they tried to catch ConstraintViolationException just in case a constraint was violated after a Session's save, delete, or update action.
Shouldn't I do that? Should I just have a generic Exception handler?
 
Stephan van Hulst
Saloon Keeper
Posts: 15252
349
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I would just let any exceptions bubble up (possibly wrapping them in more appropriate exception types) until you reach the layer of your application where the application can do something useful with the exception. Printing the stack trace is not useful, because that will happen automatically when the exception escapes the  main method.

Some of the persistence frameworks that I'm used to working with automatically roll back a transaction if you don't explicitly commit it before closing the transaction. It appears that Hibernate does not guarantee this behavior. That means you must catch any exception that can occur while you're working with your transaction, and then explicitly roll it back. Importantly though, you should rethrow the exception if you can't handle the exception in a way that is appropriate to that layer of your application. To reiterate, printing a stack trace is NOT appropriate.

I haven't looked at the Hibernate API in depth, but I think you could look something like this:

Things to note:

  • After the transaction has begun, an exception is caught to make sure the transaction is rolled back. The exception is rethrown.
  • Any time a Hibernate exception is thrown in the method, it is caught, wrapped and rethrown. It's up to the caller of this method to deal with a UnitOfWorkException.
  • UnitOfWorkException is a custom exception type I came up with. You can write your own custom exception types that might be appropriate to your application.
  • The session is disconnected and closed after the application is done with it. This is done in a finally-block.
  • Any UnitOfWorkException thrown the this method does not necessarily need to be caught. It can either bubble up out of the application, or wrapped in yet another exception type that is more appropriate.

  • Here is what your main method might then look like:

     
    Fernando Sanchez
    Ranch Hand
    Posts: 42
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    please remove this post, next post is what I really meant
     
    Fernando Sanchez
    Ranch Hand
    Posts: 42
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks for the detailed answer. I'm not developing applications with Hibernate, just trying to understand how it works. My goal with these tests was just trying to understand the "hierarchy" of exceptions which could be thrown when violationg database constraints (that is why I simply catched exceptions and showed them).

    I tried to try your suggestion forcing the error by inserting again already existing deptarment nº 3, but I found two porblems:

    1st: I don't know if I can find the UnitOfWorkException class somewhere so I improvised this:


    2nd: HibernateException exceptions are never reached:


    when I try this main method none of the HibernateExeption handler is reached:

    package exceptionhandlers;

    import clases.Departamentos;
    import util.HibernateUtil;

    public class main {

    public static void main(String... args) throws UnitOfWorkException {
    var department = new Departamentos(3, "Marketing");
    var command = new DepartmentStoreCommand(HibernateUtil.getSessionFactory());
    command.storeDepartment(department);
    }

    }



    produces this output:

    Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1411)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:489)
    at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3303)
    at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2438)
    at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:449)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:40)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281)
    at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
    at exceptionhandlers.DepartmentStoreCommand.storeDepartment(DepartmentStoreCommand.java:26)
    at exceptionhandlers.main.main(main.java:11)
    Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:59)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:37)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:200)
    at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:46)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3375)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3908)
    at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:107)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
    at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)
    at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:344)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:40)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1407)
    ... 10 more
    Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '3' for key 'departamentos.PRIMARY'
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1061)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1009)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1320)
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:994)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
    ... 22 more



     
    Stephan van Hulst
    Saloon Keeper
    Posts: 15252
    349
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Right, the problem is that transaction.commit() doesn't throw HibernateException, it throws PersistenceException (which is wrapped around a HibernateException much in the same way that we wrap HibernateException in our UnitOfWorkException).

    Personally, I find PersistenceException very appropriate for this layer of the application, so I would use that instead of UnitOfWorkException and then simply not catch any exceptions at all. But if your goal is to explore the various HibernateExceptions, you can simply unwrap the PersistenceException like this:

    Now you can catch HibernateException in your main method and do whatever you want with it.

    Please do NOT emulate this kind of code when you're writing a real application.
     
    Saloon Keeper
    Posts: 27478
    195
    Android Eclipse IDE Tomcat Server Redhat Java Linux
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    It appears that you are coding for Legacy Hibernate. I do not recommend this. Legacy Hibernate may go away someday.

    For most SQL-based ORM uses, I recommend the Java Persistence Architecture (JPA). JPA is part of the Jakarta EE standard and was part of the JEE standard that preceeded it, so there's a lot of support and documentation on it, and a much stronger audience to keep it alive.

    Further, the JBoss Hibernate team were major contributors to the design and creation of the JPA standard, meaning that it is well-adapted to theit intentions. In fact, I'd argue that Hibernate JPA is more "Hibernate" than (Legacy) Hibernate is at this point.

    But aside from that, I believe that there's a specific Duplicate Record Exception that applies when trying to add a record whose primary key matches one already stored, so it wouldn't be a ConstrainViolationException as such. Rather than ask why your code isn't catching the "Constraint Violation", I'd recommend simply catching all Exceptions, printing a Stack Trace and seeing what the actual Exception type was.
     
    Fernando Sanchez
    Ranch Hand
    Posts: 42
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Tim Holloway wrote:It appears that you are coding for Legacy Hibernate. I do not recommend this. Legacy Hibernate may go away someday.

    For most SQL-based ORM uses, I recommend the Java Persistence Architecture (JPA). JPA is part of the Jakarta EE standard and was part of the JEE standard that preceeded it, so there's a lot of support and documentation on it, and a much stronger audience to keep it alive.

    Further, the JBoss Hibernate team were major contributors to the design and creation of the JPA standard, meaning that it is well-adapted to theit intentions. In fact, I'd argue that Hibernate JPA is more "Hibernate" than (Legacy) Hibernate is at this point.



    Thank you for the suggestion, I'll research about that and hopefully find a way to begin it with.

    Tim Holloway wrote:But aside from that, I believe that there's a specific Duplicate Record Exception that applies when trying to add a record whose primary key matches one already stored, so it wouldn't be a ConstrainViolationException as such. Rather than ask why your code isn't catching the "Constraint Violation", I'd recommend simply catching all Exceptions, printing a Stack Trace and seeing what the actual Exception type was.


    The only Hibernate exception I am able to catch when violating the PK is PersistenceException which seems to "wrap" the ConstraintViolationException:

    PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
    javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement



    The reason why I tried to catch ConstraintViolationException is because in the books and other resources I read, they tried to catch it, but it doesn't work for me.
    Another thing than surprises me is that the Session's save method does not throw the exeption as I expected, it wasn't until I tried to commit the transaction that it was thrown.
     
    Tim Holloway
    Saloon Keeper
    Posts: 27478
    195
    Android Eclipse IDE Tomcat Server Redhat Java Linux
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Fernando Sanchez wrote:
    Another thing than surprises me is that the Session's save method does not throw the exeption as I expected, it wasn't until I tried to commit the transaction that it was thrown.



    That's because "save" doesn't save. It adds the object to be saved to the Transaction. When you commit the Transaction, all of the saves, deletes and updates are done all at once. Or, if you rollback the Transaction, they are all discarded. Transactions are all-at-once and all-or-nothing, never partial.

    JPA is not that much different from Legacy Hibernate. Instead of a Session, you have an EntityManager, some methods are different, and there are minor differences in the Query Language (JPQL). But if you know one, switching to the other is fairly easy. But I'm pretty sure that JPA does have a particular Exception for attempts to add duplicate records.
     
    eat bricks! HA! And here's another one! And a tiny ad!
    a bit of art, as a gift, the permaculture playing cards
    https://gardener-gift.com
    reply
      Bookmark Topic Watch Topic
    • New Topic