From fcadc63338fecaf4af398aec73e93c00de25e2d2 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Fri, 20 Sep 2019 17:12:32 +1200 Subject: [PATCH 1/3] #1656 #1824 Stateless updates - Remove update deleteMissingChildren option, instead always use orphanRemoval --- src/main/java/io/ebean/DB.java | 12 ---- src/main/java/io/ebean/Database.java | 13 ---- .../java/io/ebean/config/ServerConfig.java | 24 ------- .../server/core/DefaultServer.java | 14 ---- .../server/core/PersistRequestBean.java | 20 ++---- .../ebeaninternal/server/core/Persister.java | 5 -- .../server/deploy/BeanPropertyAssocMany.java | 6 ++ .../server/deploy/BeanPropertyAssocOne.java | 4 -- .../deploy/meta/DeployBeanPropertyAssoc.java | 10 +++ .../meta/DeployBeanPropertyAssocOne.java | 10 --- .../deploy/parse/AnnotationAssocManys.java | 2 + .../deploy/parse/AnnotationAssocOnes.java | 4 +- .../server/persist/DefaultPersister.java | 16 +---- .../ebeaninternal/server/persist/Flags.java | 8 ++- .../server/persist/SaveManyBeans.java | 31 ++++----- .../ebeaninternal/api/TDSpiEbeanServer.java | 4 -- src/test/java/org/tests/cascade/COOne.java | 37 +++++++++++ .../java/org/tests/cascade/COOneMany.java | 34 ++++++++++ src/test/java/org/tests/cascade/CORoot.java | 36 ++++++++++ .../TestCascadeOrphanStatelessUpdate.java | 66 +++++++++++++++++++ .../org/tests/model/softdelete/EsdMaster.java | 5 +- .../TestSoftDeleteStatelessUpdate.java | 11 ++-- .../org/tests/update/TestStatelessUpdate.java | 6 +- 23 files changed, 227 insertions(+), 151 deletions(-) create mode 100644 src/test/java/org/tests/cascade/COOne.java create mode 100644 src/test/java/org/tests/cascade/COOneMany.java create mode 100644 src/test/java/org/tests/cascade/CORoot.java create mode 100644 src/test/java/org/tests/cascade/TestCascadeOrphanStatelessUpdate.java diff --git a/src/main/java/io/ebean/DB.java b/src/main/java/io/ebean/DB.java index 0379bae6d..0e336ec41 100644 --- a/src/main/java/io/ebean/DB.java +++ b/src/main/java/io/ebean/DB.java @@ -396,15 +396,6 @@ public class DB { * called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used). *

*

