ManyToMany biderectional
rberehoudougou Mar 13, 2006 6:03 PMHi,
I am experiencing the now famous ManyToMany hiberante entity manager bug. I have read the many post on the forum and tried the alternative : using Set instead of collections, using Lazy Fetch type but none of that works for me. The problem is as follow :
A product belongs to many Classes of Products
A Product belongs to many Famillies of Products
So, when creating a product, the product famillies and Classes can be selected.
import java.io.Serializable; import java.util.Set; import java.util.TreeSet; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Version; import com.elina.commercialis.server.stock.classification.Classe; import com.elina.commercialis.server.stock.classification.Famille; @Entity @Table(name = "produit") public class Produit implements Serializable, Comparable { private long produitID; private String codeProduit; private String libelleProduit; private Set<Classe> classes; private Set<Famille> familles; private Stock stock; private long version; public Produit() { this.stock = new Stock(); stock.setProduit(this); classes = new TreeSet<Classe>(); familles = new TreeSet<Famille>(); } @Version() @Column(name="version") public long getVersion() { return version; } public void setVersion(long version) { this.version = version; } @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER) @JoinTable(name = "classe_produit", joinColumns = { @JoinColumn(name = "produit_id") }, inverseJoinColumns = { @JoinColumn(name = "classe_id") }) public Set<Classe> getClasses() { return classes; } public void setClasses(Set<Classe> classes) { this.classes = classes; } @Column(name = "code_produit") public String getCodeProduit() { return codeProduit; } public void setCodeProduit(String codeProduit) { this.codeProduit = codeProduit; } @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER ) @JoinTable(name = "famille_produit", joinColumns = { @JoinColumn(name = "produit_id") }, inverseJoinColumns = { @JoinColumn(name = "famille_id") }) public Set<Famille> getFamilles() { return familles; } public void setFamilles(Set<Famille> familles) { this.familles = familles; } @Column(name = "libelle_produit") public String getLibelleProduit() { return libelleProduit; } public void setLibelleProduit(String libelleProduit) { this.libelleProduit = libelleProduit; } @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "produit_id") public long getProduitID() { return produitID; } public void setProduitID(long produitID) { this.produitID = produitID; } @OneToOne(cascade = {CascadeType.ALL}) @JoinColumn(name = "stock_id") public Stock getStock() { return stock; } public void setStock(Stock stock) { this.stock = stock; } public boolean equals(Object obj) { if(obj == this) return true; if(!(obj instanceof Produit)) return false; else return this.getCodeProduit().equalsIgnoreCase(((Produit)obj).getCodeProduit()); } public int hashCode() { int result = 17; result = 37 * result + this.getCodeProduit().hashCode(); return result; } public String toString() { return this.getLibelleProduit(); } public int compareTo(Object obj) { if(!(obj instanceof Produit)) return -1; else return this.getCodeProduit().compareTo(((Produit)obj).getCodeProduit()); } }
import java.io.Serializable; import java.util.Collection; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import javax.persistence.Version; import com.elina.commercialis.server.stock.produit.Produit; @Entity @Table(name = "famille") public class Famille implements Serializable, Comparable { private long familleID; private String codeFamille; private String nomFamille; private String description; private Set<Produit> produits; private long version; @Version() @Column(name = "version") public long getVersion() { return version; } public void setVersion(long version) { this.version = version; } @Column(name = "code_famille") public String getCodeFamille() { return codeFamille; } public void setCodeFamille(String codeFamille) { this.codeFamille = codeFamille; } @Column(name = "description") public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "famille_id") public long getFamilleID() { return familleID; } public void setFamilleID(long familleID) { this.familleID = familleID; } @Column(name = "nom_famille") public String getNomFamille() { return nomFamille; } public void setNomFamille(String nomFamille) { this.nomFamille = nomFamille; } @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY, mappedBy="familles" ) public Set<Produit> getProduits() { return produits; } public void setProduits(Set<Produit> produits) { this.produits = produits; } public boolean equals(Object obj) { if(obj == this) return true; if(!(obj instanceof Famille)) return false; else return this.getCodeFamille().equalsIgnoreCase(((Famille)obj).getCodeFamille()); } public int hashCode() { return this.getCodeFamille().hashCode(); } public String toString() { return this.getNomFamille(); } public int compareTo(Object obj) { if(!(obj instanceof Famille)) return -1; else return this.getNomFamille().compareTo(((Famille)obj).getNomFamille()); } }
import java.io.Serializable; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; import javax.persistence.Version; import com.elina.commercialis.server.stock.produit.Produit; @Entity @Table(name = "classe") public class Classe implements Serializable,Comparable { private long classeID; private String codeClasse; private String libelleClasse; private String description; private Set<Produit> produits; private long version; @Version() @Column(name="version") public long getVersion() { return version; } public void setVersion(long version) { this.version = version; } @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "classe_id") public long getClasseID() { return classeID; } public void setClasseID(long classeID) { this.classeID = classeID; } @Column(name = "code_classe") public String getCodeClasse() { return codeClasse; } public void setCodeClasse(String codeClasse) { this.codeClasse = codeClasse; } @Column(name = "description") public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Column(name = "libelle_classe") public String getLibelleClasse() { return libelleClasse; } public void setLibelleClasse(String libelleClasse) { this.libelleClasse = libelleClasse; } @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY, mappedBy="classes" ) public Set<Produit> getProduits() { return produits; } public void setProduits(Set<Produit> produits) { this.produits = produits; } public boolean equals(Object obj) { if(obj == this) return true; if(!(obj instanceof Classe)) return false; else return this.getCodeClasse().equalsIgnoreCase(((Classe)obj).getCodeClasse()); } public int hashCode() { return this.getCodeClasse().hashCode(); } public String toString() { return this.getLibelleClasse(); } public int compareTo(Object obj) { if(!(obj instanceof Classe)) return -1; else return this.getLibelleClasse().compareTo(((Classe)obj).getLibelleClasse()); } }
After a few update of product (adding /removing Classes/Famillies, the system falls over with the following exception :
at java.awt.EventDispatchThread.run(Unknown Source) java.lang.NullPointerException at com.elina.commercialis.server.stock.produit.Produit.hashCode(Produit.java:151) at java.util.HashMap.hash(HashMap.java:264) at java.util.HashMap.put(HashMap.java:382) at java.util.HashSet.add(HashSet.java:194) at java.util.AbstractCollection.addAll(AbstractCollection.java:318) at org.hibernate.collection.PersistentSet.endRead(PersistentSet.java:273) at org.hibernate.engine.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:183) at org.hibernate.engine.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:268) at org.hibernate.engine.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:249) at org.hibernate.loader.Loader.endCollectionLoad(Loader.java:866) at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:853) at org.hibernate.loader.Loader.doQuery(Loader.java:717) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224) at org.hibernate.loader.Loader.loadEntity(Loader.java:1785) at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:93) at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:81) at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:2730) at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:365) at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:346) at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:123) at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:177) at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:87) at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:891) at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:859) at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:266) at org.hibernate.type.EntityType.resolve(EntityType.java:303) at org.hibernate.type.EntityType.nullSafeGet(EntityType.java:217) at org.hibernate.persister.collection.AbstractCollectionPersister.readElement(AbstractCollectionPersister.java:644) at org.hibernate.collection.PersistentSet.readFrom(PersistentSet.java:262) at org.hibernate.loader.Loader.readCollectionElement(Loader.java:994) at org.hibernate.loader.Loader.readCollectionElements(Loader.java:635) at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:580) at org.hibernate.loader.Loader.doQuery(Loader.java:689) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224) at org.hibernate.loader.Loader.loadEntity(Loader.java:1785) at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:93) at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:81) at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:2730) at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:365) at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:346) at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:123) at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:177) at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:87) at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:891) at org.hibernate.impl.SessionImpl.get(SessionImpl.java:828) at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:209) at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:100) at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:52) at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:701) at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:685) at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:689) at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:138) at org.jboss.ejb3.entity.InjectedEntityManager.merge(InjectedEntityManager.java:146) at com.elina.commercialis.server.facade.stock.produit.ProduitSessionFacadeBean.modifier(ProduitSessionFacadeBean.java:29) at com.elina.commercialis.server.facade.stock.produit.ProduitSessionFacadeBean.sauver(ProduitSessionFacadeBean.java:23) 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 org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:109) at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:47) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:79) at org.jboss.aspects.tx.TxInterceptor$RequiresNew.invoke(TxInterceptor.java:262) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:54) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:78) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:47) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:219) at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:107) at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82) at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:660) at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:513) at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:290) at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:344) at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:202) at org.jboss.remoting.RemoteClientInvoker.invoke(RemoteClientInvoker.java:163) at org.jboss.remoting.Client.invoke(Client.java:258) at org.jboss.remoting.Client.invoke(Client.java:221) at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:55) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:61) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.aspects.security.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:55) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:65) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:98) at org.jboss.ejb3.stateless.StatelessRemoteProxy.invoke(StatelessRemoteProxy.java:102) at $Proxy0.sauver(Unknown Source) at com.elina.commercialis.client.delegate.stock.produit.ProduitDelegate.createProduit(ProduitDelegate.java:21) at com.elina.commercialis.client.gui.screens.stock.produit.ProduitTab.enregistrer(ProduitTab.java:81) at com.elina.commercialis.client.util.gui.screens.CommercialisToolbarBuilder$2.actionPerformed(CommercialisToolbarBuilder.java:80) at javax.swing.AbstractButton.fireActionPerformed(Unknown Source) at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.setPressed(Unknown Source) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source) at java.awt.Component.processMouseEvent(Unknown Source) at javax.swing.JComponent.processMouseEvent(Unknown Source) at java.awt.Component.processEvent(Unknown Source) at java.awt.Container.processEvent(Unknown Source) at java.awt.Component.dispatchEventImpl(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Window.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source)
I have checked the DB and there are No row with an empty code_produit. As a matter if fact a NULL constraint is defined on this column.
Using the debugger, it appear that somehow the hasCode method is called with an Object where codeProduit is null !
Any idea ?
This many to many entity manager problem is really taking the fun out of EJB 3.0 !!!!!!!!!!!!!!!!!!!