diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index e4a8d36b34..3217e7e723 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1899,13 +1899,6 @@ public BeanDescriptor descriptor(Class otherType) { return owner.descriptor(otherType); } - /** - * Returns true, if the table is managed (i.e. an existing m2m relation). - */ - public boolean isTableManaged(String tableName) { - return owner.isTableManaged(tableName); - } - /** * Return the order column property. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index b9e16e3f69..1d856b69c7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -90,6 +90,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy private final BootupClasses bootupClasses; private final String serverName; private final List> elementDescriptors = new ArrayList<>(); + private final Set managedTables = new HashSet<>(); private final Map, BeanTable> beanTableMap = new HashMap<>(); private final Map> descMap = new HashMap<>(); private final Map> descQueueMap = new HashMap<>(); @@ -428,8 +429,7 @@ public List> beanTypes(String tableName) { @Override public boolean isTableManaged(String tableName) { - return tableToDescMap.get(tableName.toLowerCase()) != null - || tableToViewDescMap.get(tableName.toLowerCase()) != null; + return managedTables.contains(tableName); } /** @@ -699,6 +699,9 @@ private void readEntityBeanTable() { for (DeployBeanInfo info : deployInfoMap.values()) { BeanTable beanTable = createBeanTable(info); beanTableMap.put(beanTable.getBeanType(), beanTable); + if (beanTable.getBaseTable() != null) { + managedTables.add(beanTable.getBaseTable()); + } } // register non-id embedded beans (after bean tables are created) for (DeployBeanInfo info : embeddedBeans) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 6666ee2182..7860293ff2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -38,6 +38,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST * Join for manyToMany intersection table. */ private final TableJoin intersectionJoin; + private final boolean tableManaged; private final String intersectionPublishTable; private final String intersectionDraftTable; private final boolean orphanRemoval; @@ -100,9 +101,11 @@ public BeanPropertyAssocMany(BeanDescriptor descriptor, DeployBeanPropertyAss this.fetchOrderBy = deploy.getFetchOrderBy(); this.intersectionJoin = deploy.createIntersectionTableJoin(); if (intersectionJoin != null) { + this.tableManaged = deploy.isTableManaged(); this.intersectionPublishTable = intersectionJoin.getTable(); this.intersectionDraftTable = deploy.getIntersectionDraftTable(); } else { + this.tableManaged = false; this.intersectionPublishTable = null; this.intersectionDraftTable = null; } @@ -986,11 +989,11 @@ public boolean isIncludeCascadeSave() { // Note ManyToMany always included as we always 'save' // the relationship via insert/delete of intersection table // REMOVALS means including PrivateOwned relationships - return cascadeInfo.isSave() || hasJoinTable() || ModifyListenMode.REMOVALS == modifyListenMode; + return cascadeInfo.isSave() || (hasJoinTable() && !tableManaged) || ModifyListenMode.REMOVALS == modifyListenMode; } public boolean isIncludeCascadeDelete() { - return cascadeInfo.isDelete() || hasJoinTable() || ModifyListenMode.REMOVALS == modifyListenMode; + return cascadeInfo.isDelete() || (hasJoinTable() && !tableManaged) || ModifyListenMode.REMOVALS == modifyListenMode; } boolean isCascadeDeleteEscalate() { @@ -1118,13 +1121,20 @@ public void bindElementValue(SqlUpdate insert, Object value) { targetDescriptor.bindElementValue(insert, value); } + /** + * Returns true, if this M2M beanproperty has a jointable, where the jointable is managed by an other entity. + */ + public boolean isTableManaged() { + return tableManaged; + } + /** * Returns true, if we must create a m2m join table. */ public boolean createJoinTable() { if (hasJoinTable() && mappedBy() == null) { // only create on other 'owning' side - return !descriptor.isTableManaged(intersectionJoin.getTable()); + return !tableManaged; } else { return false; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 69f3dd628d..72659d5d3e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -39,6 +39,13 @@ public class DeployBeanDescriptor implements DeployBeanDescriptorMeta { private static final Map EMPTY_RAW_MAP = new HashMap<>(); + /** + * Returns true, if the table is managed (i.e. an existing m2m relation). + */ + public boolean isTableManaged(String tableName) { + return manager.isTableManaged(tableName); + } + private static class PropOrder implements Comparator { @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java index c201308e2d..edd41640c3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java @@ -113,6 +113,10 @@ public TableJoin createIntersectionTableJoin() { } } + public boolean isTableManaged() { + return intersectionJoin != null && desc.isTableManaged(intersectionJoin.getTable()); + } + /** * Create the immutable version of the inverse join. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java index b6e5d8a960..3a38865c24 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java @@ -9,8 +9,12 @@ import io.ebeaninternal.server.deploy.*; import jakarta.persistence.PersistenceException; + +import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -94,6 +98,22 @@ private boolean isSaveIntersection() { // OneToMany JoinTable return true; } + if (many.isTableManaged()) { + List tables = new ArrayList<>(3); + tables.add(many.descriptor().baseTable()); + tables.add(many.targetDescriptor().baseTable()); + tables.add(many.intersectionTableJoin().getTable()); + // put all tables in a deterministic order + tables.sort(Comparator.naturalOrder()); + + if (transaction.isSaveAssocManyIntersection(String.join("-", tables), many.descriptor().rootName())) { + // notify others, that we do save this transaction + transaction.isSaveAssocManyIntersection(many.intersectionTableJoin().getTable(), many.descriptor().rootName()); + return true; + } else { + return false; + } + } return transaction.isSaveAssocManyIntersection(many.intersectionTableJoin().getTable(), many.descriptor().rootName()); }