-
1. Re: Bug in EntityHome.remove() ?
josief Aug 5, 2009 3:49 PM (in response to josief)Hello,
I asked the question more than one year ago and I'm still stuck on this problem!
Any idea?
Thank you
Adrien
-
2. Re: Bug in EntityHome.remove() ?
asookazian Aug 5, 2009 5:55 PM (in response to josief)I am not familiar with EntityHome and the rest of the SAF but you will need to determine why the entity is not managed any longer (i.e. how/when does it become detached)?
Are you using a SMPC which is conversation-scoped (@In vs. @PersistenceContext to inject your EntityManager)? You will need to show more code to get help (specifically the component that calls this remove method).
-
3. Re: Bug in EntityHome.remove() ?
lvdberg Aug 6, 2009 11:27 AM (in response to josief)Adrien,
Have you tried to make a simple bean which does basically the same and does that work. I've found that the EntityHome-component has some problems whenever you have to deal with a complex graph of objects which are lazily loaded. Whenever i query for the complete graph it works great. The compont tends to loose its managed status whenever it has to do some additional querying. I could find the reason either, so I've switched to
normal
action beans with an injected entitymanager.Leo
-
4. Re: Bug in EntityHome.remove() ?
josief Aug 10, 2009 11:48 AM (in response to josief)I tried with simple relationships and simple entities but it doesn't work. I tried this on a simple Root/Leaf structure.
I have a Root which refers a Leaf.
Then I try to delete the leaf.
There is an exception, which is normal but then the leaf becomes not managed by the entityHome and I don't understand why.Does anyone has an idea?
Thank you
Adrien
Root.java
package neurotk.core.model; import static javax.persistence.GenerationType.IDENTITY; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import org.hibernate.validator.Length; import org.hibernate.validator.Pattern; @Entity @Table(name = "ROOT") public class Root implements Serializable, Comparable<Root>, IPersistableEntity { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 2667017876317288717L; @OneToMany private List<Leaf> leafList = new ArrayList<Leaf>(0); /** ID. */ @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "ROOT_ID") private long id; /** last name. */ @Column(name = "NAME", updatable = true, nullable = false) @Length(min = 2, max = 50) @Pattern(regex = "[^\\t\\n\\r\\f\\a\\e]+", message = "#{messages['entity.NameMustNotContainSpecialCharacters']}") private String name; public int compareTo(Root o) { // TODO Auto-generated method stub return 0; } /** * @return the id */ public long getId() { return id; } /** * @param id the id to set */ public void setId(long id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((leafList == null) ? 0 : leafList.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Root other = (Root) obj; if (leafList == null) { if (other.leafList != null) return false; } else if (!leafList.equals(other.leafList)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } /** * @return the leafList */ public List<Leaf> getLeafList() { return leafList; } /** * @param leafList the leafList to set */ public void setLeafList(List<Leaf> leafList) { this.leafList = leafList; } }
Leaf.java:
package neurotk.core.model; import static javax.persistence.GenerationType.IDENTITY; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import org.hibernate.validator.Length; @Entity @Table(name = "LEAF") public class Leaf implements Serializable, Comparable<Leaf>, IPersistableEntity { //~ Members variables ////////////////////////////////////////////////////////////////////////////////////////////// /** The Constant serialVersionUID. */ private static final long serialVersionUID = -6504089456789026052L; @ManyToOne private Root root; /** ID. */ @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "LEAF_ID") private long id; /** center name. */ @Column(name = "NAME", unique = false, nullable = false, updatable = true) @Length(min = 2, max = 200) private String name; /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(final Leaf other) { if (this.getName() != null && other.getName() != null) { return this.getName().compareTo(other.getName()); } return 0; } /** * @return the root */ public Root getRoot() { return root; } /** * @return the id */ public long getId() { return id; } /** * @return the name */ public String getName() { return name; } /** * @param root the root to set */ public void setRoot(Root root) { this.root = root; } /** * @param id the id to set */ public void setId(long id) { this.id = id; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((root == null) ? 0 : root.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Leaf other = (Leaf) obj; if (root == null) { if (other.root != null) return false; } else if (!root.equals(other.root)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
LeafHome.java :
package neurotk.core.action.impl; import javax.ejb.Stateful; import org.inria.neurotk.core.action.interfaces.ILeafHome; import org.inria.neurotk.core.model.Leaf; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; /** * The Class LeafHome. */ @Name("leafHome") @Scope(ScopeType.CONVERSATION) @Stateful public class LeafHome extends SuperClassEntityHome<Leaf> implements ILeafHome { /* * (non-Javadoc) * * @see org.inria.neurotk.core.action.impl.SuperClassEntityHome#canRemove() */ @Override public boolean canRemove() { return true; } @Override public boolean validate() { return true; } }
RootHome.java :
package neurotk.core.action.impl; import javax.ejb.Stateful; import org.inria.neurotk.core.action.interfaces.IRootHome; import org.inria.neurotk.core.model.Root; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; /** * The Class RootHome. */ @Name("rootHome") @Scope(ScopeType.CONVERSATION) @Stateful public class RootHome extends SuperClassEntityHome<Root> implements IRootHome { /* * (non-Javadoc) * * @see org.inria.neurotk.core.action.impl.SuperClassEntityHome#canRemove() */ @Override public boolean canRemove() { return true; } @Override public boolean validate() { return true; } }
SuperClassEntityHome:
package core.action.impl; import java.util.ArrayList; import java.util.List; import javax.ejb.PrePassivate; import javax.ejb.Remove; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.ejb.TransactionManagement; import javax.ejb.TransactionManagementType; import javax.persistence.PersistenceException; import org.inria.neurotk.core.action.interfaces.SuperClassEntityHome; import org.inria.neurotk.core.action.lists.SuperClassQuery; import org.inria.neurotk.core.model.IPersistableEntity; import org.inria.neurotk.util.SuperClassUtil; import org.jboss.seam.Component; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.Logger; import org.jboss.seam.annotations.security.Restrict; import org.jboss.seam.core.Conversation; import org.jboss.seam.framework.EntityHome; import org.jboss.seam.international.Messages; import org.jboss.seam.international.StatusMessages; import org.jboss.seam.international.StatusMessage.Severity; import org.jboss.seam.log.Log; import org.jboss.seam.log.Logging; import org.jboss.seam.persistence.PersistenceProvider; @TransactionManagement(value = TransactionManagementType.CONTAINER) @TransactionAttribute(value = TransactionAttributeType.REQUIRED) public abstract class SuperClassEntityHome<E extends IPersistableEntity> extends EntityHome<E> implements ISuperClassEntityHome<E> { /** Logger. */ @Logger protected Log log = Logging.getLog(this.getClass()); /** * if super.isManaged() returns false, but getInstance() has an Id, then * instance needs to be re-attached to the entityManager in order to prevent * "detached entity passed to persist" exceptions. */ protected void attachInstanceToEntityManager() { if (!super.isManaged() && (getInstance().getId() != 0)) { setInstance(getEntityManager().merge(getInstance())); } } /** * Check that the preConditions are ok before trying to remove. * * @return true, if can remove */ public abstract boolean canRemove(); /** * Clear the instance and redirect to the parent conversation. Used for * "cancel" buttons. */ public void clearInstanceAndRedirect() { clearInstance(); // End the nested conversation and redirect to the parent page view Conversation.instance().endAndRedirect(); } public void create() { super.create(); } /** * Destroy the bean. */ @Remove @Destroy public void destroy() { } /** * Find the entity with the given Id. * * @param id * the id * * @return the entity or null if no entity is found */ @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public E findById(final long id) { return (E) getEntityManager().find(getEntityClass(), id); } /** * Return a list with a single object : the entity with the given Id. If no * entity is found, return an empty list * * @param id * the id * * @return the list< center> */ @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public List<E> findByIdAsList(final long id) { final List<E> resultList = new ArrayList<E>(); final E entity = findById(id); if (entity != null) { resultList.add(entity); } return resultList; } /* * (non-Javadoc) * * @see org.jboss.seam.framework.Home#getId() */ public Long getId() { return (Long) super.getId(); } /** * Simple perist method. * * @return a message if the remove is successfull or null otherwise */ @Override @Restrict("#{!s:hasRole('guestRole')}") @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public String persist() { return this.persist(true); } /** * A simple persist method. * * @param endConversation * the end conversation * * @return a message if the perist is successfull or null otherwise */ @Restrict("#{!s:hasRole('guestRole')}") public String persist(final boolean endConversation) { log.debug("persist : Begin"); attachInstanceToEntityManager(); // Validate if (!validate()) { log.error("persist : return null"); log.error("persist : End"); return null; } final String outcome = super.persist(); if (endConversation) { // End the nested conversation and redirect to the parent page view Conversation.instance().endAndRedirect(); } log.debug("persist : return " + outcome); log.debug("persist : End"); return outcome; } /** * Persists the instance. * * @param end * the conversation if true * @param message * display a message if true * * @return a message if the persist is successfull or null otherwise */ @Restrict("#{!s:hasRole('guestRole')}") public String persistNoEndConversation(final boolean end, final boolean message) { log.debug("persistNoEndConversation : Begin"); attachInstanceToEntityManager(); getEntityManager().persist(getInstance()); getEntityManager().flush(); assignId(PersistenceProvider.instance().getId(getInstance(), getEntityManager())); if (message) { createdMessage(); } raiseAfterTransactionSuccessEvent(); final String outcome = "persisted"; if (end) { // End the nested conversation and redirect to the parent page view Conversation.instance().endAndRedirect(); } log.debug("persistNoEndConversation : return " + outcome); log.debug("persistNoEndConversation : End"); return outcome; } @PrePassivate public void prePassivate() { setEntityManager(null); } /** * Simple remove method. * * @return a message if the remove is successfull or null otherwise */ @Override @Restrict("#{!s:hasRole('guestRole')}") @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public String remove() { return this.remove(true); } /** * Simple remove method. * * @param endConversation * the end conversation * * @return a message if the remove is successfull or null otherwise */ @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) @Restrict("#{!s:hasRole('guestRole')}") public String remove(final boolean endConversation) { log.debug("remove : Begin"); attachInstanceToEntityManager(); String outcome = null; if (canRemove()) { try { log.error("remove : before isManaged = " + isManaged()); outcome = simpleRemove(); if (endConversation) { // End the nested conversation and redirect to the parent page view Conversation.instance().endAndRedirect(); } // update list final String listName = Util.lowerFirstLetter(getSimpleEntityName()) + "List"; final CustomQuery entityList = (CustomQuery) Component.getInstance(listName); entityList.getResultListForceRefresh(); } catch (final PersistenceException exc) { log.error("remove : " + exc.getMessage()); log.error("remove : after isManaged = " + isManaged()); StatusMessages.instance().add(Severity.ERROR, Messages.instance().get("manageData.deleteError")); } finally { clearInstance(); } } log.debug("remove : return " + outcome); log.debug("remove : End"); return outcome; } /** * Removes the instance. * * @param end * the conversation if true * @param message * display a message if true * * @return a message if the remove is successfull or null otherwise */ @Restrict("#{!s:hasRole('guestRole')}") public String removeNoEndConversation(final boolean end, final boolean message) { log.debug("removeNoEndConversation : Begin"); attachInstanceToEntityManager(); String outcome = null; try { getEntityManager().remove(getInstance()); getEntityManager().flush(); if (message) { deletedMessage(); } raiseAfterTransactionSuccessEvent(); outcome = "removed"; if (end) { // End the nested conversation and redirect to the parent page view Conversation.instance().endAndRedirect(); } } catch (final PersistenceException exc) { log.error("removeNoEndConversation : " + exc.getMessage()); StatusMessages.instance().add(Severity.ERROR, Messages.instance().get("manageData.deleteError")); } finally { clearInstance(); } log.debug("removeNoEndConversation : return " + outcome); log.debug("removeNoEndConversation : End"); return outcome; } /** * Sets the id. * * @param id * the id */ public void setId(final Long id) { super.setId(id); } /** * Simple persist method. * * @return a message if the persist is successfull or null otherwise */ @Restrict("#{!s:hasRole('guestRole')}") public String simplePersist() { return super.persist(); } /** * Simple remove method. * * @return a message if the remove is successfull or null otherwise */ @Restrict("#{!s:hasRole('guestRole')}") @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public String simpleRemove() { return super.remove(); } /** * Simple update method. * * @return a message if the update is successfull or null otherwise */ @Restrict("#{!s:hasRole('guestRole')}") public String simpleUpdate() { return super.update(); } /** * Simple update method. * * @return a message if the update is successfull or null otherwise */ @Override @Restrict("#{!s:hasRole('guestRole')}") @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public String update() { return this.update(true); } /** * Simple update method. * * @param endConversation * the end conversation * * @return a message if the update is successfull or null otherwise */ @Restrict("#{!s:hasRole('guestRole')}") public String update(final boolean endConversation) { log.debug("update : Begin"); attachInstanceToEntityManager(); if (!validate()) { log.error("update : return null"); log.error("update : End"); return null; } final String outcome = super.update(); if (endConversation) { // End the nested conversation and redirect to the parent page view Conversation.instance().endAndRedirect(); } log.debug("update : return " + outcome); log.debug("update : End"); return outcome; } /** * Validate before persisting or updating. * * @return true, if validate */ public abstract boolean validate(); }
The Unit test :
LeafHomeTest.javapackage neurotk.test.test; import org.inria.neurotk.core.action.interfaces.IInriaNeuroTkEntityHome; import org.inria.neurotk.core.model.Leaf; import org.inria.neurotk.core.model.Root; import org.inria.neurotk.test.InriaNeuroTkCommonTest; import org.jboss.seam.Component; import org.jboss.seam.annotations.Logger; import org.jboss.seam.log.Log; import org.jboss.seam.log.Logging; import org.testng.annotations.Test; /** * The Class LeafHomeTest. * * @author aferial * @version $Revision: 1.3 $ */ public class LeafHomeTest extends InriaNeuroTkCommonTest { /** Logger. */ @Logger private Log log; /** * Test LeafHome remove. * * @throws Exception * the exception */ @Test public void testLeafDelete() throws Exception { new FacesRequest() { protected void invokeApplication() { // login setValue("#{identity.username}", "admin"); setValue("#{identity.password}", "admin"); invokeMethod("#{identity.login}"); final IInriaNeuroTkEntityHome<Leaf> leafHome = (IInriaNeuroTkEntityHome<Leaf>) Component.getInstance("leafHome"); final IInriaNeuroTkEntityHome<Root> rootHome = (IInriaNeuroTkEntityHome<Root>) Component.getInstance("rootHome"); log = Logging.getLog(LeafHomeTest.class); Root root = new Root(); root.setName("root"); rootHome.setInstance(root); rootHome.persist(); final Leaf leaf = new Leaf(); leaf.setName("leaf"); leaf.setRoot(root); root.getLeafList().add(leaf); leafHome.setInstance(leaf); leafHome.persist(); final long leafId = leaf.getId(); final long rootId = root.getId(); assert leafId != 0; assert rootId != 0; root = rootHome.findById(rootId); assert (!root.getLeafList().isEmpty()); leafHome.setInstance(leaf); final String outcome = leafHome.remove(); assert outcome == null; rootHome.getInstance().getLeafList().remove(leaf); rootHome.update(); leafHome.remove(); assert (leafHome.findById(leafId) == null); assert (leafHome.findByIdAsList(leafId).isEmpty()); } }.run(); } }
The logs :
... DEBUG [inria.neurotk.core.action.impl.UserHome] findUser : End DEBUG [inria.neurotk.core.action.impl.AuthenticatorAction] admin DEBUG [inria.neurotk.core.action.impl.AuthenticatorAction] Successfully authenticated user: admin Hibernate: select top ? role0_.ROLE_ID as ROLE1_26_, role0_.ACCESS_LEVEL as ACCESS2_26_, role0_.DISPLAY_NAME as DISPLAY3_26_, role0_.NAME as NAME26_ from ROLE role0_ where role0_.DISPLAY_NAME=? DEBUG [inria.neurotk.core.action.impl.AuthenticatorAction] Adding Role adminRole to the user admin DEBUG [inria.neurotk.core.action.impl.AuthenticatorAction] applyUserPreferences : Begin DEBUG [inria.neurotk.core.action.impl.AuthenticatorAction] applyUserPreferences : Preferred language for user admin : en DEBUG [inria.neurotk.core.action.impl.AuthenticatorAction] applyUserPreferences : End DEBUG [inria.neurotk.core.action.impl.RootHome] persist : Begin Hibernate: insert into ROOT (ROOT_ID, NAME) values (null, ?) Hibernate: call identity() Hibernate: update USER set EMAIL=?, firstName=?, LAST_LOGIN_ON=?, lastName=?, PASSWORDHASH=?, ROLE_ID=?, USER_PREF_ID=?, USERNAME=? where USER_ID=? DEBUG [inria.neurotk.core.action.impl.RootHome] created entity org.inria.neurotk.core.model.Root 1 DEBUG [inria.neurotk.core.action.impl.RootHome] persist : return persisted DEBUG [inria.neurotk.core.action.impl.RootHome] persist : End DEBUG [inria.neurotk.core.action.impl.LeafHome] persist : Begin Hibernate: insert into LEAF (LEAF_ID, NAME, root_ROOT_ID) values (null, ?, ?) Hibernate: call identity() Hibernate: insert into ROOT_LEAF (ROOT_ROOT_ID, leafList_LEAF_ID) values (?, ?) DEBUG [inria.neurotk.core.action.impl.LeafHome] created entity org.inria.neurotk.core.model.Leaf 1 DEBUG [inria.neurotk.core.action.impl.LeafHome] persist : return persisted DEBUG [inria.neurotk.core.action.impl.LeafHome] persist : End DEBUG [inria.neurotk.core.action.impl.LeafHome] remove : Begin _ERROR [inria.neurotk.core.action.impl.LeafHome] remove : before isManaged = true_ WARN [org.jboss.seam.security.permission.PermissionManager] no permission store available - please install a PermissionStore with the name 'org.jboss.seam.security.jpaPermissionStore' if permission management is required. Hibernate: delete from LEAF where LEAF_ID=? ERROR [org.hibernate.util.JDBCExceptionReporter] failed batch ERROR [org.hibernate.event.def.AbstractFlushingEventListener] Could not synchronize database state with session org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update 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.testng.TestNG.run(TestNG.java:701) at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:73) at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:124) Caused by: java.sql.BatchUpdateException: failed batch at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source) at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source) at org.jboss.resource.adapter.jdbc.CachedPreparedStatement.executeBatch(CachedPreparedStatement.java:476) at org.jboss.resource.adapter.jdbc.WrappedStatement.executeBatch(WrappedStatement.java:518) at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70) at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268) ... 127 more ERROR [inria.neurotk.core.action.impl.LeafHome] remove : org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update _ERROR [inria.neurotk.core.action.impl.LeafHome] remove : after isManaged = false_ DEBUG [inria.neurotk.core.action.impl.LeafHome] remove : return null DEBUG [inria.neurotk.core.action.impl.LeafHome] remove : End DEBUG [inria.neurotk.core.action.impl.RootHome] update : Begin Hibernate: delete from ROOT_LEAF where ROOT_ROOT_ID=? DEBUG [inria.neurotk.core.action.impl.RootHome] updated entity org.inria.neurotk.core.model.Root 1 DEBUG [inria.neurotk.core.action.impl.RootHome] update : return updated DEBUG [inria.neurotk.core.action.impl.RootHome] update : End DEBUG [inria.neurotk.core.action.impl.LeafHome] remove : Begin ERROR [inria.neurotk.core.action.impl.LeafHome] remove : before isManaged = false DEBUG [inria.neurotk.core.action.impl.LeafHome] deleted entity org.inria.neurotk.core.model.Leaf null Hibernate: select top ? leaf0_.LEAF_ID as LEAF1_72_, leaf0_.NAME as NAME72_, leaf0_.root_ROOT_ID as root3_72_ from LEAF leaf0_ order by leaf0_.LEAF_ID desc DEBUG [inria.neurotk.core.action.impl.LeafHome] remove : return removed DEBUG [inria.neurotk.core.action.impl.LeafHome] remove : End FAILED: testLeafDelete java.lang.AssertionError at org.inria.neurotk.test.test.LeafHomeTest$1.invokeApplication(LeafHomeTest.java:74) ... at org.inria.neurotk.test.test.LeafHomeTest.testLeafDelete(LeafHomeTest.java:78) ... Removed 22 stack frames