diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index 9f9ee078f..4098ad870 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -1869,4 +1869,56 @@ public interface EbeanServer { */ List publish(Query query); + /** + * Restore the draft bean back to the live state. + *

+ * The values from the live beans are set back to the draft bean and the + * @DraftDirty and @DraftReset properties are reset. + *

+ * + * @param the type of the entity bean + * @param beanType the type of the entity bean + * @param id the id of the entity bean to restore + * @param transaction the transaction the restore process should use (can be null) + */ + T draftRestore(Class beanType, Object id, Transaction transaction); + + /** + * Restore the draft bean back to the live state. + *

+ * The values from the live beans are set back to the draft bean and the + * @DraftDirty and @DraftReset properties are reset. + *

+ * + * @param the type of the entity bean + * @param beanType the type of the entity bean + * @param id the id of the entity bean to restore + */ + T draftRestore(Class beanType, Object id); + + /** + * Restore the draft beans matching the query back to the live state. + *

+ * The values from the live beans are set back to the draft bean and the + * @DraftDirty and @DraftReset properties are reset. + *

+ * + * @param the type of the entity bean + * @param query the query used to select the draft beans to restore + * @param transaction the transaction the restore process should use (can be null) + */ + List draftRestore(Query query, Transaction transaction); + + /** + * Restore the draft beans matching the query back to the live state. + *

+ * The values from the live beans are set back to the draft bean and the + * @DraftDirty and @DraftReset properties are reset. + *

+ * + * @param the type of the entity bean + * @param query the query used to select the draft beans to restore + */ + List draftRestore(Query query); + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index 4a152b885..3be4ddb05 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -1659,6 +1659,41 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return (liveBeans.size() == 1) ? liveBeans.get(0) : null; } + @Override + public List draftRestore(Query query, Transaction transaction) { + + TransWrapper wrap = initTransIfRequired(transaction); + try { + SpiTransaction trans = wrap.transaction; + List beans = persister.draftRestore(query, trans); + wrap.commitIfCreated(); + + return beans; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + @Override + public T draftRestore(Class beanType, Object id, Transaction transaction) { + + Query query = find(beanType).setId(id); + List beans = draftRestore(query, transaction); + return (beans.size() == 1) ? beans.get(0) : null; + } + + @Override + public T draftRestore(Class beanType, Object id) { + return draftRestore(beanType, id, null); + } + + @Override + public List draftRestore(Query query) { + return draftRestore(query, null); + } + private EntityBean checkEntityBean(Object bean) { if (bean == null) { throw new IllegalArgumentException(Message.msg("bean.isnull")); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java index 56994062b..a1e08d839 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java @@ -93,4 +93,9 @@ public interface Persister { */ List publish(Query query, Transaction transaction); + /** + * Restore the draft beans back to the matching live beans. + */ + List draftRestore(Query query, Transaction transaction); + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index c54ee3868..abb9b245a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1,6 +1,7 @@ package com.avaje.ebeaninternal.server.deploy; import com.avaje.ebean.OrderBy; +import com.avaje.ebean.PersistenceContextScope; import com.avaje.ebean.Query; import com.avaje.ebean.SqlUpdate; import com.avaje.ebean.Transaction; @@ -1953,6 +1954,8 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { * Optimise the draft query fetching any draftable element relationships. */ public void draftQueryOptimise(Query query) { + // use per query PersistenceContext to ensure fresh beans loaded + query.setPersistenceContextScope(PersistenceContextScope.QUERY); draftHelp.draftQueryOptimise(query); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index 7cf75ff6e..0be601320 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -127,49 +127,82 @@ public final class DefaultPersister implements Persister { return executeOrQueue(new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute)); } + /** + * Restore draft beans to match live beans given the query. + */ @Override - public List publish(Query query, Transaction transaction) { - - query.asDraft(); + public List draftRestore(Query query, Transaction transaction) { Class beanType = query.getBeanType(); BeanDescriptor desc = server.getBeanDescriptor(beanType); - desc.draftQueryOptimise(query); - List draftBeans = server.findList(query, transaction); + DraftHandler draftHandler = new DraftHandler(desc, transaction); + + List liveBeans = draftHandler.fetchSourceBeans(query, false); + PUB.debug("draftRestore [{}] count[{}]", desc.getName(), liveBeans.size()); + if (liveBeans.isEmpty()) { + return Collections.emptyList(); + } + + draftHandler.fetchDestinationBeans(liveBeans, true); + + BeanManager mgr = beanDescriptorManager.getBeanManager(beanType); + + for (T liveBean: liveBeans) { + T draftBean = draftHandler.publishToDestinationBean(liveBean); + // reset @DraftDirty and @DraftReset properties + draftHandler.resetDraft(draftBean); + + PUB.trace("draftRestore bean [{}] id[{}]", desc.getName(), draftHandler.getId()); + update(createRequest(draftBean, transaction, null, mgr, Type.UPDATE, true, false)); + } + + PUB.debug("draftRestore - complete for [{}]", desc.getName()); + return draftHandler.getDrafts(); + } + + /** + * Helper method to return the list of Id values for the list of beans. + */ + private List getBeanIds(BeanDescriptor desc, List beans) { + List idList = new ArrayList(); + for (T liveBean: beans) { + idList.add(desc.getBeanId(liveBean)); + } + return idList; + } + + /** + * Publish from draft to live given the query. + */ + @Override + public List publish(Query query, Transaction transaction) { + + Class beanType = query.getBeanType(); + BeanDescriptor desc = server.getBeanDescriptor(beanType); + + DraftHandler draftHandler = new DraftHandler(desc, transaction); + + List draftBeans = draftHandler.fetchSourceBeans(query, true); PUB.debug("publish [{}] count[{}]", desc.getName(), draftBeans.size()); - if (draftBeans.isEmpty()) { return Collections.emptyList(); } - // get the list of Id's - List idList = new ArrayList(); - for (T draftBean: draftBeans) { - idList.add(desc.getBeanId(draftBean)); - } - - // fetch existing live beans to update (or insert if missing) - Query liveBeansQuery = server.find(beanType).where().idIn(idList).query(); - desc.draftQueryOptimise(liveBeansQuery); - - Map liveBeans = liveBeansQuery.findMap(); - - List livePublish = new ArrayList(idList.size()); + draftHandler.fetchDestinationBeans(draftBeans, false); BeanManager mgr = beanDescriptorManager.getBeanManager(beanType); - DraftHandler draftHandler = new DraftHandler(desc); - + List livePublish = new ArrayList(draftBeans.size()); for (T draftBean: draftBeans) { - Object draftID = desc.getBeanId(draftBean); - T existingLiveBean = liveBeans.get(draftID); - - T liveBean = desc.publish(draftBean, existingLiveBean); + T liveBean = draftHandler.publishToDestinationBean(draftBean); livePublish.add(liveBean); + + // reset @DraftDirty and @DraftReset properties + draftHandler.resetDraft(draftBean); - Type persistType = (existingLiveBean == null) ? Type.INSERT : Type.UPDATE; - PUB.trace("publish bean [{}] id[{}] type[{}]", desc.getName(), draftID, persistType); + Type persistType = draftHandler.isInsert() ? Type.INSERT : Type.UPDATE; + PUB.trace("publish bean [{}] id[{}] type[{}]", desc.getName(), draftHandler.getId(), persistType); PersistRequestBean request = createRequest(liveBean, transaction, null, mgr, persistType, true, true); if (persistType == Type.INSERT) { @@ -177,8 +210,6 @@ public final class DefaultPersister implements Persister { } else { update(request); } - - draftHandler.resetDraft(draftBean); } draftHandler.updateDrafts(transaction, mgr); @@ -193,14 +224,38 @@ public final class DefaultPersister implements Persister { class DraftHandler { final BeanDescriptor desc; + final Transaction transaction; final BeanProperty draftDirty; final List draftUpdates = new ArrayList(); - DraftHandler(BeanDescriptor desc) { + /** + * Id value of the last published bean. + */ + Object id; + + /** + * True if the last published bean is new/insert. + */ + boolean insert; + + /** + * The destination beans to publish/restore to mapped by id. + */ + Map destBeans; + + DraftHandler(BeanDescriptor desc, Transaction transaction) { this.desc = desc; + this.transaction = transaction; this.draftDirty = desc.getDraftDirty(); } + /** + * Return the list of draft beans with changes (to be persisted). + */ + List getDrafts() { + return draftUpdates; + } + /** * Set the draft dirty state to false and reset any dirtyReset properties. */ @@ -224,6 +279,56 @@ public final class DefaultPersister implements Persister { } } + /** + * Fetch the source beans based on the query. + */ + List fetchSourceBeans(Query query, boolean asDraft) { + desc.draftQueryOptimise(query); + if (asDraft) { + query.asDraft(); + } + return server.findList(query, transaction); + } + + /** + * Fetch the destination beans that will be published to. + */ + void fetchDestinationBeans(List sourceBeans, boolean asDraft) { + + List ids = getBeanIds(desc, sourceBeans); + + Query destQuery = server.find(desc.getBeanType()).where().idIn(ids).query(); + if (asDraft) { + destQuery.asDraft(); + } + desc.draftQueryOptimise(destQuery); + this.destBeans = server.findMap(destQuery, transaction); + } + + /** + * Publish/restore the values from the sourceBean to the matching destination bean. + */ + T publishToDestinationBean(T sourceBean) { + id = desc.getBeanId(sourceBean); + T destBean = destBeans.get(id); + insert = (destBean == null); + // apply changes from liveBean to draftBean + return desc.publish(sourceBean, destBean); + } + + /** + * Return true if the last publish resulted in an new bean to insert. + */ + boolean isInsert() { + return insert; + } + + /** + * Return the Id value of the last published/restored bean. + */ + Object getId() { + return id; + } } /** diff --git a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java index 51c3a878a..79bf7ad8a 100644 --- a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java +++ b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java @@ -367,6 +367,26 @@ public class TDSpiEbeanServer implements SpiEbeanServer { return null; } + @Override + public T draftRestore(Class beanType, Object id, Transaction transaction) { + return null; + } + + @Override + public List draftRestore(Query query, Transaction transaction) { + return null; + } + + @Override + public T draftRestore(Class beanType, Object id) { + return null; + } + + @Override + public List draftRestore(Query query) { + return null; + } + @Override public Transaction createTransaction() { return null;