diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java
index 5e75831b5..74faeccb0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java
@@ -318,4 +318,20 @@ public interface SpiTransaction extends Transaction {
* Return true if explicitly set to skip cache (ignores skipOnWrite).
*/
boolean isSkipCacheExplicit();
+
+ /**
+ * Fire pre commit processing/listeners.
+ */
+ void preCommit();
+
+ /**
+ * Fire post commit events and listeners.
+ */
+ void postCommit();
+
+ /**
+ * Fire post rollback events and listeners.
+ */
+ void postRollback(Throwable cause);
+
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
index 897a32f44..fe5351ddc 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
@@ -418,4 +418,18 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
transaction.flushBatchOnCollection();
}
+ @Override
+ public void preCommit() {
+ transaction.preCommit();
+ }
+
+ @Override
+ public void postCommit() {
+ transaction.postCommit();
+ }
+
+ @Override
+ public void postRollback(Throwable cause) {
+ transaction.postRollback(cause);
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java
index 53d266964..79bf85335 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java
@@ -586,6 +586,21 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
}
}
+ @Override
+ public void preCommit() {
+ // do nothing
+ }
+
+ @Override
+ public void postCommit() {
+ // do nothing
+ }
+
+ @Override
+ public void postRollback(Throwable cause) {
+ // do nothing
+ }
+
/**
* Return true if the transaction is active.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
index 5f810dc17..8c1897ff5 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
@@ -373,6 +373,9 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
private void firePreCommit() {
withEachCallback(TransactionCallback::preCommit);
+ if (changeLogHolder != null) {
+ changeLogHolder.preCommit();
+ }
}
private void firePostCommit() {
@@ -981,14 +984,23 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
* Batch flush, jdbc commit, trigger registered TransactionCallbacks, notify l2 cache etc.
*/
private void flushCommitAndNotify() throws SQLException {
- internalBatchFlush();
- firePreCommit();
- // only performCommit can throw an exception
+ preCommit();
performCommit();
+ postCommit();
+ }
+
+ @Override
+ public void postCommit() {
firePostCommit();
notifyCommit();
}
+ @Override
+ public void preCommit() {
+ internalBatchFlush();
+ firePreCommit();
+ }
+
/**
* Perform a commit, fire callbacks and notify l2 cache etc.
*
@@ -1132,11 +1144,16 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
} finally {
// these will not throw an exception
- firePostRollback();
- notifyRollback(cause);
+ postRollback(cause);
}
}
+ @Override
+ public void postRollback(Throwable cause) {
+ firePostRollback();
+ notifyRollback(cause);
+ }
+
/**
* If the transaction is active then perform rollback.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransaction.java
index 811d9d1d6..e0c33a193 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransaction.java
@@ -13,9 +13,9 @@ public class JtaTransaction extends JdbcTransaction {
private final UserTransaction userTransaction;
- private boolean commmitted;
+ private final boolean newTransaction;
- private boolean newTransaction;
+ private boolean committed;
/**
* Create the JtaTransaction.
@@ -41,7 +41,6 @@ public class JtaTransaction extends JdbcTransaction {
if (connection.getAutoCommit()) {
connection.setAutoCommit(false);
}
-
} catch (SQLException e) {
throw new PersistenceException(e);
}
@@ -52,7 +51,7 @@ public class JtaTransaction extends JdbcTransaction {
*/
@Override
public void commit() {
- if (commmitted) {
+ if (committed) {
throw new PersistenceException("This transaction has already been committed.");
}
try {
@@ -60,14 +59,14 @@ public class JtaTransaction extends JdbcTransaction {
if (newTransaction) {
userTransaction.commit();
}
- notifyCommit();
+ postCommit();
} finally {
close();
}
} catch (Exception e) {
throw new PersistenceException(e);
}
- commmitted = true;
+ committed = true;
}
@Override
@@ -80,7 +79,7 @@ public class JtaTransaction extends JdbcTransaction {
*/
@Override
public void rollback(Throwable e) {
- if (!commmitted) {
+ if (!committed) {
try {
try {
if (userTransaction != null) {
@@ -90,7 +89,7 @@ public class JtaTransaction extends JdbcTransaction {
userTransaction.setRollbackOnly();
}
}
- notifyRollback(e);
+ postRollback(e);
} finally {
closeConnection();
}
@@ -98,7 +97,6 @@ public class JtaTransaction extends JdbcTransaction {
throw new PersistenceException(ex);
}
}
-
}
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransactionManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransactionManager.java
index 3fbed7d84..cc9e8cdc8 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransactionManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JtaTransactionManager.java
@@ -190,19 +190,17 @@ public class JtaTransactionManager implements ExternalTransactionManager {
@Override
public void beforeCompletion() {
- // Future note: for JPA2 locking we will
- // have beforeCommit events to fire
+ transaction.preCommit();
}
@Override
public void afterCompletion(int status) {
-
switch (status) {
case Status.STATUS_COMMITTED:
if (logger.isDebugEnabled()) {
logger.debug("Jta Txn [" + transaction.getId() + "] committed");
}
- transactionManager.notifyOfCommit(transaction);
+ transaction.postCommit();
// Remove this transaction object as it is completed
transactionManager.scope().clearExternal();
break;
@@ -211,7 +209,7 @@ public class JtaTransactionManager implements ExternalTransactionManager {
if (logger.isDebugEnabled()) {
logger.debug("Jta Txn [" + transaction.getId() + "] rollback");
}
- transactionManager.notifyOfRollback(transaction, null);
+ transaction.postRollback(null);
// Remove this transaction object as it is completed
transactionManager.scope().clearExternal();
break;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java
index 48476c14a..2ef3f2058 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java
@@ -88,6 +88,21 @@ class NoTransaction implements SpiTransaction {
// do nothing
}
+ @Override
+ public void preCommit() {
+ // do nothing
+ }
+
+ @Override
+ public void postCommit() {
+ // do nothing
+ }
+
+ @Override
+ public void postRollback(Throwable cause) {
+ // do nothing
+ }
+
@Override
public String getLogPrefix() {
return null;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TChangeLogHolder.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TChangeLogHolder.java
index b97a004ea..961247bd9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TChangeLogHolder.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TChangeLogHolder.java
@@ -56,20 +56,31 @@ public class TChangeLogHolder {
* Add a bean change to the change set.
*/
public void addBeanChange(BeanChange change) {
-
changes.addBeanChange(change);
if (++count >= batchSize) {
// we hit the batch size so send what we have knowing
// that the transaction has not completed yet and
// reset the changes and count
- owner.sendChangeLog(changes);
- changes = new ChangeSet(transactionId, ++batchId);
- count = 0;
+ sendChanges();
}
}
+ private void sendChanges() {
+ owner.sendChangeLog(changes);
+ changes = new ChangeSet(transactionId, ++batchId);
+ count = 0;
+ }
+
/**
- * On post commit send the changes we have collected.
+ * Send the changes held prior to transaction commit.
+ */
+ public void preCommit() {
+ sendChanges();
+ }
+
+ /**
+ * On post commit send the changes we have collected. This should be
+ * only the COMMITTED state and with all changes sent prior to commit.
*/
public void postCommit() {
changes.setTxnState(TxnState.COMMITTED);
diff --git a/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java b/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java
index 7c1d3040c..2adbbfc4b 100644
--- a/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java
+++ b/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java
@@ -15,11 +15,15 @@ import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.changelog.ChangeSet;
import io.ebean.event.changelog.ChangeType;
+import io.ebean.event.changelog.TxnState;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.tests.model.basic.EBasicChangeLog;
+import java.util.ArrayList;
+import java.util.List;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
@@ -43,6 +47,13 @@ public class TestChangeLog extends BaseTestCase {
server.shutdown();
}
+ private BeanChange firstChange() {
+ ChangeSet changeSet0 = changeLogListener.changes.get(0);
+ BeanChange change = changeSet0.getChanges().get(0);
+ changeLogListener.changes.clear();
+ return change;
+ }
+
@Test
public void test() {
@@ -50,8 +61,13 @@ public class TestChangeLog extends BaseTestCase {
bean.setName("logBean");
bean.setShortDescription("hello");
server.save(bean);
- BeanChange change = changeLogListener.changes.getChanges().get(0);
+ final List allChanges = changeLogListener.changes;
+ assertThat(allChanges).hasSize(2);
+ assertThat(allChanges.get(0).getTxnState()).isEqualTo(TxnState.IN_PROGRESS);
+ assertThat(allChanges.get(1).getTxnState()).isEqualTo(TxnState.COMMITTED);
+
+ BeanChange change = firstChange();
assertThat(change.getEvent()).isEqualTo(ChangeType.INSERT);
assertThat(change.getData())
.contains("\"name\":\"logBean\"")
@@ -61,8 +77,7 @@ public class TestChangeLog extends BaseTestCase {
bean.setName("ChangedName");
server.save(bean);
- change = changeLogListener.changes.getChanges().get(0);
-
+ change = firstChange();
assertThat(change.getEvent()).isEqualTo(ChangeType.UPDATE);
assertThat(change.getOldData()).contains("\"name\":\"logBean\"");
assertThat(change.getData()).contains("\"name\":\"ChangedName\"");
@@ -70,14 +85,11 @@ public class TestChangeLog extends BaseTestCase {
server.delete(bean);
- change = changeLogListener.changes.getChanges().get(0);
-
+ change = firstChange();
assertThat(change.getEvent()).isEqualTo(ChangeType.DELETE);
assertThat(change.getData()).isNull();
-
}
-
@Test
public void testWithNull() {
@@ -85,7 +97,7 @@ public class TestChangeLog extends BaseTestCase {
bean.setName(null);
bean.setShortDescription("hello");
server.save(bean);
- BeanChange change = changeLogListener.changes.getChanges().get(0);
+ BeanChange change = firstChange();
assertThat(change.getEvent()).isEqualTo(ChangeType.INSERT);
assertThat(change.getData())
@@ -99,7 +111,7 @@ public class TestChangeLog extends BaseTestCase {
bean.setShortDescription("hello");
server.save(bean);
- change = changeLogListener.changes.getChanges().get(0);
+ change = firstChange();
assertThat(change.getEvent()).isEqualTo(ChangeType.UPDATE);
assertThat(change.getOldData())
@@ -113,11 +125,10 @@ public class TestChangeLog extends BaseTestCase {
server.delete(bean);
- change = changeLogListener.changes.getChanges().get(0);
+ change = firstChange();
assertThat(change.getEvent()).isEqualTo(ChangeType.DELETE);
assertThat(change.getData()).isNull();
-
}
private Database createServer() {
@@ -153,16 +164,15 @@ public class TestChangeLog extends BaseTestCase {
ObjectMapper objectMapper = new ObjectMapper();
- ChangeSet changes;
+ List changes = new ArrayList<>();
public TDChangeLogListener() {
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
-
@Override
public void log(ChangeSet changes) {
- this.changes = changes;
+ this.changes.add(changes);
try {
String json = objectMapper.writeValueAsString(changes);
ChangeSet changes1 = objectMapper.readValue(json, ChangeSet.class);
diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml
new file mode 100644
index 000000000..aa9174ea3
--- /dev/null
+++ b/ebean-redis/pom.xml
@@ -0,0 +1,94 @@
+
+
+ 4.0.0
+
+ ebean-parent
+ io.ebean
+ 12.6.3-SNAPSHOT
+
+
+ ebean-redis
+ ebean redis
+ Ebean Redis L2 Cache
+
+
+
+
+ redis.clients
+ jedis
+ 3.4.0
+
+
+
+ io.ebean
+ ebean-api
+ 12.6.3-SNAPSHOT
+ provided
+
+
+
+ io.ebean
+ ebean-core
+ 12.6.3-SNAPSHOT
+ provided
+
+
+
+ io.ebean
+ ebean-querybean
+ 12.6.3-SNAPSHOT
+ test
+
+
+
+ io.ebean
+ querybean-generator
+ 12.6.3-SNAPSHOT
+ test
+
+
+
+ io.ebean
+ ebean-test
+ 12.6.3-SNAPSHOT
+ test
+
+
+
+ org.avaje.composite
+ junit
+ 1.1
+ test
+
+
+
+ org.avaje.composite
+ logback
+ 1.1
+ test
+
+
+
+
+
+
+
+
+ io.repaint.maven
+ tiles-maven-plugin
+ 2.18
+ true
+
+
+ io.ebean.tile:enhancement:12.6.1
+
+
+
+
+
+
+
+
+
diff --git a/ebean-redis/src/main/java/io/ebean/redis/DuelCache.java b/ebean-redis/src/main/java/io/ebean/redis/DuelCache.java
new file mode 100644
index 000000000..80025e7cc
--- /dev/null
+++ b/ebean-redis/src/main/java/io/ebean/redis/DuelCache.java
@@ -0,0 +1,161 @@
+package io.ebean.redis;
+
+import io.ebean.cache.ServerCache;
+import io.ebean.cache.ServerCacheStatistics;
+import io.ebean.meta.MetricVisitor;
+import io.ebeaninternal.server.cache.DefaultServerCache;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class DuelCache implements ServerCache, NearCacheInvalidate {
+
+ private final DefaultServerCache near;
+ private final RedisCache remote;
+ private final NearCacheNotify cacheNotify;
+ private final String cacheKey;
+
+ public DuelCache(DefaultServerCache near, RedisCache remote, String cacheKey, NearCacheNotify cacheNotify) {
+ this.near = near;
+ this.remote = remote;
+ this.cacheKey = cacheKey;
+ this.cacheNotify = cacheNotify;
+ }
+
+ @Override
+ public void visit(MetricVisitor visitor) {
+ near.visit(visitor);
+ remote.visit(visitor);
+ }
+
+ @Override
+ public void invalidateKeys(Set