Index: docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml
===================================================================
--- docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml (revision 2491)
+++ docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml (working copy)
@@ -129,6 +129,16 @@
+ isolationLevel
+ Optional property that, if used, defines the transaction isolation level to use. Valid values for this method must be the int values
+ of the java.sql.Connection#TRANSACTION_* constants. In other words, "0", "1", "2", "4", etc. are valid values for this property. "TRANSACTION_NONE",
+ "TRANSACTION_READ_COMMITTED", "TRANSACTION_SERIALIZABLE", etc. are not valid values for this property. Note that
+ not all JDBC drivers support all isolation levels. Check the documentation for your JDBC driver to be sure.
+ When this property is not used, this source will reuse whatever
+ isolation level is currently set on the database connection.
+
+
+
largeValueSizeInBytes
An advanced boolean property that controls the size of property values at which they are considered to be "large values".
Index: extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/JpaConnectorI18n.java
===================================================================
--- extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/JpaConnectorI18n.java (revision 2491)
+++ extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/JpaConnectorI18n.java (working copy)
@@ -47,6 +47,7 @@ public final class JpaConnectorI18n {
public static I18n locationShouldHavePathAndOrProperty;
public static I18n invalidUuidForWorkspace;
public static I18n invalidReferences;
+ public static I18n invalidIsolationLevel;
public static I18n unableToDeleteBecauseOfReferences;
public static I18n workspaceAlreadyExists;
@@ -95,6 +96,9 @@ public final class JpaConnectorI18n {
public static I18n urlPropertyDescription;
public static I18n urlPropertyLabel;
public static I18n urlPropertyCategory;
+ public static I18n isolationLevelPropertyDescription;
+ public static I18n isolationLevelPropertyLabel;
+ public static I18n isolationLevelPropertyCategory;
public static I18n driverClassNamePropertyDescription;
public static I18n driverClassNamePropertyLabel;
public static I18n driverClassNamePropertyCategory;
Index: extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/JpaSource.java
===================================================================
--- extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/JpaSource.java (revision 2491)
+++ extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/JpaSource.java (working copy)
@@ -23,6 +23,7 @@
*/
package org.modeshape.connector.store.jpa;
+import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -115,6 +116,7 @@ public class JpaSource implements RepositorySource, ObjectFactory {
protected static final String URL = "url";
protected static final String DRIVER_CLASS_NAME = "driverClassName";
protected static final String DRIVER_CLASSLOADER_NAME = "driverClassloaderName";
+ protected static final String ISOLATION_LEVEL = "isolationLevel";
protected static final String MAXIMUM_CONNECTIONS_IN_POOL = "maximumConnectionsInPool";
protected static final String MINIMUM_CONNECTIONS_IN_POOL = "minimumConnectionsInPool";
protected static final String MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS = "maximumConnectionIdleTimeInSeconds";
@@ -168,6 +170,8 @@ public class JpaSource implements RepositorySource, ObjectFactory {
*/
public static final String DEFAULT_NAME_OF_DEFAULT_WORKSPACE = "default";
+ private static final int DEFAULT_ISOLATION_LEVEL = Connection.TRANSACTION_REPEATABLE_READ;
+
private static final int DEFAULT_RETRY_LIMIT = 0;
private static final int DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS = 60 * 5; // 5 minutes
private static final int DEFAULT_MAXIMUM_FETCH_DEPTH = 3;
@@ -308,6 +312,11 @@ public class JpaSource implements RepositorySource, ObjectFactory {
@Category( i18n = JpaConnectorI18n.class, value = "defaultWorkspaceNamePropertyCategory" )
private volatile String defaultWorkspace = DEFAULT_NAME_OF_DEFAULT_WORKSPACE;
+ @Description( i18n = JpaConnectorI18n.class, value = "isolationLevelPropertyDescription" )
+ @Label( i18n = JpaConnectorI18n.class, value = "isolationLevelPropertyLabel" )
+ @Category( i18n = JpaConnectorI18n.class, value = "isolationLevelPropertyCategory" )
+ private volatile int isolationLevel = DEFAULT_ISOLATION_LEVEL;
+
@Description( i18n = JpaConnectorI18n.class, value = "predefinedWorkspacesPropertyDescription" )
@Label( i18n = JpaConnectorI18n.class, value = "predefinedWorkspacesPropertyLabel" )
@Category( i18n = JpaConnectorI18n.class, value = "predefinedWorkspacesPropertyCategory" )
@@ -903,6 +912,28 @@ public class JpaSource implements RepositorySource, ObjectFactory {
}
/**
+ * @return isolationLevel
+ */
+ public int getIsolationLevel() {
+ return isolationLevel;
+ }
+
+ /**
+ * @param isolationLevel Sets isolationLevel to the specified value.
+ */
+ public synchronized void setIsolationLevel( Integer isolationLevel ) {
+ if (isolationLevel == null) isolationLevel = DEFAULT_ISOLATION_LEVEL;
+
+ if (isolationLevel != Connection.TRANSACTION_NONE && isolationLevel != Connection.TRANSACTION_READ_COMMITTED
+ && isolationLevel != Connection.TRANSACTION_READ_UNCOMMITTED
+ && isolationLevel != Connection.TRANSACTION_REPEATABLE_READ && isolationLevel != Connection.TRANSACTION_SERIALIZABLE) {
+ throw new RepositorySourceException(this.name, JpaConnectorI18n.invalidIsolationLevel.text(isolationLevel));
+ }
+
+ this.isolationLevel = isolationLevel;
+ }
+
+ /**
* {@inheritDoc}
*
* @see org.modeshape.graph.connector.RepositorySource#initialize(org.modeshape.graph.connector.RepositoryContext)
@@ -930,6 +961,7 @@ public class JpaSource implements RepositorySource, ObjectFactory {
ref.add(new StringRefAddr(URL, getUrl()));
ref.add(new StringRefAddr(DRIVER_CLASS_NAME, getDriverClassName()));
ref.add(new StringRefAddr(DRIVER_CLASSLOADER_NAME, getDriverClassloaderName()));
+ ref.add(new StringRefAddr(ISOLATION_LEVEL, Integer.toString(getIsolationLevel())));
ref.add(new StringRefAddr(MAXIMUM_CONNECTIONS_IN_POOL, Integer.toString(getMaximumConnectionsInPool())));
ref.add(new StringRefAddr(MINIMUM_CONNECTIONS_IN_POOL, Integer.toString(getMinimumConnectionsInPool())));
ref.add(new StringRefAddr(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS,
@@ -994,6 +1026,7 @@ public class JpaSource implements RepositorySource, ObjectFactory {
String url = values.get(URL);
String driverClassName = values.get(DRIVER_CLASS_NAME);
String driverClassloaderName = values.get(DRIVER_CLASSLOADER_NAME);
+ String isolationLevel = values.get(ISOLATION_LEVEL);
String maxConnectionsInPool = values.get(MAXIMUM_CONNECTIONS_IN_POOL);
String minConnectionsInPool = values.get(MINIMUM_CONNECTIONS_IN_POOL);
String maxConnectionIdleTimeInSec = values.get(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS);
@@ -1028,6 +1061,7 @@ public class JpaSource implements RepositorySource, ObjectFactory {
if (url != null) source.setUrl(url);
if (driverClassName != null) source.setDriverClassName(driverClassName);
if (driverClassloaderName != null) source.setDriverClassloaderName(driverClassloaderName);
+ if (isolationLevel != null) source.setIsolationLevel(Integer.parseInt(isolationLevel));
if (maxConnectionsInPool != null) source.setMaximumConnectionsInPool(Integer.parseInt(maxConnectionsInPool));
if (minConnectionsInPool != null) source.setMinimumConnectionsInPool(Integer.parseInt(minConnectionsInPool));
if (maxConnectionIdleTimeInSec != null) source.setMaximumConnectionIdleTimeInSeconds(Integer.parseInt(maxConnectionIdleTimeInSec));
@@ -1069,6 +1103,7 @@ public class JpaSource implements RepositorySource, ObjectFactory {
// Set the Hibernate properties used in all situations ...
setProperty(configurator, "hibernate.dialect", this.dialect);
+ setProperty(configurator, "hibernate.connection.isolation", this.isolationLevel);
// Configure additional properties, which may be overridden by subclasses ...
configure(configurator);
Index: extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaConnection.java
===================================================================
--- extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaConnection.java (revision 2491)
+++ extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaConnection.java (working copy)
@@ -163,4 +163,20 @@ public class SimpleJpaConnection implements RepositoryConnection {
logger.trace(this.getClass().getSimpleName() + ".execute(...) took " + sw.getTotalDuration());
}
}
+
+ /*
+
+ This method is needed only to support the SimpleJpaSourceTest#shouldAllowChangingIsolationLevel() test.
+
+ EntityManager entityManager() {
+ EntityManager entityManager = this.entityManager;
+
+ if (entityManager == null) {
+ acquireRepository();
+ entityManager = this.entityManager;
+ }
+
+ return entityManager;
+ }
+ */
}
Index: extensions/modeshape-connector-store-jpa/src/main/resources/org/modeshape/connector/store/jpa/JpaConnectorI18n.properties
===================================================================
--- extensions/modeshape-connector-store-jpa/src/main/resources/org/modeshape/connector/store/jpa/JpaConnectorI18n.properties (revision 2491)
+++ extensions/modeshape-connector-store-jpa/src/main/resources/org/modeshape/connector/store/jpa/JpaConnectorI18n.properties (working copy)
@@ -36,6 +36,9 @@ locationShouldHavePathAndOrProperty = The source {0} is unable to find a node wi
invalidUuidForWorkspace = There is no node with UUID "{0}" in workspace "{1}"
invalidReferences = One or more references were invalid in {0}
unableToDeleteBecauseOfReferences = At least one deleted node is referenced by a node that is not being deleted
+invalidIsolationLevel = An invalid isolation level "{0}" was specified and will be ignored. Valid isolation levels are TRANSACTION_NONE, TRANSACTION_READ_COMMITTED, TRANSACTION_READ_UNCOMMITTED, TRANSACTION_REPEATABLE_READ, and TRANSACTION_SERIALIZABLE from the java.sql.Connection class.
+
+
workspaceAlreadyExists = The source {0} already has a workspace named "{1}"
workspaceDoesNotExist = The source {0} has no workspace named "{1}"
@@ -95,6 +98,10 @@ driverClassloaderNamePropertyDescription = The name of the class loader or class
driverClassloaderNamePropertyLabel = Driver Classloader Name
driverClassloaderNamePropertyCategory = Driver
+isolationLevelPropertyDescription = The isolation level that should be used when connecting to the database.
+isolationLevelPropertyLabel = Database Isolation Level
+isolationLevelPropertyCategory = Driver
+
maximumConnectionsInPoolPropertyDescription = The maximum number of connections that may be in the connection pool. The default is "5". This is not required if the DataSource is found in JNDI.
maximumConnectionsInPoolPropertyLabel = Maximum Connection Pool Size
maximumConnectionsInPoolPropertyCategory = Driver
Index: extensions/modeshape-connector-store-jpa/src/test/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaSourceTest.java
===================================================================
--- extensions/modeshape-connector-store-jpa/src/test/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaSourceTest.java (revision 2491)
+++ extensions/modeshape-connector-store-jpa/src/test/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaSourceTest.java (working copy)
@@ -24,6 +24,8 @@
package org.modeshape.connector.store.jpa.model.simple;
import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
import org.modeshape.connector.store.jpa.JpaSource;
import org.modeshape.graph.ExecutionContext;
import org.modeshape.graph.Subgraph;
@@ -31,8 +33,6 @@ import org.modeshape.graph.connector.RepositoryConnection;
import org.modeshape.graph.connector.RepositoryConnectionFactory;
import org.modeshape.graph.connector.RepositoryContext;
import org.modeshape.graph.observe.Observer;
-import org.junit.Before;
-import org.junit.Test;
public class SimpleJpaSourceTest {
@@ -50,7 +50,7 @@ public class SimpleJpaSourceTest {
source.setUsername("sa");
source.setPassword("");
source.setUrl("jdbc:hsqldb:mem:.");
- source.setShowSql(true);
+ source.setShowSql(false);
source.setAutoGenerateSchema("create");
source.initialize(new RepositoryContext() {
@@ -82,4 +82,32 @@ public class SimpleJpaSourceTest {
connection.ping(1, TimeUnit.SECONDS);
connection.close();
}
+
+ /*
+ The test below helps establish that the hibernate.connection.isolation property is being passed to Hibernate
+ correctly and respected by Hibernate, but the test case is very brittle and should not be checked in.
+
+ If you wish to reverify this behavior, please uncomment the entityManager() method in SimpleJpaConnection.
+
+ @FixFor("MODE-983")
+ @Test
+ public void shouldAllowChangingIsolationLevel() throws Exception {
+ RepositoryConnection conn;
+ SimpleJpaConnection jpaConn;
+ EntityManagerImpl emgr;
+ int txLevel;
+
+ // txLevel = Connection.TRANSACTION_SERIALIZABLE;
+
+ txLevel = Connection.TRANSACTION_REPEATABLE_READ;
+ source.setIsolationLevel(txLevel);
+ conn = source.getConnection();
+ assertThat(conn, instanceOf(SimpleJpaConnection.class));
+
+ jpaConn = (SimpleJpaConnection)conn;
+ emgr = (EntityManagerImpl)jpaConn.entityManager();
+ assertTrue(emgr.getSession().connection().getTransactionIsolation() == txLevel);
+ jpaConn.close();
+ }
+ */
}