From bca787b54dd1e64e8c347ec8bd73146c213ae167 Mon Sep 17 00:00:00 2001 From: rbygrave <=> Date: Tue, 2 Oct 2012 14:59:27 +1300 Subject: [PATCH] Fixes to only allow m2m intersection save from one direction based on beanName. Supports BUG 429 : Pull Request: jroper - Fixing ManyToMany association saving bug --- .../ebeaninternal/api/SpiTransaction.java | 5 ++++ .../server/persist/DefaultPersister.java | 13 ++++++++-- .../server/transaction/JdbcTransaction.java | 25 +++++++++++++++++++ .../avaje/tests/model/basic/Attribute.java | 1 - 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java index 372789244..6b8b8bed4 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java @@ -181,4 +181,9 @@ public interface SpiTransaction extends Transaction { *

*/ public Connection getInternalConnection(); + + /** + * Return true if the manyToMany intersection should be persisted for this particular relationship direction. + */ + public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index fac5471bf..e9b849c16 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -719,6 +719,10 @@ public final class DefaultPersister implements Persister { this.deleteMissingChildren = false; this.updateNullProperties = false; } + + public boolean isSaveIntersection() { + return t.isSaveAssocManyIntersection(many.getIntersectionTableJoin().getTable(), many.getBeanDescriptor().getName()); + } private Object getValue() { return many.getValue(parentBean); @@ -764,14 +768,19 @@ public final class DefaultPersister implements Persister { private void saveMany(SaveManyPropRequest saveMany) { if (saveMany.getMany().isManyToMany()) { - // save the beans that are in the manyToMany + + // check if we can save the m2m intersection in this direction + boolean saveIntersectionFromThisDirection = saveMany.isSaveIntersection(); if (saveMany.isCascade()) { // Need explicit Cascade to save the beans on other side saveAssocManyDetails(saveMany, false, saveMany.isUpdateNullProperties()); } // for ManyToMany save the 'relationship' via inserts/deletes // into/from the intersection table - saveAssocManyIntersection(saveMany, saveMany.isDeleteMissingChildren()); + if (saveIntersectionFromThisDirection) { + // only allowed on one direction of a m2m based on beanName + saveAssocManyIntersection(saveMany, saveMany.isDeleteMissingChildren()); + } } else { if (saveMany.isCascade()) { saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren(), saveMany.isUpdateNullProperties()); diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java index 20aa94dec..8812841dd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java @@ -117,6 +117,7 @@ public class JdbcTransaction implements SpiTransaction { HashSet persistingBeans = new HashSet(); HashSet deletingBeansHash; + HashMap m2mIntersectionSave; TransactionLogBuffer logBuffer; @@ -223,6 +224,30 @@ public class JdbcTransaction implements SpiTransaction { return !persistingBeans.add(bean); } + /** + * Return true if the m2m intersection save is allowed from a given bean direction. + * This is to stop m2m intersection management via both directions of a m2m. + */ + @Override + public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) { + if (m2mIntersectionSave == null) { + // first attempt so yes allow this m2m intersection direction + m2mIntersectionSave = new HashMap(); + m2mIntersectionSave.put(intersectionTable, beanName); + return true; + } + String existingBean = m2mIntersectionSave.get(intersectionTable); + if (existingBean == null) { + // first time into this intersection table so allow + m2mIntersectionSave.put(intersectionTable, beanName); + return true; + } + + // only allow if save coming from the same bean type + // to stop saves coming from both directions of m2m + return existingBean.equals(beanName); + } + /** * Return the depth of the current persist request plus the diff. This has the * effect of changing the current depth and returning the new value. Pass diff --git a/src/test/java/com/avaje/tests/model/basic/Attribute.java b/src/test/java/com/avaje/tests/model/basic/Attribute.java index 19a421c90..7aa32e767 100644 --- a/src/test/java/com/avaje/tests/model/basic/Attribute.java +++ b/src/test/java/com/avaje/tests/model/basic/Attribute.java @@ -26,5 +26,4 @@ public class Attribute extends BasicDomain{ this.attributeHolder = attributeHolder; } - }