mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Merge branch 'feature/1656'
This commit is contained in:
@@ -396,15 +396,6 @@ public class DB {
|
||||
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link DatabaseConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link DatabaseConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
|
||||
* controls if only the changed properties are included in the update or if all the loaded
|
||||
* properties are included instead.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // A 'stateless update' example
|
||||
@@ -414,9 +405,6 @@ public class DB {
|
||||
* database.update(customer);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see DatabaseConfig#setUpdatesDeleteMissingChildren(boolean)
|
||||
* @see DatabaseConfig#setUpdateChangesOnly(boolean)
|
||||
*/
|
||||
public static void update(Object bean) throws OptimisticLockException {
|
||||
getDefault().update(bean);
|
||||
|
||||
@@ -1216,9 +1216,6 @@ public interface Database {
|
||||
* database.update(customer);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @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.
|
||||
|
||||
@@ -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'");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -104,8 +104,6 @@ public final class PersistRequestBean<T> 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<T> 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<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
this.saveManyIntersections.add(saveManyIntersection);
|
||||
}
|
||||
|
||||
public boolean isForcedUpdate() {
|
||||
return Flags.isUpdateForce(flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> 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<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public BeanPropertyAssocMany(BeanDescriptor<?> descriptor, DeployBeanPropertyAssocMany<T> 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<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return hasOrderColumn;
|
||||
}
|
||||
|
||||
public boolean isOrphanRemoval() {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
return true;
|
||||
|
||||
@@ -39,11 +39,8 @@ import java.util.Map;
|
||||
public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> 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<T> extends BeanPropertyAssoc<T> implements STr
|
||||
for (BeanProperty embeddedProp : embeddedProps) {
|
||||
embeddedPropsMap.put(embeddedProp.getName(), embeddedProp);
|
||||
}
|
||||
|
||||
} else {
|
||||
embeddedProps = null;
|
||||
embeddedPropsMap = null;
|
||||
|
||||
@@ -45,6 +45,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
|
||||
private PropertyForeignKey foreignKey;
|
||||
|
||||
boolean orphanRemoval;
|
||||
|
||||
/**
|
||||
* Construct the property.
|
||||
*/
|
||||
@@ -148,6 +150,14 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
public void setOrphanRemoval() {
|
||||
orphanRemoval = true;
|
||||
}
|
||||
|
||||
public boolean isOrphanRemoval() {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DocStoreEmbedded deployment information.
|
||||
*/
|
||||
|
||||
@@ -21,8 +21,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private String columnPrefix;
|
||||
|
||||
private boolean orphanRemoval;
|
||||
|
||||
/**
|
||||
* Create the property.
|
||||
*/
|
||||
@@ -145,14 +143,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -84,14 +83,10 @@ 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.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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object> 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.
|
||||
* </p>
|
||||
*/
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user