mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Fix for #168 - ENH: DataSource - add support for configuring autocommit=true
This commit is contained in:
@@ -32,6 +32,8 @@ public class DataSourceConfig {
|
||||
|
||||
private int isolationLevel = Transaction.READ_COMMITTED;
|
||||
|
||||
private boolean autoCommit;
|
||||
|
||||
private String heartbeatSql;
|
||||
|
||||
private int heartbeatFreqSecs = 30;
|
||||
@@ -131,6 +133,20 @@ public class DataSourceConfig {
|
||||
public void setIsolationLevel(int isolationLevel) {
|
||||
this.isolationLevel = isolationLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return autoCommit setting.
|
||||
*/
|
||||
public boolean isAutoCommit() {
|
||||
return autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to turn on autoCommit.
|
||||
*/
|
||||
public void setAutoCommit(boolean autoCommit) {
|
||||
this.autoCommit = autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum number of connections the pool should maintain.
|
||||
@@ -450,6 +466,7 @@ public class DataSourceConfig {
|
||||
String dbUrl = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", dbUrl);
|
||||
|
||||
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
|
||||
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
|
||||
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
|
||||
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
|
||||
|
||||
@@ -175,6 +175,13 @@ public class ServerConfig {
|
||||
*/
|
||||
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
|
||||
|
||||
/**
|
||||
* Set to true if the DataSource uses autoCommit.
|
||||
* <p>
|
||||
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
private boolean autoCommitMode;
|
||||
|
||||
/**
|
||||
* The data source JNDI name if using a JNDI DataSource.
|
||||
*/
|
||||
@@ -582,6 +589,20 @@ public class ServerConfig {
|
||||
public void setDataSourceJndiName(String dataSourceJndiName) {
|
||||
this.dataSourceJndiName = dataSourceJndiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
public boolean isAutoCommitMode() {
|
||||
return autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
public void setAutoCommitMode(boolean autoCommitMode) {
|
||||
this.autoCommitMode = autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value used to represent TRUE in the database.
|
||||
@@ -1281,6 +1302,7 @@ public class ServerConfig {
|
||||
|
||||
loadDataSourceSettings(p);
|
||||
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", false);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
|
||||
namingConvention = createNamingConvention(p);
|
||||
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -22,6 +24,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
@@ -31,6 +34,7 @@ import com.avaje.ebeaninternal.server.resource.ResourceManager;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
|
||||
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
|
||||
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
|
||||
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
|
||||
@@ -42,8 +46,6 @@ import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
/**
|
||||
* Used to extend the ServerConfig with additional objects used to configure and
|
||||
* construct an EbeanServer.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class InternalConfiguration {
|
||||
|
||||
@@ -113,8 +115,7 @@ public class InternalConfiguration {
|
||||
this.beanDescriptorManager = new BeanDescriptorManager(this);
|
||||
beanDescriptorManager.deploy();
|
||||
|
||||
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
|
||||
serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
this.transactionManager = createTransactionManager();
|
||||
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
|
||||
|
||||
@@ -131,6 +132,34 @@ public class InternalConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
private TransactionManager createTransactionManager() {
|
||||
|
||||
if (isAutoCommitMode()) {
|
||||
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on.
|
||||
*/
|
||||
private boolean isAutoCommitMode() {
|
||||
if (serverConfig.isAutoCommitMode()) {
|
||||
// explicitly set
|
||||
return true;
|
||||
}
|
||||
DataSource dataSource = serverConfig.getDataSource();
|
||||
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
|
||||
// We know the DataSourcePool is using autoCommit
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public JsonContext createJsonContext(SpiEbeanServer server) {
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ public class DataSourcePool implements DataSource {
|
||||
this.name = name;
|
||||
this.poolListener = createPoolListener(params.getPoolListener());
|
||||
|
||||
this.autoCommit = false;
|
||||
this.autoCommit = params.isAutoCommit();
|
||||
this.transactionIsolation = params.getIsolationLevel();
|
||||
|
||||
this.maxInactiveMillis = 1000 * params.getMaxInactiveTimeSecs();
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.avaje.ebeaninternal.server.transaction;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* AutoCommit friendly Transaction.
|
||||
* <p>
|
||||
* Skips actual commit and rollback as these are performed automatically.
|
||||
*/
|
||||
public class AutoCommitJdbcTransaction extends JdbcTransaction {
|
||||
|
||||
public AutoCommitJdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
super(id, explicit, connection, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRollback() throws SQLException {
|
||||
// do nothing as autoCommit
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommit() throws SQLException {
|
||||
// do nothing as autoCommit
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.avaje.ebeaninternal.server.transaction;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* AutoCommit based TransactionManager.
|
||||
* <p>
|
||||
* Intended to be used if when autoCommit mode is desired.
|
||||
*/
|
||||
public class AutoCommitTransactionManager extends TransactionManager {
|
||||
|
||||
public AutoCommitTransactionManager(ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
|
||||
ServerConfig config, BeanDescriptorManager descMgr, BootupClasses bootupClasses) {
|
||||
|
||||
super(clusterManager, backgroundExecutor, config, descMgr, bootupClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an autoCommit based Transaction.
|
||||
*/
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
|
||||
return new AutoCommitJdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -540,28 +540,40 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
* rollback or just close the connection for performance.
|
||||
* </p>
|
||||
*/
|
||||
private void connectionEndForQueryOnly() {
|
||||
protected void connectionEndForQueryOnly() {
|
||||
try {
|
||||
switch (onQueryOnly) {
|
||||
case ROLLBACK:
|
||||
connection.rollback();
|
||||
performRollback();
|
||||
break;
|
||||
case COMMIT:
|
||||
connection.commit();
|
||||
performCommit();
|
||||
break;
|
||||
case CLOSE_ON_READCOMMITTED:
|
||||
// Connection is closed via deactivate() which follows
|
||||
// This optimisation is only available at READ COMMITTED Isolation
|
||||
// valid at READ COMMITTED Isolation
|
||||
break;
|
||||
default:
|
||||
connection.rollback();
|
||||
performRollback();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
String m = "Error when ending a query only transaction via " + onQueryOnly;
|
||||
logger.error(m, e);
|
||||
logger.error("Error when ending a query only transaction via " + onQueryOnly, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the actual rollback on the connection.
|
||||
*/
|
||||
protected void performRollback() throws SQLException {
|
||||
connection.rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the actual commit on the connection.
|
||||
*/
|
||||
protected void performCommit() throws SQLException {
|
||||
connection.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* End the transaction on a query only request.
|
||||
*/
|
||||
@@ -594,7 +606,7 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
if (batchControl != null && !batchControl.isEmpty()) {
|
||||
batchControl.flush();
|
||||
}
|
||||
connection.commit();
|
||||
performCommit();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -611,13 +623,12 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
* Notify the transaction manager.
|
||||
*/
|
||||
protected void notifyRollback(Throwable cause) {
|
||||
if (manager == null) {
|
||||
return;
|
||||
}
|
||||
if (queryOnly) {
|
||||
manager.notifyOfQueryOnly(false, this, cause);
|
||||
} else {
|
||||
manager.notifyOfRollback(this, cause);
|
||||
if (manager != null) {
|
||||
if (queryOnly) {
|
||||
manager.notifyOfQueryOnly(false, this, cause);
|
||||
} else {
|
||||
manager.notifyOfRollback(this, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +648,7 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
throw new IllegalStateException(illegalStateMessage);
|
||||
}
|
||||
try {
|
||||
connection.rollback();
|
||||
performRollback();
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
@@ -42,7 +42,7 @@ public class TransactionManager {
|
||||
public static final Logger TXN_LOGGER = LoggerFactory.getLogger("org.avaje.ebean.TXN");
|
||||
|
||||
/**
|
||||
* The behaviour desired when ending a query only transaction.
|
||||
* The behavior desired when ending a query only transaction.
|
||||
*/
|
||||
public enum OnQueryOnly {
|
||||
|
||||
@@ -62,47 +62,47 @@ public class TransactionManager {
|
||||
COMMIT
|
||||
}
|
||||
|
||||
private final BeanDescriptorManager beanDescriptorManager;
|
||||
protected final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
/**
|
||||
* Prefix for transaction id's (logging).
|
||||
*/
|
||||
private final String prefix;
|
||||
protected final String prefix;
|
||||
|
||||
private final String externalTransPrefix;
|
||||
protected final String externalTransPrefix;
|
||||
|
||||
/**
|
||||
* The dataSource of connections.
|
||||
*/
|
||||
private final DataSource dataSource;
|
||||
protected final DataSource dataSource;
|
||||
|
||||
/**
|
||||
* Flag to indicate the default Isolation is READ COMMITTED. This enables us
|
||||
* to close queryOnly transactions rather than commit or rollback them.
|
||||
*/
|
||||
private final OnQueryOnly onQueryOnly;
|
||||
protected final OnQueryOnly onQueryOnly;
|
||||
|
||||
/**
|
||||
* The default batchMode for transactions.
|
||||
*/
|
||||
private final boolean defaultBatchMode;
|
||||
protected final boolean defaultBatchMode;
|
||||
|
||||
private final BackgroundExecutor backgroundExecutor;
|
||||
protected final BackgroundExecutor backgroundExecutor;
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
protected final ClusterManager clusterManager;
|
||||
|
||||
private final String serverName;
|
||||
protected final String serverName;
|
||||
|
||||
/**
|
||||
* Id's for transaction logging.
|
||||
*/
|
||||
private AtomicLong transactionCounter = new AtomicLong(1000);
|
||||
protected AtomicLong transactionCounter = new AtomicLong(1000);
|
||||
|
||||
private int clusterDebugLevel;
|
||||
protected int clusterDebugLevel;
|
||||
|
||||
private final BulkEventListenerMap bulkEventListenerMap;
|
||||
protected final BulkEventListenerMap bulkEventListenerMap;
|
||||
|
||||
private TransactionEventListener[] transactionEventListeners;
|
||||
protected TransactionEventListener[] transactionEventListeners;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
@@ -146,14 +146,14 @@ public class TransactionManager {
|
||||
((DataSourcePool)dataSource).shutdown(deregisterDriver);
|
||||
}
|
||||
}
|
||||
|
||||
public BeanDescriptorManager getBeanDescriptorManager() {
|
||||
return beanDescriptorManager;
|
||||
}
|
||||
|
||||
public BulkEventListenerMap getBulkEventListenerMap() {
|
||||
return bulkEventListenerMap;
|
||||
}
|
||||
public BeanDescriptorManager getBeanDescriptorManager() {
|
||||
return beanDescriptorManager;
|
||||
}
|
||||
|
||||
public BulkEventListenerMap getBulkEventListenerMap() {
|
||||
return bulkEventListenerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the behaviour to use when a query only transaction is committed.
|
||||
@@ -219,26 +219,26 @@ public class TransactionManager {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cluster debug level.
|
||||
*/
|
||||
public int getClusterDebugLevel() {
|
||||
return clusterDebugLevel;
|
||||
}
|
||||
/**
|
||||
* Return the cluster debug level.
|
||||
*/
|
||||
public int getClusterDebugLevel() {
|
||||
return clusterDebugLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cluster debug level.
|
||||
*/
|
||||
public void setClusterDebugLevel(int clusterDebugLevel) {
|
||||
this.clusterDebugLevel = clusterDebugLevel;
|
||||
}
|
||||
/**
|
||||
* Set the cluster debug level.
|
||||
*/
|
||||
public void setClusterDebugLevel(int clusterDebugLevel) {
|
||||
this.clusterDebugLevel = clusterDebugLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the type of behaviour to use when closing a transaction that was used to query data only.
|
||||
*/
|
||||
public OnQueryOnly getOnQueryOnly() {
|
||||
return onQueryOnly;
|
||||
}
|
||||
/**
|
||||
* Defines the type of behavior to use when closing a transaction that was used to query data only.
|
||||
*/
|
||||
public OnQueryOnly getOnQueryOnly() {
|
||||
return onQueryOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the externally supplied Connection.
|
||||
@@ -273,7 +273,7 @@ public class TransactionManager {
|
||||
c = dataSource.getConnection();
|
||||
long id = transactionCounter.incrementAndGet();
|
||||
|
||||
JdbcTransaction t = new JdbcTransaction(prefix + id, explicit, c, this);
|
||||
SpiTransaction t = createTransaction(explicit, c, id);
|
||||
|
||||
// set the default batch mode. This can be on for
|
||||
// jdbc drivers that support getGeneratedKeys
|
||||
@@ -309,7 +309,7 @@ public class TransactionManager {
|
||||
c = dataSource.getConnection();
|
||||
long id = transactionCounter.incrementAndGet();
|
||||
|
||||
JdbcTransaction t = new JdbcTransaction(prefix + id, false, c, this);
|
||||
SpiTransaction t = createTransaction(false, c, id);
|
||||
|
||||
// set the default batch mode. Can be true for
|
||||
// jdbc drivers that support getGeneratedKeys
|
||||
@@ -336,6 +336,13 @@ public class TransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new transaction.
|
||||
*/
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
return new JdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a local rolled back transaction.
|
||||
*/
|
||||
@@ -446,34 +453,33 @@ public class TransactionManager {
|
||||
|
||||
backgroundExecutor.execute(postCommit.notifyPersistListeners());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify local BeanPersistListeners etc of events from another server in the cluster.
|
||||
*/
|
||||
public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) {
|
||||
|
||||
if (clusterDebugLevel > 0 || logger.isDebugEnabled()){
|
||||
logger.info("Cluster Received: "+remoteEvent.toString());
|
||||
}
|
||||
|
||||
List<TableIUD> tableIUDList = remoteEvent.getTableIUDList();
|
||||
if (tableIUDList != null){
|
||||
for (int i = 0; i < tableIUDList.size(); i++) {
|
||||
TableIUD tableIUD = tableIUDList.get(i);
|
||||
beanDescriptorManager.cacheNotify(tableIUD);
|
||||
}
|
||||
}
|
||||
|
||||
List<BeanPersistIds> beanPersistList = remoteEvent.getBeanPersistList();
|
||||
if (beanPersistList != null){
|
||||
for (int i = 0; i < beanPersistList.size(); i++) {
|
||||
BeanPersistIds beanPersist = beanPersistList.get(i);
|
||||
beanPersist.notifyCacheAndListener();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify local BeanPersistListeners etc of events from another server in the cluster.
|
||||
*/
|
||||
public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) {
|
||||
|
||||
if (clusterDebugLevel > 0 || logger.isDebugEnabled()) {
|
||||
logger.info("Cluster Received: " + remoteEvent.toString());
|
||||
}
|
||||
|
||||
List<TableIUD> tableIUDList = remoteEvent.getTableIUDList();
|
||||
if (tableIUDList != null) {
|
||||
for (int i = 0; i < tableIUDList.size(); i++) {
|
||||
TableIUD tableIUD = tableIUDList.get(i);
|
||||
beanDescriptorManager.cacheNotify(tableIUD);
|
||||
}
|
||||
}
|
||||
|
||||
List<BeanPersistIds> beanPersistList = remoteEvent.getBeanPersistList();
|
||||
if (beanPersistList != null) {
|
||||
for (int i = 0; i < beanPersistList.size(); i++) {
|
||||
BeanPersistIds beanPersist = beanPersistList.get(i);
|
||||
beanPersist.notifyCacheAndListener();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user