diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index eeb0a9afe..40b7db04e 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -234,7 +234,12 @@ public class ServerConfig { * Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager. */ private boolean autoCommitMode; - + + /** + * Set to true if transaction begin should be started with explicit statement. + */ + private boolean explicitTransactionBeginMode; + /** * The data source JNDI name if using a JNDI DataSource. */ @@ -1220,6 +1225,28 @@ public class ServerConfig { this.autoCommitMode = autoCommitMode; } + /** + * Return true if transaction begin should be started with explicit statement. + */ + public boolean isExplicitTransactionBeginMode() { + return explicitTransactionBeginMode; + } + + /** + * Set to true if transaction begin should be started with explicit statement. + *
+ * This works for H2 and Postgres but not for Oracle - only use this if you first name + * is Daryl or you have explicitly talked to Rob about this feature. + *
+ *+ * This is generally not expected to be turned on but instead allow transactions to start + * implicitly which is generally the standard approach. + *
+ */ + public void setExplicitTransactionBeginMode(boolean explicitTransactionBeginMode) { + this.explicitTransactionBeginMode = explicitTransactionBeginMode; + } + /** * Return a value used to represent TRUE in the database. *
@@ -2067,6 +2094,7 @@ public class ServerConfig {
}
loadDataSourceSettings(p);
+ explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java
index 23e010f8c..8fad1abf6 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java
@@ -39,6 +39,7 @@ import com.avaje.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
+import com.avaje.ebeaninternal.server.transaction.ExplicitTransactionManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
@@ -220,6 +221,10 @@ public class InternalConfiguration {
*/
private TransactionManager createTransactionManager() {
+ if (serverConfig.isExplicitTransactionBeginMode()) {
+ return new ExplicitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
+ }
+
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitJdbcTransaction.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitJdbcTransaction.java
new file mode 100644
index 000000000..b8fc830da
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitJdbcTransaction.java
@@ -0,0 +1,45 @@
+package com.avaje.ebeaninternal.server.transaction;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * This only works for Postgres and H2 (and doesn't work for Oracle).
+ *
+ * Uses explicit begin statement to start the transactions.
+ */
+public class ExplicitJdbcTransaction extends JdbcTransaction {
+
+ public ExplicitJdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
+ super(id, explicit, connection, manager);
+ }
+
+ @Override
+ protected void checkAutoCommit(Connection connection) throws SQLException {
+ // begin the transaction explicitly
+ executeStatement("begin");
+ }
+
+ @Override
+ protected void performRollback() throws SQLException {
+ // Postgres needs this explicit rollback statement when used with AutoCommit=true
+ executeStatement("rollback");
+ }
+
+ @Override
+ protected void performCommit() throws SQLException {
+ // Postgres needs this explicit commit statement when used with AutoCommit=true
+ executeStatement("commit");
+ }
+
+ private void executeStatement(String statement) throws SQLException {
+ PreparedStatement stmt = connection.prepareStatement(statement);
+ try {
+ stmt.execute();
+ } finally {
+ stmt.close();
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java
new file mode 100644
index 000000000..52b416617
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java
@@ -0,0 +1,32 @@
+package com.avaje.ebeaninternal.server.transaction;
+
+import com.avaje.ebean.BackgroundExecutor;
+import com.avaje.ebean.config.ServerConfig;
+import com.avaje.ebeaninternal.api.SpiTransaction;
+import com.avaje.ebeaninternal.server.cluster.ClusterManager;
+import com.avaje.ebeaninternal.server.core.BootupClasses;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
+
+import java.sql.Connection;
+
+/**
+ * TransactionManager where the transactions start with explicit "begin" statement.
+ */
+public class ExplicitTransactionManager extends TransactionManager {
+
+ public ExplicitTransactionManager(ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
+ ServerConfig config, BeanDescriptorManager descMgr, BootupClasses bootupClasses) {
+
+ super(clusterManager, backgroundExecutor, config, descMgr, bootupClasses);
+ }
+
+ /**
+ * Create a ExplicitJdbcTransaction.
+ */
+ @Override
+ protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
+
+ return new ExplicitJdbcTransaction(prefix + id, explicit, c, this);
+ }
+
+}
diff --git a/src/test/java/com/avaje/tests/transaction/TestExplicitTransactionMode.java b/src/test/java/com/avaje/tests/transaction/TestExplicitTransactionMode.java
new file mode 100644
index 000000000..04d63a0e5
--- /dev/null
+++ b/src/test/java/com/avaje/tests/transaction/TestExplicitTransactionMode.java
@@ -0,0 +1,98 @@
+package com.avaje.tests.transaction;
+
+import com.avaje.ebean.BaseTestCase;
+import com.avaje.ebean.EbeanServer;
+import com.avaje.ebean.EbeanServerFactory;
+import com.avaje.ebean.Query;
+import com.avaje.ebean.Transaction;
+import com.avaje.ebean.config.DataSourceConfig;
+import com.avaje.ebean.config.ServerConfig;
+import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
+import com.avaje.tests.model.basic.UTDetail;
+import com.avaje.tests.model.basic.UTMaster;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TestExplicitTransactionMode extends BaseTestCase {
+
+ @Test
+ public void test() throws SQLException {
+
+ DataSourceConfig dsConfig = new DataSourceConfig();
+ dsConfig.loadSettings("h2autocommit");//"h2autocommit","pg"
+ dsConfig.setAutoCommit(true);
+
+ DataSourcePool pool = new DataSourcePool(null, "h2autocommit", dsConfig);
+
+ Connection connection = pool.getConnection();
+ assertTrue(connection.getAutoCommit());
+ connection.close();
+
+ ServerConfig config = new ServerConfig();
+ config.setName("h2autocommit");
+ config.loadFromProperties();
+ config.setDataSource(pool);
+ config.setDefaultServer(false);
+ config.setRegister(false);
+ config.setExplicitTransactionBeginMode(true);
+
+ config.addClass(UTMaster.class);
+ config.addClass(UTDetail.class);
+ config.setDdlGenerate(true);
+ config.setDdlRun(true);
+
+ EbeanServer ebeanServer = EbeanServerFactory.create(config);
+
+ Query