Reputation: 77
I have two tables with a m:n relationship. This relationship should be bidirectional when I am extracting the data from the database, so, I need the @ManyToMany
in both entity classes. But, at the same time, I need that the non owner class does not insert the owner class when I perform a persist or merge operation on it.
For example, I have the Gene
class, that is the owner class, and I have the Ontology
class. One Gene has many Ontologies and one Ontology has many Genes. Classical many-to-many relationship. If I persist or merge a Gene I want to insert its Ontologies too, but I don't want that this Ontology insertion insert all other Genes linked to it.
On other hand, if I insert an Ontology I don't want to insert the genes linked to that Ontology.
I have been trying with a lot of JPA tags on the @ManyToMany
and nothing works on the way that I want.
Have any one an Idea to solve this problem?
The Gene class
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = GeneTables.GENEINFO_HAS_ONTOLOGY,
joinColumns =
@JoinColumn(name = "GeneInfo_WID", referencedColumnName = "WID"),
inverseJoinColumns =
@JoinColumn(name = "Ontology_WID", referencedColumnName = "WID"))
private Set<Ontology> ontology;
The Ontology class
@ManyToMany(cascade = CascadeType.REFRESH, mappedBy = "ontology")
private Set<GeneInfo> geneInfo;
I tried all the Cascade types and even without the cascade option. The result is the same.
I have this error when I execute this code
EntityManager em = getEntityManager();
m.getTransaction().begin();
em.persist(ontology);
em.getTransaction().commit();
I have this error:
[EL Warning]: 2012-08-29 14:52:13.013--UnitOfWork(544628019)--java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
at org.jbiowh.core.datasets.ontology.controller.OntologyJpaController.create(OntologyJpaController.java:41)
at org.jbiowh.tools.prototypes.Test.main(Test.java:65)
Solved
I solved the problem. I create controller classes to handle the special cascades.
The create method on the Ontology controller class will be:
if (loadGeneFlag && ontology.getGene() != null && !ontology.getGene().isEmpty()) {
Set<Gene> geneSet = new HashSet<>();
GeneJpaController gController = new GeneJpaController(emf);
for (Gene gene : ontology.getGene()) {
Gene geneOnDB = em.find(Gene.class, gene.getWid());
if (geneOnDB != null) {
geneSet.add(geneOnDB);
} else {
gController.create(gene);
geneSet.add(em.getReference(Gene.class, gene.getWid()));
}
}
ontology.setGene(geneSet);
}
This code will create all gene references using the Gene controller class and not following the cascade operation. This give me the possibility to handle the Gene cascades correctly on the Gene controller class. Now, I don't have any duplicate object neither exceptions.
Upvotes: 0
Views: 1428
Reputation: 18379
Where do these Genes come from?
If they are new, then you need to either persist them, or set cascade persist on the genes relationship. If they are existing, then you need to find them in the context of the current EntityManager/transaction.
If you don't want then persisted, then don't add them to the genes collection.
Upvotes: 1
Reputation: 18379
You must use mappedBy on one side of a bidirectional relationship.
See, http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Bi-directional_Many_to_Many
Upvotes: 0
Reputation: 6738
Set cascade=CascadeType.REFRESH on both entities or remove cascade attributes on both entities.
Upvotes: 0