Hi All,
Hibernate EntityManager: 3.1beta6
This posting relates to a potential bug in the Hibernate EntityManager implementation.
In section 3.4 "Entity Listeners and Callback Methods" of the ejb 3.0 proposed final draft spec document (JSR220), it is written:
"Lifecycle callback methods may throw unchecked/runtime exceptions. A
runtime exception thrown by a callback method that executes within a
transaction causes that transaction to be rolled back."
Which is understandable. In section 3.4.3 an example is offered of an
entity class having a @PrePersist annotated method, validateCreate() which throws an unchecked exception, as follows:
Code:
@PrePersist
protected void validateCreate() {
if (getBalance() < MIN_REQUIRED_BALANCE)
throw new AccountException("Insufficient balance to open an account");
}
This example suggests that performing validation in a @PrePersist annotated method in an entity is a reasonable thing to do. It does stand to reason that an entity should have control over validating it's state - it's clearly better encapsulation than having a facading session bean perform validation (for example).
To attempt to use this, I added the following @PrePersist annotated method to an entity named MessageFolder. Note ValidateException extends RuntimeException, and name is a String property in the entity.
Code:
@PrePersist
protected void validateFolder() throws ValidateException
{
if(getName() == null || "".equals(getName()))
{
throw new ValidateException("error.no.folder.name");
}
}
Using a suitable instance of an EntityManager with an extended application managed persistence context and attempting to persist() a new MessageFolder entity which has no name property set results in the stack track seen below.
Note the specific ValidateException thrown is not seen - therefore rendering the throwing of such an exception, and it's intended purpose (to report back to the object trying to persist the entity that there's a problem with the unmanaged new entity's state) is near unusable.
A better implementation would be for the transaction to roll back, and for the container to return the exception thrown, without wrapping it.
javax.ejb.EJBException: Transaction was rolled back:
org.hibernate.PersistentObjectException: detached entity passed to persist:
com.formicary.epix.module.message.MessageFolder; nested exception is:
org.hibernate.PersistentObjectException:
detached entity passed to persist:
com.formicary.epix.module.message.MessageFolder
org.hibernate.PersistentObjectException: detached entity passed to
persist: com.formicary.epix.module.message.MessageFolder
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(Default
PersistEventListener.java:79)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(Default
PersistEventListener.java:38)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:640)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:614)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:618)
at
org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityMan
agerImpl.java:127)
at
com.formicary.epix.module.message.MessageModuleBean.createFolder(MessageModuleBean.java:129)
at
MessageModule_StatelessSessionBeanWrapper0.createFolder(MessageModule_StatelessSessionBeanWrapper0.java:1052)
at
com.formicary.epix.actions.message.MessageAction.doAddFolder(MessageAction.java:384)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at
webwork.util.InjectionUtils$DefaultInjectionImpl.invoke(InjectionUtils.java:61)
at webwork.util.InjectionUtils.invoke(InjectionUtils.java:52)
at webwork.action.ActionSupport.invokeCommand(ActionSupport.java:417)
at webwork.action.ActionSupport.execute(ActionSupport.java:146)
at
webwork.dispatcher.GenericDispatcher.executeAction(GenericDispatcher.java:132)
at
webwork.dispatcher.ServletDispatcher.service(ServletDispatcher.java:175)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:333)
at com.evermind._ha.doFilter(.:59)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:123)
at com.evermind._gz.doFilter(.:16)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:63)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119)
at
com.formicary.epix.filter.SitemeshFilter.parsePage(SitemeshFilter.java:27)
at
com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilte
r.java:55)
at
com.formicary.epix.filter.ProxyPageFilter.doFilter(ProxyPageFilter.java:37)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityPrefixFilter.doFilter(CommunityPrefixFilter.java:70)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:86)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:653)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.RequestFilter.doFilter(RequestFilter.java:261)
at com.evermind._csb._pud(.:379)
at com.evermind._csb._bqc(.:176)
at com.evermind._ax._ltc(.:668)
at com.evermind._ax._ucb(.:193)
at com.evermind._bf.run(.:62)
javax.ejb.EJBException: Transaction was rolled back:
org.hibernate.PersistentObjectException: detached entity passed to persist:
com.formicary.epix.module.message.MessageFolder; nested exception is: org.hibernate.PersistentObjectException:
detached entity passed to persist:
com.formicary.epix.module.message.MessageFolder
at
MessageModule_StatelessSessionBeanWrapper0.createFolder(MessageModule_StatelessSessionBeanWrapper0.java:1087)
at
com.formicary.epix.actions.message.MessageAction.doAddFolder(MessageAction.java:384)
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:585)
at
webwork.util.InjectionUtils$DefaultInjectionImpl.invoke(InjectionUtils.java:61)
at webwork.util.InjectionUtils.invoke(InjectionUtils.java:52)
at webwork.action.ActionSupport.invokeCommand(ActionSupport.java:417)
at webwork.action.ActionSupport.execute(ActionSupport.java:146)
at
webwork.dispatcher.GenericDispatcher.executeAction(GenericDispatcher.java:132)
at
webwork.dispatcher.ServletDispatcher.service(ServletDispatcher.java:175)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:333)
at com.evermind._ha.doFilter(.:59)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:123)
at com.evermind._gz.doFilter(.:16)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:63)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119)
at
com.formicary.epix.filter.SitemeshFilter.parsePage(SitemeshFilter.java:27)
at
com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55)
at
com.formicary.epix.filter.ProxyPageFilter.doFilter(ProxyPageFilter.java:37)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityPrefixFilter.doFilter(CommunityPrefixFilter.java:70)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:86)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:653)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.RequestFilter.doFilter(RequestFilter.java:261)
at com.evermind._csb._pud(.:379)
at com.evermind._csb._bqc(.:176)
at com.evermind._ax._ltc(.:668)
at com.evermind._ax._ucb(.:193)
at com.evermind._bf.run(.:62)
ERROR [2006-02-15 12:37:58,744] JSP "500.jsp: Transaction was rolled back:
org.hibernate.PersistentObjectException: detached entity passed to persist:
com.formicary.epix.module.message.MessageFolder; nested exception is:
org.hibernate.PersistentObjectException: detached entity passed to persist:
com.formicary.epix.module.message.MessageFolder<p><small><small><pre>javax.ejb.EJBException:
Transaction was rolled back: org.hibernate.PersistentObjectException: detached
entity passed to persist: com.formicary.epix.module.message.MessageFolder; nested exception is: org.hibernate.PersistentObjectException: detached entity passed to
persist:
com.formicary.epix.module.message.MessageFolder
org.hibernate.PersistentObjectException: detached entity passed to
persist: com.formicary.epix.module.message.MessageFolder
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:79)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:38)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:640)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:614)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:618)
at
org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:127)
at
com.formicary.epix.module.message.MessageModuleBean.createFolder(MessageModuleBean.java:129)
at
MessageModule_StatelessSessionBeanWrapper0.createFolder(MessageModule_StatelessSessionBeanWrapper0.java:1052)
at
com.formicary.epix.actions.message.MessageAction.doAddFolder(MessageAction.java:384)
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:585)
at
webwork.util.InjectionUtils$DefaultInjectionImpl.invoke(InjectionUtils.java:61)
at webwork.util.InjectionUtils.invoke(InjectionUtils.java:52)
at webwork.action.ActionSupport.invokeCommand(ActionSupport.java:417)
at webwork.action.ActionSupport.execute(ActionSupport.java:146)
at
webwork.dispatcher.GenericDispatcher.executeAction(GenericDispatcher.java:132)
at
webwork.dispatcher.ServletDispatcher.service(ServletDispatcher.java:175)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:333)
at com.evermind._ha.doFilter(.:59)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:123)
at com.evermind._gz.doFilter(.:16)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:63)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119)
at
com.formicary.epix.filter.SitemeshFilter.parsePage(SitemeshFilter.java:27)
at
com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55)
at
com.formicary.epix.filter.ProxyPageFilter.doFilter(ProxyPageFilter.java:37)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityPrefixFilter.doFilter(CommunityPrefixFilter.java:70)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:86)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:653)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.RequestFilter.doFilter(RequestFilter.java:261)
at com.evermind._csb._pud(.:379)
at com.evermind._csb._bqc(.:176)
at com.evermind._ax._ltc(.:668)
at com.evermind._ax._ucb(.:193)
at com.evermind._bf.run(.:62)
javax.ejb.EJBException: Transaction was rolled back:
org.hibernate.PersistentObjectException: detached entity passed to persist:
com.formicary.epix.module.message.MessageFolder; nested exception is:
org.hibernate.PersistentObjectException:
detached entity passed to persist: com.formicary.epix.module.message.MessageFolder
at
MessageModule_StatelessSessionBeanWrapper0.createFolder(MessageModule_StatelessSessionBeanWrapper0.java:1087)
at
com.formicary.epix.actions.message.MessageAction.doAddFolder(MessageAction.java:384)
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:585)
at
webwork.util.InjectionUtils$DefaultInjectionImpl.invoke(InjectionUtils.java:61)
at webwork.util.InjectionUtils.invoke(InjectionUtils.java:52)
at webwork.action.ActionSupport.invokeCommand(ActionSupport.java:417)
at webwork.action.ActionSupport.execute(ActionSupport.java:146)
at
webwork.dispatcher.GenericDispatcher.executeAction(GenericDispatcher.java:132)
at
webwork.dispatcher.ServletDispatcher.service(ServletDispatcher.java:175)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:333)
at com.evermind._ha.doFilter(.:59)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:123)
at com.evermind._gz.doFilter(.:16)
at
com.formicary.epix.filter.SmartURLFilter.doFilter(SmartURLFilter.java:63)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityFilter.doFilter(CommunityFilter.java:336)
at com.evermind._gz.doFilter(.:20)
at
com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119)
at
com.formicary.epix.filter.SitemeshFilter.parsePage(SitemeshFilter.java:27)
at
com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55)
at
com.formicary.epix.filter.ProxyPageFilter.doFilter(ProxyPageFilter.java:37)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.CommunityPrefixFilter.doFilter(CommunityPrefixFilter.java:70)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:86)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.LoginFilter.doFilter(LoginFilter.java:653)
at com.evermind._gz.doFilter(.:20)
at
com.formicary.epix.filter.RequestFilter.doFilter(RequestFilter.java:261)
at com.evermind._csb._pud(.:379)
at com.evermind._csb._bqc(.:176)
at com.evermind._ax._ltc(.:668)
at com.evermind._ax._ucb(.:193)
at com.evermind._bf.run(.:62)
</pre></small></small></p>"