diff --git a/pom.xml b/pom.xml index 4ea495c50..e58258676 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.avaje.ebeanorm avaje-ebeanorm-agent - 4.11.1 + 4.12.1-SNAPSHOT test @@ -249,7 +249,7 @@ org.avaje.ebeanorm avaje-ebeanorm-mavenenhancer - 4.11.1 + 4.12.1-SNAPSHOT test diff --git a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java index 24462e614..1f9af8aa9 100644 --- a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java +++ b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java @@ -39,6 +39,8 @@ public final class EntityBeanIntercept implements Serializable { private transient BeanLoader beanLoader; + private transient PreGetterCallback preGetterCallback; + private String ebeanServerName; /** @@ -186,6 +188,21 @@ public final class EntityBeanIntercept implements Serializable { return embeddedOwnerIndex; } + /** + * Clear the getter callback. + */ + public void clearGetterCallback() { + this.preGetterCallback = null; + } + + /** + * Register the callback to be triggered when getter is called. + * This is used primarily to automatically flush the JDBC batch. + */ + public void registerGetterCallback(PreGetterCallback getterCallback) { + this.preGetterCallback = getterCallback; + } + /** * Set the embedded beans owning bean. */ @@ -832,19 +849,31 @@ public final class EntityBeanIntercept implements Serializable { public void initialisedMany(int propertyIndex) { loadedProps[propertyIndex] = true; } - + + private final void preGetterCallback() { + if (preGetterCallback != null) { + preGetterCallback.preGetterTrigger(); + } + } + + /** + * Called prior to Id property getter. + */ + public void preGetId() { + preGetterCallback(); + } + /** * Method that is called prior to a getter method on the actual entity. */ public void preGetter(int propertyIndex) { + preGetterCallback(); if (state == STATE_NEW || disableLazyLoad) { return; } - if (!isLoadedProperty(propertyIndex)) { loadBean(propertyIndex); } - if (nodeUsageCollector != null) { nodeUsageCollector.addUsed(getProperty(propertyIndex)); } diff --git a/src/main/java/com/avaje/ebean/bean/PreGetterCallback.java b/src/main/java/com/avaje/ebean/bean/PreGetterCallback.java new file mode 100644 index 000000000..8faecec22 --- /dev/null +++ b/src/main/java/com/avaje/ebean/bean/PreGetterCallback.java @@ -0,0 +1,13 @@ +package com.avaje.ebean.bean; + +/** + * A callback that can be registered to fire on getter method calls. + * It's primary purpose is to automatically flush JDBC batch buffer. + */ +public interface PreGetterCallback { + + /** + * Trigger the callback. + */ + void preGetterTrigger(); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index afd86bfa6..55c997538 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -1,6 +1,7 @@ package com.avaje.ebeaninternal.server.core; import com.avaje.ebean.ValuePair; +import com.avaje.ebean.bean.PreGetterCallback; import com.avaje.ebeaninternal.api.ConcurrencyMode; import com.avaje.ebean.annotation.DocStoreMode; import com.avaje.ebean.bean.EntityBean; @@ -36,7 +37,7 @@ import java.util.Set; /** * PersistRequest for insert update or delete of a bean. */ -public final class PersistRequestBean extends PersistRequest implements BeanPersistRequest, DocStoreUpdate { +public final class PersistRequestBean extends PersistRequest implements BeanPersistRequest, DocStoreUpdate, PreGetterCallback { private final BeanManager beanManager; @@ -137,6 +138,11 @@ public final class PersistRequestBean extends PersistRequest implements BeanP private long version; + /** + * Flag set when request is added to JDBC batch registered as a "getter callback" to automatically flush batch. + */ + private boolean getterCallback; + public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse, boolean publish) { @@ -239,8 +245,17 @@ public final class PersistRequestBean extends PersistRequest implements BeanP */ public void setBatched() { batched = true; + if (type == Type.INSERT || type == Type.UPDATE) { + // used to trigger automatic jdbc batch flush + intercept.registerGetterCallback(this); + getterCallback = true; + } } + @Override + public void preGetterTrigger() { + transaction.flushBatch(); + } public void setSkipBatchForTopLevel() { skipBatchForTopLevel = true; @@ -621,6 +636,9 @@ public final class PersistRequestBean extends PersistRequest implements BeanP @Override public int executeNow() { + if (getterCallback) { + intercept.clearGetterCallback(); + } switch (type) { case INSERT: persistExecute.executeInsertBean(this); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java index 0a3f05474..aa677d98b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java @@ -217,12 +217,10 @@ public final class IdBinderSimple implements IdBinder { if (!idValue.getClass().equals(expectedType)) { idValue = scalarType.toBeanType(idValue); } - if (bean != null) { // support PropertyChangeSupport idProperty.setValueIntercept(bean, idValue); } - return idValue; } } diff --git a/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertFlush.java b/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertFlush.java index ed5fc8892..141da0b3c 100644 --- a/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertFlush.java +++ b/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertFlush.java @@ -10,6 +10,8 @@ import org.junit.Test; import java.sql.Timestamp; +import static org.junit.Assert.assertNotNull; + public class TestBatchInsertFlush extends BaseTestCase { @Test @@ -26,12 +28,8 @@ public class TestBatchInsertFlush extends BaseTestCase { EBasicVer b2 = new EBasicVer("b2"); server.save(b2, txn); - //txn.flushBatch(); - - b1.setDescription("modify"); - System.out.println("here"); - Timestamp lastUpdate = b1.getLastUpdate(); Integer id = b1.getId(); + assertNotNull(id); EBasicVer b3 = new EBasicVer("b3"); server.save(b3, txn); @@ -41,7 +39,60 @@ public class TestBatchInsertFlush extends BaseTestCase { } finally { txn.end(); } + } + @Test + public void testFlushOnGetProperty() { + EbeanServer server = Ebean.getDefaultServer(); + Transaction txn = server.beginTransaction(); + try { + txn.setBatch(PersistBatch.ALL); + + EBasicVer b1 = new EBasicVer("b1"); + server.save(b1, txn); + + EBasicVer b2 = new EBasicVer("b2"); + server.save(b2, txn); + + // flush here + Timestamp lastUpdate = b1.getLastUpdate(); + assertNotNull(lastUpdate); + + EBasicVer b3 = new EBasicVer("b3"); + server.save(b3, txn); + + txn.commit(); + + } finally { + txn.end(); + } + } + + @Test + public void testFlushOnSetProperty() { + + EbeanServer server = Ebean.getDefaultServer(); + Transaction txn = server.beginTransaction(); + try { + txn.setBatch(PersistBatch.ALL); + + EBasicVer b1 = new EBasicVer("b1"); + server.save(b1, txn); + + EBasicVer b2 = new EBasicVer("b2"); + server.save(b2, txn); + + // flush here + b1.setDescription("modify"); + + EBasicVer b3 = new EBasicVer("b3"); + server.save(b3, txn); + + txn.commit(); + + } finally { + txn.end(); + } } } diff --git a/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java b/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java index 617cc1e95..a9c22e64c 100644 --- a/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java +++ b/src/test/java/com/avaje/tests/model/basic/xtra/TestInsertBatchThenUpdate.java @@ -10,6 +10,7 @@ import org.junit.Test; import java.util.ArrayList; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; public class TestInsertBatchThenUpdate extends BaseTestCase { @@ -24,6 +25,8 @@ public class TestInsertBatchThenUpdate extends BaseTestCase { try { txn.setBatch(PersistBatch.ALL); + LoggedSqlCollector.start(); + EdParent parent = new EdParent(); parent.setName("MyComputer"); @@ -36,21 +39,17 @@ public class TestInsertBatchThenUpdate extends BaseTestCase { Ebean.save(parent); - // nothing flushed yet - List loggedSql0 = LoggedSqlCollector.start(); - assertEquals(0, loggedSql0.size()); - parent.setName("MyDesk"); Ebean.save(parent); - // nothing flushed yet - assertEquals(0, LoggedSqlCollector.start().size()); - Ebean.commitTransaction(); // insert statements for EdExtendedParent - List loggedSql2 = LoggedSqlCollector.start(); - assertEquals(2, loggedSql2.size()); + List loggedSql = LoggedSqlCollector.stop(); + assertEquals(3, loggedSql.size()); + assertThat(loggedSql.get(0)).contains("insert into td_parent"); + assertThat(loggedSql.get(1)).contains("insert into td_child "); + assertThat(loggedSql.get(2)).contains("update td_parent set parent_name=? where parent_id=?"); } finally { Ebean.endTransaction();