- * {@link DatabaseConfig#setUpdatesDeleteMissingChildren(boolean)}: When cascade saving to a - * OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children - * that are in the database but are not in the collection are deleted. - *

- *

- * {@link DatabaseConfig#setUpdateChangesOnly(boolean)}: The updateChangesOnly setting - * controls if only the changed properties are included in the update or if all the loaded - * properties are included instead. - *

*
{@code
    *
    *   // A 'stateless update' example
@@ -414,9 +405,6 @@ public class DB {
    *   database.update(customer);
    *
    * }
- * - * @see DatabaseConfig#setUpdatesDeleteMissingChildren(boolean) - * @see DatabaseConfig#setUpdateChangesOnly(boolean) */ public static void update(Object bean) throws OptimisticLockException { getDefault().update(bean); diff --git a/src/main/java/io/ebean/Database.java b/src/main/java/io/ebean/Database.java index ee9ece29d..25d6e6480 100644 --- a/src/main/java/io/ebean/Database.java +++ b/src/main/java/io/ebean/Database.java @@ -1216,9 +1216,6 @@ public interface Database { * database.update(customer); * * } - * - * @see ServerConfig#setUpdatesDeleteMissingChildren(boolean) - * @see ServerConfig#setUpdateChangesOnly(boolean) */ void update(Object bean) throws OptimisticLockException; @@ -1227,16 +1224,6 @@ public interface Database { */ void update(Object bean, Transaction transaction) throws OptimisticLockException; - /** - * Update a bean additionally specifying a transaction and the deleteMissingChildren setting. - * - * @param bean the bean to update - * @param transaction the transaction to use (can be null). - * @param deleteMissingChildren specify false if you do not want 'missing children' of a OneToMany - * or ManyToMany to be automatically deleted. - */ - void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException; - /** * Update a collection of beans. If there is no current transaction one is created and used to * update all the beans in the collection. diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index e8534e30e..43fda49a1 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -355,11 +355,6 @@ public class ServerConfig { */ private boolean updateAllPropertiesInBatch; - /** - * Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children. - */ - private boolean updatesDeleteMissingChildren = true; - /** * Database platform configuration. */ @@ -2423,22 +2418,6 @@ public class ServerConfig { this.updateAllPropertiesInBatch = updateAllPropertiesInBatch; } - /** - * Return true if updates by default delete missing children when cascading save to a OneToMany or - * ManyToMany. When not set this defaults to true. - */ - public boolean isUpdatesDeleteMissingChildren() { - return updatesDeleteMissingChildren; - } - - /** - * Set if updates by default delete missing children when cascading save to a OneToMany or - * ManyToMany. When not set this defaults to true. - */ - public void setUpdatesDeleteMissingChildren(boolean updatesDeleteMissingChildren) { - this.updatesDeleteMissingChildren = updatesDeleteMissingChildren; - } - /** * Return true if query statistics should be collected by ObjectGraphNode. */ @@ -2948,9 +2927,6 @@ public class ServerConfig { skipCacheAfterWrite = p.getBoolean("skipCacheAfterWrite", skipCacheAfterWrite); updateAllPropertiesInBatch = p.getBoolean("updateAllPropertiesInBatch", updateAllPropertiesInBatch); - boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", updatesDeleteMissingChildren); - updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren); - if (p.get("batch.mode") != null || p.get("persistBatching") != null) { throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'"); } diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 87adc2066..04ce0a268 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -1736,30 +1736,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { ((EntityBean) bean)._ebean_getIntercept().setDirty(true); } - /** - * Update the bean using the default 'updatesDeleteMissingChildren' setting. - */ @Override public void update(Object bean) { update(bean, null); } - /** - * Update the bean using the default 'updatesDeleteMissingChildren' setting. - */ @Override public void update(Object bean, Transaction t) { persister.update(checkEntityBean(bean), t); } - /** - * Update the bean specifying the deleteMissingChildren option. - */ - @Override - public void update(Object bean, Transaction t, boolean deleteMissingChildren) { - persister.update(checkEntityBean(bean), t, deleteMissingChildren); - } - @Override public void updateAll(Collection beans) throws OptimisticLockException { updateAll(beans, null); diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java index b892f21c3..67cdc9324 100644 --- a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java @@ -104,8 +104,6 @@ public final class PersistRequestBean extends PersistRequest implements BeanP private boolean notifyCache; - private boolean deleteMissingChildren; - /** * Flag used to detect when only many properties where updated via a cascade. Used to ensure * appropriate caches are updated in that case. @@ -629,20 +627,6 @@ public final class PersistRequestBean extends PersistRequest implements BeanP return beanDescriptor; } - /** - * Return true if a stateless update should also delete any missing details beans. - */ - public boolean isDeleteMissingChildren() { - return deleteMissingChildren; - } - - /** - * Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany. - */ - public void setDeleteMissingChildren(boolean deleteMissingChildren) { - this.deleteMissingChildren = deleteMissingChildren; - } - /** * Prepare the update after potential modifications in a BeanPersistController. */ @@ -1476,4 +1460,8 @@ public final class PersistRequestBean extends PersistRequest implements BeanP } this.saveManyIntersections.add(saveManyIntersection); } + + public boolean isForcedUpdate() { + return Flags.isUpdateForce(flags); + } } diff --git a/src/main/java/io/ebeaninternal/server/core/Persister.java b/src/main/java/io/ebeaninternal/server/core/Persister.java index a0bdf1bb5..2bcda4b88 100644 --- a/src/main/java/io/ebeaninternal/server/core/Persister.java +++ b/src/main/java/io/ebeaninternal/server/core/Persister.java @@ -30,11 +30,6 @@ public interface Persister { */ void update(EntityBean entityBean, Transaction t); - /** - * Update the bean specifying deleteMissingChildren. - */ - void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren); - /** * Force an Insert using the given bean. */ diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index f884bb900..353fc5195 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -49,6 +49,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST private final TableJoin intersectionJoin; private final String intersectionPublishTable; private final String intersectionDraftTable; + private final boolean orphanRemoval; private IntersectionTable intersectionTable; @@ -123,6 +124,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST public BeanPropertyAssocMany(BeanDescriptor descriptor, DeployBeanPropertyAssocMany deploy) { super(descriptor, deploy); this.unidirectional = deploy.isUnidirectional(); + this.orphanRemoval = deploy.isOrphanRemoval(); this.o2mJoinTable = deploy.isO2mJoinTable(); this.hasOrderColumn = deploy.hasOrderColumn(); this.manyToMany = deploy.isManyToMany(); @@ -510,6 +512,10 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST return hasOrderColumn; } + public boolean isOrphanRemoval() { + return orphanRemoval; + } + @Override public boolean isAssocMany() { return true; diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 718a2d23b..aa41e95ab 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -39,11 +39,8 @@ import java.util.Map; public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STreePropertyAssocOne { private final boolean oneToOne; - private final boolean oneToOneExported; - private final boolean orphanRemoval; - private final boolean primaryKeyExport; private final boolean primaryKeyJoin; @@ -89,7 +86,6 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr for (BeanProperty embeddedProp : embeddedProps) { embeddedPropsMap.put(embeddedProp.getName(), embeddedProp); } - } else { embeddedProps = null; embeddedPropsMap = null; diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java index bd2ccc6be..a7e060004 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java +++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java @@ -45,6 +45,8 @@ public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { private PropertyForeignKey foreignKey; + boolean orphanRemoval; + /** * Construct the property. */ @@ -148,6 +150,14 @@ public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { } } + public void setOrphanRemoval() { + orphanRemoval = true; + } + + public boolean isOrphanRemoval() { + return orphanRemoval; + } + /** * Set DocStoreEmbedded deployment information. */ diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java index 4dd42a503..79f253b86 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java +++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java @@ -21,8 +21,6 @@ public class DeployBeanPropertyAssocOne extends DeployBeanPropertyAssoc { private String columnPrefix; - private boolean orphanRemoval; - /** * Create the property. */ @@ -145,14 +143,6 @@ public class DeployBeanPropertyAssocOne extends DeployBeanPropertyAssoc { } } - public void setOrphanRemoval(boolean orphanRemoval) { - this.orphanRemoval = orphanRemoval; - } - - public boolean isOrphanRemoval() { - return orphanRemoval; - } - public void setJoinType(boolean outerJoin) { tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index 3550fc192..55640603b 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -84,11 +84,13 @@ class AnnotationAssocManys extends AnnotationParser { if (oneToMany != null) { readToOne(oneToMany, prop); if (readOrphanRemoval(oneToMany)) { + prop.setOrphanRemoval(); prop.setModifyListenMode(ModifyListenMode.REMOVALS); prop.getCascadeInfo().setDelete(true); } PrivateOwned privateOwned = get(prop, PrivateOwned.class); if (privateOwned != null) { + prop.setOrphanRemoval(); prop.setModifyListenMode(ModifyListenMode.REMOVALS); prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove()); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java index dda924752..4e2084100 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java @@ -225,7 +225,9 @@ public class AnnotationAssocOnes extends AnnotationParser { prop.setNullable(propAnn.optional()); prop.setFetchType(propAnn.fetch()); prop.setMappedBy(propAnn.mappedBy()); - prop.setOrphanRemoval(readOrphanRemoval(propAnn)); + if (readOrphanRemoval(propAnn)) { + prop.setOrphanRemoval(); + } if (!"".equals(propAnn.mappedBy())) { prop.setOneToOneExported(); } diff --git a/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java index e3a93cfad..24541a636 100644 --- a/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java @@ -74,11 +74,8 @@ public final class DefaultPersister implements Persister { private final BeanDescriptorManager beanDescriptorManager; - private final boolean updatesDeleteMissingChildren; - public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr) { this.server = server; - this.updatesDeleteMissingChildren = server.getServerConfig().isUpdatesDeleteMissingChildren(); this.beanDescriptorManager = descMgr; this.persistExecute = new DefaultPersistExecute(binder, server.getServerConfig().getPersistBatchSize()); } @@ -407,17 +404,7 @@ public final class DefaultPersister implements Persister { */ @Override public void update(EntityBean entityBean, Transaction t) { - update(entityBean, t, updatesDeleteMissingChildren); - } - - /** - * Update the bean specifying deleteMissingChildren. - */ - @Override - public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren) { - PersistRequestBean req = createRequest(entityBean, t, PersistRequest.Type.UPDATE); - req.setDeleteMissingChildren(deleteMissingChildren); req.checkDraft(); try { req.initTransIfRequiredWithBatchCascade(); @@ -448,8 +435,7 @@ public final class DefaultPersister implements Persister { @Override public void save(EntityBean bean, Transaction t) { if (bean._ebean_getIntercept().isUpdate()) { - // deleteMissingChildren is false when using 'save' on 'loaded' beans - update(bean, t, false); + update(bean, t); } else { insert(bean, t); } diff --git a/src/main/java/io/ebeaninternal/server/persist/Flags.java b/src/main/java/io/ebeaninternal/server/persist/Flags.java index 0d9a9ccf9..6c200911e 100644 --- a/src/main/java/io/ebeaninternal/server/persist/Flags.java +++ b/src/main/java/io/ebeaninternal/server/persist/Flags.java @@ -78,6 +78,10 @@ public final class Flags { return (state & PUBLISH_MERGE_NORMAL) != 0; } + public static boolean isUpdateForce(int state) { + return !isSet(state, INSERT) && !isSet(state, NORMAL); + } + /** * Return true if the given flag is set. */ @@ -149,10 +153,10 @@ public final class Flags { } private static int set(int state, int flag) { - return (state |= flag); + return state | flag; } private static int unset(int state, int flag) { - return state &= ~flag; + return state & ~flag; } } diff --git a/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java b/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java index afbf394cd..883947439 100644 --- a/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java +++ b/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java @@ -38,7 +38,6 @@ public class SaveManyBeans extends SaveManyBase { private Collection collection; private final DefaultPersister persister; - private boolean deleteMissing; private int sortOrder; SaveManyBeans(boolean insertedParent, BeanPropertyAssocMany many, EntityBean parentBean, PersistRequestBean request, DefaultPersister persister) { @@ -56,18 +55,17 @@ public class SaveManyBeans extends SaveManyBase { void save() { if (many.hasJoinTable()) { - // check if we can save the m2m intersection in this direction // we only allow one direction based on first traversed basis boolean saveIntersectionFromThisDirection = isSaveIntersection(); if (cascade) { - saveAssocManyDetails(false); + saveAssocManyDetails(); } // for ManyToMany save the 'relationship' via inserts/deletes // into/from the intersection table if (saveIntersectionFromThisDirection) { // only allowed on one direction of a m2m based on beanName - saveAssocManyIntersection(request.isDeleteMissingChildren()); + saveAssocManyIntersection(); } else { resetModifyState(); } @@ -79,7 +77,7 @@ public class SaveManyBeans extends SaveManyBase { } if (cascade) { // potentially deletes 'missing children' for 'stateless update' - saveAssocManyDetails(request.isDeleteMissingChildren()); + saveAssocManyDetails(); } } } @@ -98,13 +96,9 @@ public class SaveManyBeans extends SaveManyBase { /** * Save the details from a OneToMany collection. */ - private void saveAssocManyDetails(boolean deleteMissingChildren) { - - this.deleteMissing = deleteMissingChildren; - + private void saveAssocManyDetails() { // check that the list is not null and if it is a BeanCollection // check that is has been populated (don't trigger lazy loading) - collection = BeanCollectionUtil.getActualEntries(value); if (collection != null) { processDetails(); @@ -127,7 +121,7 @@ public class SaveManyBeans extends SaveManyBase { targetDescriptor.preAllocateIds(collection.size()); } - if (deleteMissing) { + if (!insertedParent && many.isOrphanRemoval() && request.isForcedUpdate()) { // collect the Id's (to exclude from deleteManyDetails) List detailIds = collectIds(collection, targetDescriptor, isMap); // deleting missing children - children not in our collected detailIds @@ -142,7 +136,6 @@ public class SaveManyBeans extends SaveManyBase { transaction.depth(-1); } - private void saveAllBeans(BeanProperty orderColumn) { // if a map, then we get the key value and @@ -240,17 +233,16 @@ public class SaveManyBeans extends SaveManyBase { * This is done via MapBeans. *

*/ - private void saveAssocManyIntersection(boolean deleteMissingChildren) { + private void saveAssocManyIntersection() { if (value == null) { return; } if (request.isQueueManyIntersection()) { // queue/delay until bean persist request is flushed - this.deleteMissing = deleteMissingChildren; request.addManyIntersection(this); } else { - saveAssocManyIntersection(deleteMissingChildren, false); + saveAssocManyIntersection(false); } } @@ -258,13 +250,14 @@ public class SaveManyBeans extends SaveManyBase { * Push intersection table changes onto batch flush queue. */ public void saveIntersectionBatch() { - saveAssocManyIntersection(deleteMissing, true); + saveAssocManyIntersection(true); } - private void saveAssocManyIntersection(boolean deleteMissingChildren, boolean queue) { + private void saveAssocManyIntersection(boolean queue) { + boolean forcedUpdate = request.isForcedUpdate(); boolean vanillaCollection = !(value instanceof BeanCollection); - if (vanillaCollection || deleteMissingChildren) { + if (vanillaCollection || forcedUpdate) { // delete all intersection rows and then treat all // beans in the collection as additions persister.deleteManyIntersection(parentBean, many, transaction, publish, queue); @@ -273,7 +266,7 @@ public class SaveManyBeans extends SaveManyBase { Collection deletions = null; Collection additions; - if (insertedParent || vanillaCollection || deleteMissingChildren) { + if (insertedParent || vanillaCollection || forcedUpdate) { // treat everything in the list/set/map as an intersection addition if (value instanceof Map) { additions = ((Map) value).values(); diff --git a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java index 5da3fdabf..636e4db84 100644 --- a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java +++ b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java @@ -809,10 +809,6 @@ public class TDSpiEbeanServer implements SpiEbeanServer { public void update(Object bean, Transaction t) throws OptimisticLockException { } - @Override - public void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException { - } - @Override public void insert(Object bean) { } diff --git a/src/test/java/org/tests/cascade/COOne.java b/src/test/java/org/tests/cascade/COOne.java new file mode 100644 index 000000000..cc2430574 --- /dev/null +++ b/src/test/java/org/tests/cascade/COOne.java @@ -0,0 +1,37 @@ +package org.tests.cascade; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.util.List; + +import static javax.persistence.CascadeType.ALL; + +@Entity +public class COOne { + + @Id + private long id; + + private final String name; + + @OneToMany(cascade = ALL, orphanRemoval = true) + private List children; + + public COOne(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public List getChildren() { + return children; + } + +} diff --git a/src/test/java/org/tests/cascade/COOneMany.java b/src/test/java/org/tests/cascade/COOneMany.java new file mode 100644 index 000000000..8894ca8ee --- /dev/null +++ b/src/test/java/org/tests/cascade/COOneMany.java @@ -0,0 +1,34 @@ +package org.tests.cascade; + +import io.ebean.annotation.SoftDelete; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class COOneMany { + + @Id + private long id; + + @SoftDelete + private boolean deleted; + + private String name; + + public COOneMany(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public long getId() { + return id; + } + + public boolean isDeleted() { + return deleted; + } +} diff --git a/src/test/java/org/tests/cascade/CORoot.java b/src/test/java/org/tests/cascade/CORoot.java new file mode 100644 index 000000000..68700b0d3 --- /dev/null +++ b/src/test/java/org/tests/cascade/CORoot.java @@ -0,0 +1,36 @@ +package org.tests.cascade; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToOne; + +import static javax.persistence.CascadeType.ALL; + +@Entity +public class CORoot { + + @Id + private long id; + + private final String name; + + @OneToOne(cascade = ALL, orphanRemoval = true) + private COOne one; + + public CORoot(String name, COOne one) { + this.name = name; + this.one = one; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public COOne getOne() { + return one; + } +} diff --git a/src/test/java/org/tests/cascade/TestCascadeOrphanStatelessUpdate.java b/src/test/java/org/tests/cascade/TestCascadeOrphanStatelessUpdate.java new file mode 100644 index 000000000..178c5271c --- /dev/null +++ b/src/test/java/org/tests/cascade/TestCascadeOrphanStatelessUpdate.java @@ -0,0 +1,66 @@ +package org.tests.cascade; + +import io.ebean.BaseTestCase; +import io.ebean.DB; +import io.ebean.text.json.JsonContext; +import org.junit.Test; + +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; + +public class TestCascadeOrphanStatelessUpdate extends BaseTestCase { + + @Test + public void update() { + + final CORoot orig = setup(); + + CORoot root = DB.find(CORoot.class, orig.getId()); + assertNotNull(root); + assertThat(root.getOne().getChildren()).hasSize(3); + + final JsonContext jsonContext = DB.json(); + String asJson = jsonContext.toJson(root); + + final CORoot deserialized = jsonContext.toBean(CORoot.class, asJson); + + final COOne one = deserialized.getOne(); + COOneMany removed0 = one.getChildren().remove(0); + COOneMany removed1 = one.getChildren().remove(1); + + one.getChildren().add(new COOneMany("m3")); + one.getChildren().add(new COOneMany("m4")); + + DB.update(deserialized); + + CORoot saved = DB.find(CORoot.class, orig.getId()); + assertNotNull(saved); + assertThat(saved.getOne().getChildren()).hasSize(3); + + List softDeleted = DB.find(COOneMany.class) + .setIncludeSoftDeletes() + .where() + .idIn(asList(removed0.getId(), removed1.getId())) + .findList(); + + assertThat(softDeleted).hasSize(2); + assertThat(softDeleted.stream().map(COOneMany::getId).collect(toList())) + .contains(removed0.getId(), removed1.getId()); + } + + private CORoot setup() { + COOne one = new COOne("one"); + one.getChildren().add(new COOneMany("m0")); + one.getChildren().add(new COOneMany("m1")); + one.getChildren().add(new COOneMany("m2")); + + CORoot root = new CORoot("r0", one); + DB.save(root); + + return root; + } +} diff --git a/src/test/java/org/tests/model/softdelete/EsdMaster.java b/src/test/java/org/tests/model/softdelete/EsdMaster.java index 23bb3d33a..29632338b 100644 --- a/src/test/java/org/tests/model/softdelete/EsdMaster.java +++ b/src/test/java/org/tests/model/softdelete/EsdMaster.java @@ -1,16 +1,17 @@ package org.tests.model.softdelete; -import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.OneToMany; import java.util.List; +import static javax.persistence.CascadeType.ALL; + @Entity public class EsdMaster extends BaseSoftDelete { String name; - @OneToMany(mappedBy = "master", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "master", cascade = ALL, orphanRemoval = true) List details; public EsdMaster(String name) { diff --git a/src/test/java/org/tests/softdelete/TestSoftDeleteStatelessUpdate.java b/src/test/java/org/tests/softdelete/TestSoftDeleteStatelessUpdate.java index 720447f58..cf06b2c0f 100644 --- a/src/test/java/org/tests/softdelete/TestSoftDeleteStatelessUpdate.java +++ b/src/test/java/org/tests/softdelete/TestSoftDeleteStatelessUpdate.java @@ -1,7 +1,7 @@ package org.tests.softdelete; import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import io.ebean.DB; import io.ebeantest.LoggedSql; import org.junit.Test; import org.tests.model.softdelete.EsdDetail; @@ -21,8 +21,7 @@ public class TestSoftDeleteStatelessUpdate extends BaseTestCase { master.getDetails().add(new EsdDetail("d2")); master.getDetails().add(new EsdDetail("d3")); - - Ebean.save(master); + DB.save(master); EsdMaster upd = new EsdMaster("m1-modified"); upd.setId(master.getId()); @@ -38,7 +37,7 @@ public class TestSoftDeleteStatelessUpdate extends BaseTestCase { LoggedSql.start(); - Ebean.getDefaultServer().update(upd, null, true); + DB.update(upd); List sql = LoggedSql.collect(); assertThat(sql).hasSize(5); @@ -47,7 +46,7 @@ public class TestSoftDeleteStatelessUpdate extends BaseTestCase { assertThat(sql.get(1)).contains("update esd_detail set deleted=true where master_id = ? and not"); } - EsdMaster fetchedWithSoftDeletes = Ebean.find(EsdMaster.class) + EsdMaster fetchedWithSoftDeletes = DB.find(EsdMaster.class) .setId(master.getId()) .setIncludeSoftDeletes() .fetch("details") @@ -59,7 +58,7 @@ public class TestSoftDeleteStatelessUpdate extends BaseTestCase { assertThat(sql).hasSize(1); assertThat(sql.get(0)).contains("left join esd_detail t1 on t1.master_id = t0.id where t0.id = ?"); - EsdMaster fetchedWithOutSoftDeletes = Ebean.find(EsdMaster.class) + EsdMaster fetchedWithOutSoftDeletes = DB.find(EsdMaster.class) .setId(master.getId()) .fetch("details") .findOne(); diff --git a/src/test/java/org/tests/update/TestStatelessUpdate.java b/src/test/java/org/tests/update/TestStatelessUpdate.java index 7a3376adf..21b7af20c 100644 --- a/src/test/java/org/tests/update/TestStatelessUpdate.java +++ b/src/test/java/org/tests/update/TestStatelessUpdate.java @@ -48,7 +48,7 @@ public class TestStatelessUpdate extends TransactionalTestCase { updateAll.setId(e.getId()); updateAll.setName("updAllProps"); - server.update(updateAll, null, false); + server.update(updateAll); eBasic = server.find(EBasic.class, e.getId()); assertEquals(e.getStatus(), eBasic.getStatus()); @@ -383,9 +383,7 @@ public class TestStatelessUpdate extends TransactionalTestCase { updateCustomer.getContacts().add(updateContact3); // not adding contact2 but it won't be deleted in this case - boolean deleteMissingChildren = false; - server.update(updateCustomer, null, deleteMissingChildren); - + server.update(updateCustomer); // assert Customer assCustomer = server.find(Customer.class, customer.getId()); From b98739bb49f71b03d885ee3690e012f65c3b0be3 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Fri, 20 Sep 2019 17:18:07 +1200 Subject: [PATCH 2/3] #1656 #1824 Stateless updates - Remove update deleteMissingChildren option, instead always use orphanRemoval Adjust test that doesn't have orphanRemoval (so missing children are not removed here now) --- .../org/tests/update/TestStatelessUpdate.java | 52 ++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/test/java/org/tests/update/TestStatelessUpdate.java b/src/test/java/org/tests/update/TestStatelessUpdate.java index 21b7af20c..28addd519 100644 --- a/src/test/java/org/tests/update/TestStatelessUpdate.java +++ b/src/test/java/org/tests/update/TestStatelessUpdate.java @@ -3,13 +3,12 @@ package org.tests.update; import io.ebean.Ebean; import io.ebean.EbeanServer; import io.ebean.TransactionalTestCase; - +import org.junit.Assert; +import org.junit.Test; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; import org.tests.model.basic.EBasic; import org.tests.model.basic.EBasic.Status; -import org.junit.Assert; -import org.junit.Test; import javax.persistence.EntityNotFoundException; import java.util.ArrayList; @@ -20,7 +19,9 @@ import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; public class TestStatelessUpdate extends TransactionalTestCase { @@ -170,14 +171,14 @@ public class TestStatelessUpdate extends TransactionalTestCase { customerWithChange.setName("new name"); // contacts is not loaded - Assert.assertFalse(containsContacts(customerWithChange)); + assertFalse(containsContacts(customerWithChange)); server.update(customerWithChange); Customer result = Ebean.find(Customer.class, customer.getId()); // assert null list was ignored (missing children not deleted) Assert.assertNotNull(result.getContacts()); - Assert.assertFalse("the contacts mustn't be deleted", result.getContacts().isEmpty()); + assertFalse("the contacts mustn't be deleted", result.getContacts().isEmpty()); } /** @@ -208,14 +209,14 @@ public class TestStatelessUpdate extends TransactionalTestCase { customerWithChange.getContacts(); // contacts has been initialised to empty BeanList - Assert.assertTrue(containsContacts(customerWithChange)); + assertTrue(containsContacts(customerWithChange)); server.update(customerWithChange); Customer result = Ebean.find(Customer.class, customer.getId()); // assert empty bean list was ignore (missing children not deleted) Assert.assertNotNull(result.getContacts()); - Assert.assertFalse("the contacts mustn't be deleted", result.getContacts().isEmpty()); + assertFalse("the contacts mustn't be deleted", result.getContacts().isEmpty()); } @Test @@ -240,14 +241,13 @@ public class TestStatelessUpdate extends TransactionalTestCase { // with Ebean enhancement this loads the an empty contacts BeanList customerWithChange.setContacts(Collections.emptyList()); - Assert.assertTrue(containsContacts(customerWithChange)); + assertTrue(containsContacts(customerWithChange)); server.update(customerWithChange); Customer result = Ebean.find(Customer.class, customer.getId()); // assert empty bean list was ignore (missing children not deleted) - Assert.assertNotNull(result.getContacts()); - Assert.assertTrue("the contacts were deleted", result.getContacts().isEmpty()); + assertThat(result.getContacts()).hasSize(1); } private boolean containsContacts(Customer cust) { @@ -316,9 +316,7 @@ public class TestStatelessUpdate extends TransactionalTestCase { updateContact1.setId(contact1.getId()); updateContact1.setLastName("contact1-changed"); - Contact updateContact3 = new Contact(); - //updateContact3.setId(contact3.getId()); updateContact3.setLastName("contact3-added"); Customer updateCustomer = new Customer(); @@ -326,31 +324,25 @@ public class TestStatelessUpdate extends TransactionalTestCase { updateCustomer.getContacts().add(updateContact1); updateCustomer.getContacts().add(updateContact3); - // not adding contact2 so it will get deleted - //updateCustomer.getContacts().add(updateContact2); - server.update(updateCustomer); - // assert Customer assCustomer = server.find(Customer.class, customer.getId()); List assContacts = assCustomer.getContacts(); - assertEquals(2, assContacts.size()); + assertThat(assContacts).hasSize(3); Set ids = new LinkedHashSet<>(); Set names = new LinkedHashSet<>(); for (Contact contact : assContacts) { ids.add(contact.getId()); names.add(contact.getLastName()); } - Assert.assertTrue(ids.contains(contact1.getId())); - Assert.assertTrue(ids.contains(updateContact3.getId())); - Assert.assertFalse(ids.contains(contact2.getId())); - - Assert.assertTrue(names.contains(updateContact1.getLastName())); - Assert.assertTrue(names.contains(updateContact3.getLastName())); + assertTrue(ids.contains(contact1.getId())); + assertTrue(ids.contains(updateContact3.getId())); + assertTrue(ids.contains(contact2.getId())); + assertTrue(names.contains(updateContact1.getLastName())); + assertTrue(names.contains(updateContact3.getLastName())); } - @Test public void testStatelessRecursiveUpdateWithChangesInDetailOnlyAnd() { // arrange @@ -398,12 +390,12 @@ public class TestStatelessUpdate extends TransactionalTestCase { ids.add(contact.getId()); names.add(contact.getLastName()); } - Assert.assertTrue(ids.contains(contact1.getId())); - Assert.assertTrue(ids.contains(updateContact3.getId())); - Assert.assertTrue(ids.contains(contact2.getId())); + assertTrue(ids.contains(contact1.getId())); + assertTrue(ids.contains(updateContact3.getId())); + assertTrue(ids.contains(contact2.getId())); - Assert.assertTrue(names.contains(updateContact1.getLastName())); - Assert.assertTrue(names.contains(contact2.getLastName())); - Assert.assertTrue(names.contains(updateContact3.getLastName())); + assertTrue(names.contains(updateContact1.getLastName())); + assertTrue(names.contains(contact2.getLastName())); + assertTrue(names.contains(updateContact3.getLastName())); } } From f0bc4c786c62cdd899dfff7b05580dbabd3f44b4 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Tue, 24 Sep 2019 22:36:17 +1200 Subject: [PATCH 3/3] #1826 - Remove PrivateOwned, migrate to orphanRemoval=true attribute on OneToMany --- pom.xml | 2 +- .../deploy/parse/AnnotationAssocManys.java | 7 -- .../TestPrivateOwnedNoCascadeRemove.java | 58 -------------- .../inheritance/OrganizationTreeNode.java | 8 +- .../org/tests/model/basic/AnimalShelter.java | 8 +- .../tests/model/basic/TMapSuperEntity.java | 3 - .../org/tests/model/basic/TSDetailTwo.java | 74 ----------------- .../org/tests/model/basic/TSMasterTwo.java | 79 ------------------- .../org/tests/model/embedded/EEmbOuter.java | 5 +- .../org/tests/model/selfref/ResourceFile.java | 5 +- 10 files changed, 9 insertions(+), 240 deletions(-) delete mode 100644 src/test/java/org/tests/cascade/TestPrivateOwnedNoCascadeRemove.java delete mode 100644 src/test/java/org/tests/model/basic/TSDetailTwo.java delete mode 100644 src/test/java/org/tests/model/basic/TSMasterTwo.java diff --git a/pom.xml b/pom.xml index cd0bce45b..4c4cb7a95 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ io.ebean ebean-annotation - 5.1 + 6.1 diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index 55640603b..829377ff0 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.parse; import io.ebean.annotation.DbForeignKey; import io.ebean.annotation.FetchPreference; import io.ebean.annotation.HistoryExclude; -import io.ebean.annotation.PrivateOwned; import io.ebean.annotation.Where; import io.ebean.bean.BeanCollection.ModifyListenMode; import io.ebean.config.BeanNotRegisteredException; @@ -88,12 +87,6 @@ class AnnotationAssocManys extends AnnotationParser { prop.setModifyListenMode(ModifyListenMode.REMOVALS); prop.getCascadeInfo().setDelete(true); } - PrivateOwned privateOwned = get(prop, PrivateOwned.class); - if (privateOwned != null) { - prop.setOrphanRemoval(); - prop.setModifyListenMode(ModifyListenMode.REMOVALS); - prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove()); - } OrderColumn orderColumn = get(prop, OrderColumn.class); if (orderColumn != null) { // need to cascade as we set the order on cascade diff --git a/src/test/java/org/tests/cascade/TestPrivateOwnedNoCascadeRemove.java b/src/test/java/org/tests/cascade/TestPrivateOwnedNoCascadeRemove.java deleted file mode 100644 index 473f96d88..000000000 --- a/src/test/java/org/tests/cascade/TestPrivateOwnedNoCascadeRemove.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.tests.cascade; - -import io.ebean.BaseTestCase; -import io.ebean.Ebean; -import io.ebean.annotation.IgnorePlatform; -import io.ebean.annotation.Platform; -import io.ebean.bean.BeanCollection; -import org.tests.model.basic.TSDetailTwo; -import org.tests.model.basic.TSMasterTwo; -import org.junit.Assert; -import org.junit.Test; - -import java.util.List; -import java.util.Set; - -public class TestPrivateOwnedNoCascadeRemove extends BaseTestCase { - - @IgnorePlatform(Platform.NUODB) - @Test - public void test() { - - TSMasterTwo m0 = new TSMasterTwo(); - m0.setName("m1"); - - m0.addDetail(new TSDetailTwo("m1 detail 1")); - m0.addDetail(new TSDetailTwo("m1 detail 2")); - - Ebean.save(m0); - - TSMasterTwo master = Ebean.find(TSMasterTwo.class, m0.getId()); - List details = master.getDetails(); - - TSDetailTwo removedDetail = details.remove(1); - - BeanCollection bc = (BeanCollection) details; - Set modifyRemovals = bc.getModifyRemovals(); - - Assert.assertNotNull(modifyRemovals); - Assert.assertTrue(modifyRemovals.size() == 1); - Assert.assertTrue(modifyRemovals.contains(removedDetail)); - - Ebean.save(master); - - TSMasterTwo masterReload = Ebean.find(TSMasterTwo.class, m0.getId()); - List detailsReload = masterReload.getDetails(); - - // the removed bean has really been removed - Assert.assertTrue(detailsReload.size() == 1); - - try { - Ebean.delete(masterReload); - Assert.fail("delete should error"); - } catch (Exception e) { - Assert.assertTrue("delete failed", true); - } - } - -} diff --git a/src/test/java/org/tests/inheritance/OrganizationTreeNode.java b/src/test/java/org/tests/inheritance/OrganizationTreeNode.java index ae8d4b4a6..f03850f1c 100644 --- a/src/test/java/org/tests/inheritance/OrganizationTreeNode.java +++ b/src/test/java/org/tests/inheritance/OrganizationTreeNode.java @@ -1,13 +1,12 @@ package org.tests.inheritance; -import javax.persistence.CascadeType; import javax.persistence.Entity; -import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.validation.constraints.NotNull; -import io.ebean.annotation.PrivateOwned; +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.LAZY; /** * Model class to reference an organization tree node. @@ -22,9 +21,8 @@ public class OrganizationTreeNode { private String name; - @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parentTreeNode") + @OneToOne(cascade = ALL, fetch = LAZY, mappedBy = "parentTreeNode", orphanRemoval = true) @NotNull - @PrivateOwned private OrganizationNode organizationNode; public Long getId() { diff --git a/src/test/java/org/tests/model/basic/AnimalShelter.java b/src/test/java/org/tests/model/basic/AnimalShelter.java index f585c9dde..761a8d4c7 100644 --- a/src/test/java/org/tests/model/basic/AnimalShelter.java +++ b/src/test/java/org/tests/model/basic/AnimalShelter.java @@ -1,14 +1,13 @@ package org.tests.model.basic; -import io.ebean.annotation.PrivateOwned; - -import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Version; import java.util.List; +import static javax.persistence.CascadeType.PERSIST; + @Entity public class AnimalShelter { @@ -20,8 +19,7 @@ public class AnimalShelter { String name; - @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "shelter") - @PrivateOwned + @OneToMany(cascade = PERSIST, mappedBy = "shelter", orphanRemoval = true) List animals; public Long getId() { diff --git a/src/test/java/org/tests/model/basic/TMapSuperEntity.java b/src/test/java/org/tests/model/basic/TMapSuperEntity.java index 10d46ac47..04a054372 100644 --- a/src/test/java/org/tests/model/basic/TMapSuperEntity.java +++ b/src/test/java/org/tests/model/basic/TMapSuperEntity.java @@ -1,14 +1,11 @@ package org.tests.model.basic; -import io.ebean.annotation.UpdateMode; - import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "t_mapsuper1") -@UpdateMode(updateChangesOnly = true) public class TMapSuperEntity extends TMappedSuper2 { private static final long serialVersionUID = 1L; diff --git a/src/test/java/org/tests/model/basic/TSDetailTwo.java b/src/test/java/org/tests/model/basic/TSDetailTwo.java deleted file mode 100644 index 4ef810351..000000000 --- a/src/test/java/org/tests/model/basic/TSDetailTwo.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.tests.model.basic; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -/** - * A basic entity to test simple things. - */ -@Entity -@Table(name = "ts_detail_two") -public class TSDetailTwo { - - @Id - Integer id; - - String name; - - String description; - - boolean active; - - @ManyToOne - TSMasterTwo master; - - public TSDetailTwo(String name) { - this.name = name; - } - - public TSDetailTwo() { - - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public boolean isActive() { - return active; - } - - public void setActive(boolean active) { - this.active = active; - } - - public TSMasterTwo getMaster() { - return master; - } - - public void setMaster(TSMasterTwo master) { - this.master = master; - } -} diff --git a/src/test/java/org/tests/model/basic/TSMasterTwo.java b/src/test/java/org/tests/model/basic/TSMasterTwo.java deleted file mode 100644 index 471e5ce07..000000000 --- a/src/test/java/org/tests/model/basic/TSMasterTwo.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.tests.model.basic; - -import io.ebean.annotation.PrivateOwned; - -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import java.util.ArrayList; -import java.util.List; - -/** - * A basic entity to test simple things. - */ -@Entity -@Table(name = "ts_master_two") -public class TSMasterTwo { - - @Id - Integer id; - - String name; - - String description; - - boolean active; - - @OneToMany(cascade = CascadeType.ALL, mappedBy = "master") - @PrivateOwned(cascadeRemove = false) - List details; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public boolean isActive() { - return active; - } - - public void setActive(boolean active) { - this.active = active; - } - - public List getDetails() { - return details; - } - - public void setDetails(List details) { - this.details = details; - } - - public void addDetail(TSDetailTwo detail) { - if (details == null) { - details = new ArrayList<>(); - } - details.add(detail); - } -} diff --git a/src/test/java/org/tests/model/embedded/EEmbOuter.java b/src/test/java/org/tests/model/embedded/EEmbOuter.java index d573e22b1..1ae4da057 100644 --- a/src/test/java/org/tests/model/embedded/EEmbOuter.java +++ b/src/test/java/org/tests/model/embedded/EEmbOuter.java @@ -1,7 +1,5 @@ package org.tests.model.embedded; -import io.ebean.annotation.PrivateOwned; - import javax.persistence.CascadeType; import javax.persistence.Embedded; import javax.persistence.Entity; @@ -20,8 +18,7 @@ public class EEmbOuter { @Version private int updateCount; - @OneToMany(cascade = CascadeType.ALL) - @PrivateOwned + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) List inners; @Embedded diff --git a/src/test/java/org/tests/model/selfref/ResourceFile.java b/src/test/java/org/tests/model/selfref/ResourceFile.java index b8e9b964f..3be5e7674 100644 --- a/src/test/java/org/tests/model/selfref/ResourceFile.java +++ b/src/test/java/org/tests/model/selfref/ResourceFile.java @@ -1,7 +1,5 @@ package org.tests.model.selfref; -import io.ebean.annotation.PrivateOwned; - import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; @@ -23,8 +21,7 @@ public class ResourceFile extends BaseResourceFile { @JoinColumn(name = "parentresourcefileid", nullable = true) private ResourceFile parent; - @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "parent") - @PrivateOwned + @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "parent", orphanRemoval = true) private Set alternatives = new HashSet<>(); @Column(name = "name", length = 128, nullable = false)