Roberto Vera Alvarez
Roberto Vera Alvarez

Reputation: 77

How to avoid bidirectional insertion on ManyToMany relationship

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

Answers (3)

James
James

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

James
James

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

Sai Ye Yan Naing Aye
Sai Ye Yan Naing Aye

Reputation: 6738

Set cascade=CascadeType.REFRESH on both entities or remove cascade attributes on both entities.

Upvotes: 0

Related Questions