Few tips on using Java Persistence API and Hibernate (with Hibernate Annotations and Hibernate EntityManager)
(Hibernate mapování s MySQL – nejčastější chyby a výjimky)
Some exceptions seem complicated for someone who doesn't have a clue what's going on under Hibernate's hood.
During the developement of my latest project, I've explained several weird-looking exceptions to my co-worker, and we also discussed the reason to use some code patterns encouraged (if not required) by Hibernate mapping.
So I have decided do put some of the exceptions and their explanation here. The respective error messages comming from database are MySQL specific, but for other RDBMS it will be similar.
Sometimes the important part is almost at the end – either at the end of a very long line, or at the end of stack trace. So don't panic when you see something like:
org.springframework.orm.hibernate3.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL
Current habit of handlig exceptions is to add it's getMessage()
at the end of the message of the exception to be thrown. This way, when the
exception bubbles from the deepness of several libraries (like JDBC driver,
Hibernate Core, Hibernate EntityManager and Java Persistence API (JPA)), the
first line is usually very long. Given that the actual important information
(Database's message) comes from the very lowest layer, it ends up at the end
of the line.
For the case when you find nothing useful at the first line, scroll down the stacktrace to see the root causes. Those are the lines starting with „Caused by:“. Read them carefully, because they are something like a scenario, whereas the lines starting with „at“ are kind of scaffolding or sceneries.
When you haven't found anything familiar even in the root causes, still it may be useful to scan the „at“ lines to see where and when exactly the exception happened, because there's a small chance that you've find a bug, or you have triggered an exception that is not (yet) well documented and discussed in forums to get simple answer just by googling for the Exception text. However, there are some time-proven libraries like JDBC driver which are very unlikely to have bugs, and almost all possible exceptions have been discussed somewhere – so you can skip their „at“ lines when investigating; just keep in mind that your exception appeared in the context of this library.
Last note for those who don't know – the exceptions in the stack trace are in reverse order in which they appeared. In other words, if you split the stack trace by the „Caused by“ lines and reverse the blocks order, you would get the actual call stack in the moment when the deepest root cause happened.
Most commonly appearing exceptions come from these situations:
EntityManagerFactory
.To be written later (when I get some of these again :).
To be written later.
This makes Hibernate complain such like „trying to persist deleted entity“. Most common cause is that you have some references across the network of objects, which prevent Hibernate to perform the requested operation.
In case of deletion, check all direct AND indirect bi-directional references between the entity to be deleted and other entities – including collections. If the references reach to an entity that keeps the other side of the relation, Hibernate can't delete the refering entity before you release the other side of the relation.
Let's see the example:
@Entity class Order @OneToMany( ... ) Set<OrderItem> items = new HashSet(); // BTW: Don't use List unless you have a column to store the items' positions to // and have mapped it with org.hibernate.annotations.IndexColumn. /** Removes the item from the order. */ public boolean removeItem( OrderItem item ){ Item.setOrder( null ); return this.items.remove( subj ); } ... }
java.lang.IllegalArgumentException: Removing a detached instance ...
To be written later.
Long story in short:
Option 1: getReference()
public void remove( Item item ) { this.em.remove( this.em.getReference( Item.class, item.getId() ) ); }
Option 2: merge()
public void remove( Item item ) { this.em.remove( this.em.merge( Item.class, item.getId() ) ); }
There is additional overhead to merge since in addition to doing a lookup, it will merge into it if it exists, and it will create a new instance if it doesn't. – http://forums.java.net/…/thread.jspa?…
Generally speaking, using merge()
is slower, but safer when
you're not sure in which state you will call the remove()
method.
Caused by: org.hibernate.exception.GenericJDBCException: could not insert: [cz.dynawest.isir.entities.User] Caused by: java.sql.SQLException: Field 'created' doesn't have a default value
This one is easy – probably you have a NOT NULL column, but haven't
annotated it – so Hibernate, when it sees null
value, it does
not include the column in the INSERT
query. Add
@Column( nullable=false )
to the mentioned property.
@Column( nullable=false ) private Date created;
This will not solve the problem, but at least you will catch it sooner – before Hibernate sends the SQL to the database, it will throw an exception with the message saying which property you did not set.
Full stack trace:
org.springframework.orm.hibernate3.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [insert into isir_sled_users (aktivace_klic, mail, pass) values (?, ?, ?)]; SQL state [HY000]; error code [1364]; could not insert: [cz.dynawest.isir.entities.User]; nested exception is org.hibernate.exception.GenericJDBCException: could not insert: [cz.dynawest.isir.entities.User] at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:642) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:95) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:212) at org.springframework.orm.jpa.JpaAccessor.translateIfNecessary(JpaAccessor.java:152) at org.springframework.orm.jpa.JpaTemplate.execute(JpaTemplate.java:189) at org.springframework.orm.jpa.JpaTemplate.persist(JpaTemplate.java:266) at cz.dynawest.isir.dao.UserDaoImpl.create(UserDaoImpl.java:42) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at $Proxy25.create(Unknown Source) at cz.pohlidame.entities.UserTest.testCreateUser(UserTest.java:67) Caused by: org.hibernate.exception.GenericJDBCException: could not insert: [cz.dynawest.isir.entities.User] at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:126) at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:114) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:64) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2186) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2666) at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71) at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279) at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321) at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204) at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130) at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49) at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154) at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110) at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61) at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:645) at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619) at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623) at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220) at org.springframework.orm.jpa.JpaTemplate$5.doInJpa(JpaTemplate.java:268) at org.springframework.orm.jpa.JpaTemplate.execute(JpaTemplate.java:184) ... 51 more Caused by: java.sql.SQLException: Field 'created' doesn't have a default value at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542) at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2019) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1937) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1922) at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:94) at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:57)