mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1844 - Persisting an update of ElementCollection with JDBC batch, ensure delete, insert batch order
This commit is contained in:
@@ -27,7 +27,7 @@ import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
import io.ebeaninternal.server.persist.DeleteMode;
|
||||
import io.ebeaninternal.server.persist.Flags;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
import io.ebeaninternal.server.persist.SaveManyBeans;
|
||||
import io.ebeaninternal.server.persist.SaveMany;
|
||||
import io.ebeaninternal.server.transaction.BeanPersistIdMap;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdate;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
@@ -169,10 +169,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private boolean getterCallback;
|
||||
|
||||
/**
|
||||
* postUpdate notifications. Used to combine bean and element update updates into single postUpdate event.
|
||||
*/
|
||||
private int pendingPostUpdateNotify;
|
||||
private boolean pendingPostUpdateNotify;
|
||||
|
||||
/**
|
||||
* Set to true when post execute has occurred (so includes batch flush).
|
||||
@@ -187,7 +184,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
/**
|
||||
* Many to many intersection table changes that are held for later batch processing.
|
||||
*/
|
||||
private List<SaveManyBeans> saveManyIntersections;
|
||||
private List<SaveMany> saveMany;
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
|
||||
@@ -889,12 +886,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
private void postUpdateNotify() {
|
||||
if (pendingPostUpdateNotify > 0) {
|
||||
// invoke the delayed postUpdate notification (combined with element collection update)
|
||||
if (pendingPostUpdateNotify) {
|
||||
controller.postUpdate(this);
|
||||
} else {
|
||||
// batched update with no element collection, send postUpdate notification once it executes
|
||||
pendingPostUpdateNotify = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -917,6 +910,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
saveQueuedMany();
|
||||
postExecute = true;
|
||||
if (controller != null) {
|
||||
controllerPost();
|
||||
@@ -944,12 +938,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (isLogSummary()) {
|
||||
logSummary();
|
||||
}
|
||||
saveQueuedManyIntersection();
|
||||
}
|
||||
|
||||
private void saveQueuedManyIntersection() {
|
||||
if (saveManyIntersections != null) {
|
||||
saveManyIntersections.forEach(SaveManyBeans::saveIntersectionBatch);
|
||||
private void saveQueuedMany() {
|
||||
if (saveMany != null) {
|
||||
saveMany.forEach(SaveMany::saveBatch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -960,19 +953,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (controller != null && !dirty) {
|
||||
// fire preUpdate notification when only element collection updated
|
||||
controller.preUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine with the beans postUpdate event notification.
|
||||
*/
|
||||
public boolean postElementCollectionUpdate() {
|
||||
if (controller != null) {
|
||||
pendingPostUpdateNotify += 2;
|
||||
pendingPostUpdateNotify = true;
|
||||
}
|
||||
if (!dirty) {
|
||||
setNotifyCache();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNotifyCache() {
|
||||
return notifyCache;
|
||||
}
|
||||
|
||||
@@ -982,13 +970,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
controller.postInsert(this);
|
||||
break;
|
||||
case UPDATE:
|
||||
if (pendingPostUpdateNotify == -1) {
|
||||
// notify now - batched bean update with no element collection
|
||||
controller.postUpdate(this);
|
||||
} else {
|
||||
// delay notify to combine with element collection update
|
||||
pendingPostUpdateNotify++;
|
||||
}
|
||||
controller.postUpdate(this);
|
||||
break;
|
||||
case DELETE_SOFT:
|
||||
controller.postSoftDelete(this);
|
||||
@@ -1105,7 +1087,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
updatedManysOnly = true;
|
||||
setNotifyCache();
|
||||
addPostCommitListeners();
|
||||
saveQueuedManyIntersection();
|
||||
saveQueuedMany();
|
||||
}
|
||||
notifyCacheOnComplete();
|
||||
postUpdateNotify();
|
||||
@@ -1444,20 +1426,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the intersection table updates should be queued and batched.
|
||||
* Return true if the intersection table updates or element collection updates should be queued.
|
||||
*/
|
||||
public boolean isQueueManyIntersection() {
|
||||
public boolean isQueueSaveMany() {
|
||||
return !postExecute;
|
||||
}
|
||||
|
||||
/**
|
||||
* The intersection table updates to the batch executed later on postExecute.
|
||||
* The intersection table updates or element collection to the batch executed later on postExecute.
|
||||
*/
|
||||
public void addManyIntersection(SaveManyBeans saveManyIntersection) {
|
||||
if (this.saveManyIntersections == null) {
|
||||
this.saveManyIntersections = new ArrayList<>();
|
||||
public void addSaveMany(SaveMany saveManyRequest) {
|
||||
if (this.saveMany == null) {
|
||||
this.saveMany = new ArrayList<>();
|
||||
}
|
||||
this.saveManyIntersections.add(saveManyIntersection);
|
||||
this.saveMany.add(saveManyRequest);
|
||||
}
|
||||
|
||||
public boolean isForcedUpdate() {
|
||||
@@ -1471,7 +1453,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
saveRecurse = true;
|
||||
}
|
||||
|
||||
public void setGeneratedId() {
|
||||
private void setGeneratedId() {
|
||||
beanDescriptor.setGeneratedId(entityBean, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +104,18 @@ public interface Persister {
|
||||
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
* Queue the SqlUpdate for early execution (with JDBC batch).
|
||||
*/
|
||||
void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t);
|
||||
|
||||
/**
|
||||
* Queue the SqlUpdate for late execution (with JDBC batch).
|
||||
*/
|
||||
void addToFlushQueueLast(SpiSqlUpdate update, SpiTransaction t);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
|
||||
@@ -963,7 +963,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return !elementCollection && cascadeInfo.isDelete();
|
||||
}
|
||||
|
||||
public String insertElementCollection() {
|
||||
public SpiSqlUpdate insertElementCollection() {
|
||||
return sqlHelp.insertElementCollection();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ class BeanPropertyAssocManySqlHelp<T> {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String insertElementCollection() {
|
||||
return elementCollectionInsertSql;
|
||||
SpiSqlUpdate insertElementCollection() {
|
||||
return new DefaultSqlUpdate(elementCollectionInsertSql);
|
||||
}
|
||||
|
||||
private static class Cols extends BaseTablePropertyVisitor {
|
||||
|
||||
@@ -151,6 +151,16 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t) {
|
||||
addToFlushQueue(update, t, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToFlushQueueLast(SpiSqlUpdate update, SpiTransaction t) {
|
||||
addToFlushQueue(update, t, false);
|
||||
}
|
||||
|
||||
private void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t, boolean early) {
|
||||
new PersistRequestUpdateSql(server, update, t, persistExecute).addToFlushQueue(early);
|
||||
}
|
||||
@@ -949,18 +959,15 @@ public final class DefaultPersister implements Persister {
|
||||
|
||||
private SaveManyBase saveManyRequest(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
if (!many.isElementCollection()) {
|
||||
return new SaveManyBeans(insertedParent, many, parentBean, request, this);
|
||||
|
||||
return new SaveManyBeans(this, insertedParent, many, parentBean, request);
|
||||
} else if (many.getManyType().isMap()) {
|
||||
return new SaveManyElementCollectionMap(insertedParent, many, parentBean, request);
|
||||
|
||||
return new SaveManyElementCollectionMap(this, insertedParent, many, parentBean, request);
|
||||
} else {
|
||||
return new SaveManyElementCollection(insertedParent, many, parentBean, request);
|
||||
return new SaveManyElementCollection(this, insertedParent, many, parentBean, request);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteManyIntersection(EntityBean bean, BeanPropertyAssocMany<?> many, SpiTransaction t, boolean publish, boolean queue) {
|
||||
|
||||
SpiSqlUpdate sqlDelete = deleteAllIntersection(bean, many, publish);
|
||||
if (queue) {
|
||||
addToFlushQueue(sqlDelete, t, true);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
/**
|
||||
* Save many that can be queued up to execute after the associated
|
||||
* bean has been actually been persisted.
|
||||
*/
|
||||
public interface SaveMany {
|
||||
|
||||
/**
|
||||
* Save the many property (after the associated bean persist).
|
||||
*/
|
||||
void saveBatch();
|
||||
}
|
||||
@@ -14,10 +14,11 @@ import java.io.IOException;
|
||||
/**
|
||||
* Base for saving entity bean collections and element collections.
|
||||
*/
|
||||
abstract class SaveManyBase {
|
||||
abstract class SaveManyBase implements SaveMany {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SaveManyBase.class);
|
||||
|
||||
final DefaultPersister persister;
|
||||
final PersistRequestBean<?> request;
|
||||
final SpiEbeanServer server;
|
||||
final boolean insertedParent;
|
||||
@@ -26,7 +27,8 @@ abstract class SaveManyBase {
|
||||
final EntityBean parentBean;
|
||||
final Object value;
|
||||
|
||||
SaveManyBase(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
SaveManyBase(DefaultPersister persister, boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
this.persister = persister;
|
||||
this.request = request;
|
||||
this.server = request.getServer();
|
||||
this.insertedParent = insertedParent;
|
||||
@@ -41,10 +43,10 @@ abstract class SaveManyBase {
|
||||
*/
|
||||
abstract void save();
|
||||
|
||||
void preElementCollectionUpdate(Object parentId) {
|
||||
void preElementCollectionUpdate() {
|
||||
if (!insertedParent) {
|
||||
request.preElementCollectionUpdate();
|
||||
server.execute(many.deleteByParentId(parentId, null), transaction);
|
||||
persister.addToFlushQueue(many.deleteByParentId(request.getBeanId(), null), transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ abstract class SaveManyBase {
|
||||
|
||||
void postElementCollectionUpdate() {
|
||||
if (!insertedParent) {
|
||||
if (request.postElementCollectionUpdate()) {
|
||||
if (request.isNotifyCache()) {
|
||||
try {
|
||||
String asJson = many.jsonWriteCollection(value);
|
||||
request.addCollectionChange(many.getName(), asJson);
|
||||
|
||||
@@ -37,12 +37,10 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
private final DeleteMode deleteMode;
|
||||
|
||||
private Collection<?> collection;
|
||||
private final DefaultPersister persister;
|
||||
private int sortOrder;
|
||||
|
||||
SaveManyBeans(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request, DefaultPersister persister) {
|
||||
super(insertedParent, many, parentBean, request);
|
||||
this.persister = persister;
|
||||
SaveManyBeans(DefaultPersister persister, boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(persister, insertedParent, many, parentBean, request);
|
||||
this.cascade = many.getCascadeInfo().isSave();
|
||||
this.publish = request.isPublish();
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
@@ -238,9 +236,9 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (request.isQueueManyIntersection()) {
|
||||
if (request.isQueueSaveMany()) {
|
||||
// queue/delay until bean persist request is flushed
|
||||
request.addManyIntersection(this);
|
||||
request.addSaveMany(this);
|
||||
} else {
|
||||
saveAssocManyIntersection(false);
|
||||
}
|
||||
@@ -249,7 +247,8 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
/**
|
||||
* Push intersection table changes onto batch flush queue.
|
||||
*/
|
||||
public void saveIntersectionBatch() {
|
||||
@Override
|
||||
public void saveBatch() {
|
||||
saveAssocManyIntersection(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanCollectionUtil;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
@@ -13,28 +13,44 @@ import java.util.Collection;
|
||||
*/
|
||||
class SaveManyElementCollection extends SaveManyBase {
|
||||
|
||||
SaveManyElementCollection(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(insertedParent, many, parentBean, request);
|
||||
private Collection<?> collection;
|
||||
|
||||
SaveManyElementCollection(DefaultPersister persister, boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(persister, insertedParent, many, parentBean, request);
|
||||
}
|
||||
|
||||
private boolean modifiedCollection() {
|
||||
return collection != null && (insertedParent || BeanCollectionUtil.isModified(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
void save() {
|
||||
|
||||
Collection<?> collection = BeanCollectionUtil.getActualEntries(value);
|
||||
if (collection != null && (insertedParent || BeanCollectionUtil.isModified(value))) {
|
||||
Object parentId = request.getBeanId();
|
||||
preElementCollectionUpdate(parentId);
|
||||
|
||||
transaction.depth(+1);
|
||||
SqlUpdate sqlInsert = server.createSqlUpdate(many.insertElementCollection());
|
||||
for (Object value : collection) {
|
||||
sqlInsert.setNextParameter(parentId);
|
||||
many.bindElementValue(sqlInsert, value);
|
||||
server.execute(sqlInsert, transaction);
|
||||
collection = BeanCollectionUtil.getActualEntries(value);
|
||||
if (modifiedCollection()) {
|
||||
preElementCollectionUpdate();
|
||||
if (insertedParent && request.isQueueSaveMany()) {
|
||||
request.addSaveMany(this);
|
||||
} else {
|
||||
saveCollection();
|
||||
}
|
||||
transaction.depth(-1);
|
||||
resetModifyState();
|
||||
postElementCollectionUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveBatch() {
|
||||
saveCollection();
|
||||
}
|
||||
|
||||
private void saveCollection() {
|
||||
SpiSqlUpdate proto = many.insertElementCollection();
|
||||
Object parentId = request.getBeanId();
|
||||
for (Object value : collection) {
|
||||
final SpiSqlUpdate sqlInsert = proto.copy();
|
||||
sqlInsert.setNextParameter(parentId);
|
||||
many.bindElementValue(sqlInsert, value);
|
||||
persister.addToFlushQueueLast(sqlInsert, transaction);
|
||||
}
|
||||
resetModifyState();
|
||||
postElementCollectionUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanCollectionUtil;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
@@ -14,31 +14,46 @@ import java.util.Set;
|
||||
*/
|
||||
class SaveManyElementCollectionMap extends SaveManyBase {
|
||||
|
||||
SaveManyElementCollectionMap(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(insertedParent, many, parentBean, request);
|
||||
private Set<Map.Entry<?, ?>> entries;
|
||||
|
||||
SaveManyElementCollectionMap(DefaultPersister persister, boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(persister, insertedParent, many, parentBean, request);
|
||||
}
|
||||
|
||||
private boolean modifiedCollection() {
|
||||
return entries != null && (insertedParent || BeanCollectionUtil.isModified(value));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
void save() {
|
||||
|
||||
Set<Map.Entry<?, ?>> entries = (Set<Map.Entry<?, ?>>) BeanCollectionUtil.getActualEntries(value);
|
||||
if (entries != null && (insertedParent || BeanCollectionUtil.isModified(value))) {
|
||||
Object parentId = request.getBeanId();
|
||||
preElementCollectionUpdate(parentId);
|
||||
|
||||
transaction.depth(+1);
|
||||
SqlUpdate sqlInsert = server.createSqlUpdate(many.insertElementCollection());
|
||||
for (Map.Entry<?, ?> entry : entries) {
|
||||
sqlInsert.setNextParameter(parentId);
|
||||
sqlInsert.setNextParameter(entry.getKey());
|
||||
many.bindElementValue(sqlInsert, entry.getValue());
|
||||
server.execute(sqlInsert, transaction);
|
||||
entries = (Set<Map.Entry<?, ?>>) BeanCollectionUtil.getActualEntries(value);
|
||||
if (modifiedCollection()) {
|
||||
preElementCollectionUpdate();
|
||||
if (insertedParent && request.isQueueSaveMany()) {
|
||||
request.addSaveMany(this);
|
||||
} else {
|
||||
saveCollection();
|
||||
}
|
||||
|
||||
transaction.depth(-1);
|
||||
resetModifyState();
|
||||
postElementCollectionUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveBatch() {
|
||||
saveCollection();
|
||||
}
|
||||
|
||||
private void saveCollection() {
|
||||
SpiSqlUpdate proto = many.insertElementCollection();
|
||||
Object parentId = request.getBeanId();
|
||||
for (Map.Entry<?, ?> entry : entries) {
|
||||
final SpiSqlUpdate sqlInsert = proto.copy();
|
||||
sqlInsert.setNextParameter(parentId);
|
||||
sqlInsert.setNextParameter(entry.getKey());
|
||||
many.bindElementValue(sqlInsert, entry.getValue());
|
||||
persister.addToFlushQueueLast(sqlInsert, transaction);
|
||||
}
|
||||
resetModifyState();
|
||||
postElementCollectionUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Version;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class EcsmChild {
|
||||
|
||||
@Id
|
||||
UUID oneId;
|
||||
|
||||
String name;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "ecsm_values", joinColumns = @JoinColumn(name = "host_id", referencedColumnName = "one_id"))
|
||||
Set<String> values = new LinkedHashSet<>();
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public EcsmChild(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "id:" + oneId + " name:" + name + " values:" + values;
|
||||
}
|
||||
|
||||
public UUID getOneId() {
|
||||
return oneId;
|
||||
}
|
||||
|
||||
public void setOneId(UUID oneId) {
|
||||
this.oneId = oneId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Set<String> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(Set<String> values) {
|
||||
this.values = values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
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.ALL;
|
||||
|
||||
@Entity
|
||||
public class EcsmParent {
|
||||
|
||||
@Id
|
||||
private long id;
|
||||
|
||||
@Version
|
||||
private long version;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany(cascade = ALL)
|
||||
private List<EcsmChild> children;
|
||||
|
||||
public EcsmParent(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<EcsmChild> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<EcsmChild> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,36 @@ public class TestElementCollectionBasic extends BaseTestCase {
|
||||
return EcPersonPersistAdapter.eventLog();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertThen_UpdateWhenNotChanged_expect_noChanges() {
|
||||
|
||||
EcPerson person = new EcPerson("Nothing021");
|
||||
person.getPhoneNumbers().add("021 1234");
|
||||
person.getPhoneNumbers().add("021 4321");
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.save(person);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(eventLog()).containsOnly("preInsert", "postInsert");
|
||||
assertThat(sql).hasSize(4);
|
||||
|
||||
final EcPerson found = Ebean.find(EcPerson.class, person.getId());
|
||||
found.getPhoneNumbers().size();
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("from ec_person t0 where t0.id = ?");
|
||||
assertThat(sql.get(1)).contains("from ec_person_phone t0 where");
|
||||
|
||||
// save when not actually changed
|
||||
Ebean.save(found);
|
||||
|
||||
sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).isEmpty();
|
||||
assertThat(eventLog()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcmPerson person = new EcmPerson("Fiona021");
|
||||
EcmPerson person = new EcmPerson("MFiona021");
|
||||
person.getPhoneNumbers().put("home", "021 1234");
|
||||
person.getPhoneNumbers().put("work", "021 4321");
|
||||
DB.save(person);
|
||||
@@ -36,7 +36,7 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
assertThat(sql.get(2)).contains("insert into ecm_person_phone");
|
||||
}
|
||||
|
||||
EcmPerson person1 = new EcmPerson("Fiona09");
|
||||
EcmPerson person1 = new EcmPerson("MFiona09");
|
||||
person1.getPhoneNumbers().put("home", "09 1234");
|
||||
person1.getPhoneNumbers().put("work", "09 4321");
|
||||
person1.getPhoneNumbers().put("mob", "09 9876");
|
||||
@@ -46,10 +46,12 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
|
||||
List<EcmPerson> found =
|
||||
DB.find(EcmPerson.class).where()
|
||||
.startsWith("name", "Fiona0")
|
||||
.startsWith("name", "MFiona0")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
assertThat(found).hasSize(2);
|
||||
|
||||
Map<String, String> phoneNumbers0 = found.get(0).getPhoneNumbers();
|
||||
Map<String, String> phoneNumbers1 = found.get(1).getPhoneNumbers();
|
||||
phoneNumbers0.size();
|
||||
@@ -68,7 +70,7 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
DB.find(EcmPerson.class)
|
||||
.fetch("phoneNumbers")
|
||||
.where()
|
||||
.startsWith("name", "Fiona0")
|
||||
.startsWith("name", "MFiona0")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestElementCollectionCascade extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
EcsmParent parent = new EcsmParent("p1");
|
||||
final List<EcsmChild> children = parent.getChildren();
|
||||
|
||||
children.add(createChild("c0", 2));
|
||||
children.add(createChild("c1", 2));
|
||||
|
||||
DB.save(parent);
|
||||
|
||||
replaceWith(children.get(0), 22);
|
||||
replaceWith(children.get(1), 5);
|
||||
|
||||
parent.setName("p1-mod");
|
||||
DB.save(parent);
|
||||
|
||||
Map<String, Set<String>> childVals = new LinkedHashMap<>();
|
||||
|
||||
final EcsmParent foundParent = DB.find(EcsmParent.class, parent.getId());
|
||||
final List<EcsmChild> children1 = foundParent.getChildren();
|
||||
for (EcsmChild ecsmOne : children1) {
|
||||
final Set<String> values = ecsmOne.getValues();
|
||||
values.size();
|
||||
childVals.put(ecsmOne.getName(), values);
|
||||
}
|
||||
|
||||
Set<String> vals0 = childVals.get("c0");
|
||||
assertThat(vals0).hasSize(22);
|
||||
|
||||
Set<String> vals1 = childVals.get("c1");
|
||||
assertThat(vals1).hasSize(5);
|
||||
}
|
||||
|
||||
private void replaceWith(EcsmChild one, int count) {
|
||||
one.getValues().clear();
|
||||
createChild(one, count);
|
||||
}
|
||||
|
||||
private EcsmChild createChild(String name, int count) {
|
||||
return createChild(new EcsmChild(name), count);
|
||||
}
|
||||
|
||||
private EcsmChild createChild(EcsmChild one, int count) {
|
||||
final String name = one.getName();
|
||||
for (int i = 0; i < count; i++) {
|
||||
one.getValues().add(name + i);
|
||||
}
|
||||
return one;
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -29,18 +29,17 @@ public class TestElementCollectionCascadeMultiple extends BaseTestCase {
|
||||
save(top);
|
||||
|
||||
final List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(9);
|
||||
|
||||
assertThat(sql.get(0)).contains("insert into ecs_person");
|
||||
assertThat(sql.get(1)).contains("-- bind");
|
||||
assertThat(sql.get(2)).contains("insert into ecs_person_phone");
|
||||
assertThat(sql.get(2)).contains("insert into ec_top");
|
||||
assertThat(sql.get(3)).contains("-- bind");
|
||||
assertThat(sql.get(4)).contains("-- bind");
|
||||
assertThat(sql.get(5)).contains("insert into ec_top");
|
||||
assertThat(sql.get(4)).contains("insert into ecs_person_phone");
|
||||
assertThat(sql.get(5)).contains("-- bind");
|
||||
assertThat(sql.get(6)).contains("-- bind");
|
||||
assertThat(sql.get(7)).contains("insert into ec_top_ecs_person");
|
||||
assertThat(sql.get(8)).contains("-- bind");
|
||||
|
||||
assertThat(sql).hasSize(9);
|
||||
}
|
||||
|
||||
@Transactional(batchSize = 20)
|
||||
|
||||
Reference in New Issue
Block a user