JBoss.orgCommunity Documentation
JBossJTA supports construction of both local and distributed transactional applications which access databases using the JDBC APIs. JDBC supports two-phase commit of transactions, and is similar to the XA X/Open standard. JBossTS provides JDBC support in package com.arjuna.ats.jdbc. A list of the tested drivers is available from the JBossTS website.
Only use the transactional JDBC support provided in package com.arjuna.ats.jdbc when you are using JBossTS outside of an application server, such as JBoss Application Server, or another container. Otherwise, use the JDBC support provided by your application server or container.
JBossJTA needs the ability to associate work performed on a JDBC connection with a specific transaction. Therefore, applications need to use a combination of implicit transaction propagation and indirect transaction management. For each JDBC connection, JBossJTA must be able to determine the invoking thread's current transaction context.
Nested transactions are not supported by JDBC. If you try to use a JDBC connection within a subtransaction,
JBossJTA throws a suitable exception and no work is allowed on that connection. However, if you need nested
transactions, and are comfortable with straying from the JDBC standard, you can set property
com.arjuna.ats.jta.supportSubtransactions
property to YES
.
The approach JBossJTA takes for incorporating JDBC connections within transactions is to provide transactional
JDBC drivers as conduits for all interactions. These drivers intercept all invocations and ensure that they are
registered with, and driven by, appropriate transactions. The driver
com.arjuna.ats.jdbc.TransactionalDriver
handles all JDBC drivers, implementing the
java.sql.Driver
interface. If the database is not transactional, ACID properties
cannot be guaranteed.
Example 2.1. Instantiating and using the driver within an application
TransactionalDriver arjunaJDBC2Driver = new TransactionalDriver();
Example 2.2. Registering the drivers with the JDBC driver manager using the Java system properties
Properties p = System.getProperties();
switch (dbType)
{
case MYSQL:
p.put("jdbc.drivers", "com.mysql.jdbc.Driver");
break;
case PGSQL:
p.put("jdbc.drivers", "org.postgresql.Driver");
break;
}
System.setProperties(p);
The jdbc.drivers property contains a colon-separated list of driver class names, which the JDBC driver manager loads when it is initialized. After the driver is loaded, you can use it to make a connection with a database.
Example 2.3. Using the Class.forName
method
Calling Class.forName()
automatically registers the driver with the JDBC driver
manager. It is also possible to explicitly create an instance of the JDBC driver.
sun.jdbc.odbc.JdbcOdbcDriver drv = new sun.jdbc.odbc.JdbcOdbcDriver();
DriverManager.registerDriver(drv);
Because JBossJTA provides JDBC connectivity via its own JDBC driver, application code can support transactions with relatively small code changes. Typically, the application programmer only needs to start and terminate transactions.
The JBossJTA driver accepts the following properties, all located in class
com.arjuna.ats.jdbc.TransactionalDriver
.
username |
the database username |
password |
the database password |
createDb |
creates the database automatically if set to |
dynamicClass |
specifies a class to instantiate to connect to the database, instead of using JNDI. |
JDBC connections are created from appropriate DataSources. Connections which participate in distributed transactions are obtained from XADataSources. When using a JDBC driver, JBossJTA uses the appropriate DataSource whenever a connection to the database is made. It then obtains XAResources and registers them with the transaction via the JTA interfaces. The transaction service uses these XAResources when the transaction terminates in order to drive the database to either commit or roll back the changes made via the JDBC connection.
JBossJTA JDBC support can obtain XADataSources through the Java Naming and Directory Interface (JNDI) or dynamic class instantiation.
A JDBC driver can use arbitrary DataSources without having to know specific details about their implementations, by using JNDI. A specific DataSource or XADataSource can be created and registered with an appropriate JNDI implementation, and the application, or JDBC driver, can later bind to and use it. Since JNDI only allows the application to see the DataSource or XADataSource as an instance of the interface (e.g., javax.sql.XADataSource) rather than as an instance of the implementation class (e.g., com.mydb.myXADataSource), the application is not tied at build-time to only use a specific implementation.
For the TransactionalDriver class to use a JNDI-registered XADataSource, you need to create the XADataSource instance and store it in an appropriate JNDI implementation. Details of how to do this can be found in the JDBC tutorial available at the Java web site.
Example 2.4. Storing a datasource in a JNDI implementation
XADataSource ds = MyXADataSource();
Hashtable env = new Hashtable();
String initialCtx = PropertyManager.getProperty("Context.INITIAL_CONTEXT_FACTORY");
env.put(Context.INITIAL_CONTEXT_FACTORY, initialCtx);
initialContext ctx = new InitialContext(env);
ctx.bind("jdbc/foo", ds);
The Context.INITIAL_CONTEXT_FACTORY property is the JNDI way of specifying the type of JNDI implementation to use.
The application must pass an appropriate connection URL to the JDBC driver:
Properties dbProps = new Properties();
dbProps.setProperty(TransactionalDriver.userName, "user");
dbProps.setProperty(TransactionalDriver.password, "password");
// the driver uses its own JNDI context info, remember to set it up:
jdbcPropertyManager.propertyManager.setProperty(
"Context.INITIAL_CONTEXT_FACTORY", initialCtx);
jdbcPropertyManager.propertyManager.setProperty(
"Context.PROVIDER_URL", myUrl);
TransactionalDriver arjunaJDBCDriver = new TransactionalDriver();
Connection connection = arjunaJDBCDriver.connect("jdbc:arjuna:jdbc/foo", dbProps);
The JNDI URL must be pre-pended with jdbc:arjuna:
in order for the TransactionalDriver to
recognize that the DataSource must participate within transactions and be driven accordingly.
If a JNDI implementation is not available. you can specify an implementation of the
DynamicClass
interface, which is used to get the XADataSource object. This is
not recommended, but provides a fallback for environments where use of JNDI is not feasible.
Use the property TransactionalDriver.dynamicClass
to specify the implementation to use. An
example is PropertyFileDynamicClass
, a DynamicClass implementation that reads the
XADataSource implementation class name and configuration properties from a file, then instantiates and
configures it.
The oracle_8_1_6 dynamic class is deprecated and should not be used.
Example 2.5. Instantiating a dynamic class
The application code must specify which dynamic class the TransactionalDriver should instantiate when setting up the connection:
Properties dbProps = new Properties();
dbProps.setProperty(TransactionalDriver.userName, "user");
dbProps.setProperty(TransactionalDriver.password, "password");
dbProps.setProperty(TransactionalDriver.dynamicClass,
"com.arjuna.ats.internal.jdbc.drivers.PropertyFileDynamicClass");
TransactionalDriver arjunaJDBC2Driver = new TransactionalDriver();
Connection connection = arjunaJDBC2Driver.connect("jdbc:arjuna:/path/to/property/file", dbProperties);
Once the connection is established, all operations on the connection are monitored by JBossJTA. you do not need to use the transactional connection within transactions. If a transaction is not present when the connection is used, then operations are performed directly on the database.
JDBC does not support subtransactions.
You can use transaction timeouts to automatically terminate transactions if a connection is not terminated within an appropriate period.
You can use JBossJTA connections within multiple transactions simultaneously. An example would be different
threads, with different notions of the current transaction. JBossJTA does connection pooling for each
transaction within the JDBC connection. Although multiple threads may use the same instance of the JDBC
connection, internally there may be a separate connection for each transaction. With the exception of method
close
, all operations performed on the connection at the application level are only
performed on this transaction-specific connection.
JBossJTA automatically registers the JDBC driver connection with the transaction via an appropriate resource. When the transaction terminates, this resource either commits or rolls back any changes made to the underlying database via appropriate calls on the JDBC driver.
Once created, the driver and any connection can be used in the same way as any other JDBC driver or connection.
Example 2.6. Creating and using a connection
Statement stmt = conn.createStatement();
try
{
stmt.executeUpdate("CREATE TABLE test_table (a INTEGER,b INTEGER)");
}
catch (SQLException e)
{
// table already exists
}
stmt.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)");
ResultSet res1 = stmt.executeQuery("SELECT * FROM test_table");
For each user name and password, JBossJTA maintains a single instance of each connection for as long as that
connection is in use. Subsequent requests for the same connection get a reference to the original connection,
rather than a new instance. You can try to close the connection, but the connection will only actually be closed
when all users (including transactions) have either finished with the connection, or issued
close
calls.
Some JDBC drivers allow the reuse of a connection for multiple different transactions once a given transaction
completes. Unfortunately this is not a common feature, and other drivers require a new connection to be
obtained for each new transaction. By default, the JBossJTA transactional driver always obtains a new
connection for each new transaction. However, if an existing connection is available and is currently unused,
JBossJTA can reuse this connection. To turn on this feature, add option reuseconnection=true
to the JDBC URL. For instance, jdbc:arjuna:sequelink://host:port;databaseName=foo;reuseconnection=true
When a transaction with an associated JDBC connection terminates, because of the application or because a transaction timeout expires, JBossJTA uses the JDBC driver to drive the database to either commit or roll back any changes made to it. This happens transparently to the application.
If property AutoCommit
of the interface java.sql.Connection
is set to
true
for JDBC, the execution of every SQL statement is a separate top-level transaction, and
it is not possible to group multiple statements to be managed within a single OTS transaction. Therefore,
JBossJTA disables AutoCommit
on JDBC connections before they can be used. If
AutoCommit
is later set to true
by the application, JBossJTA throws the
java.sql.SQLException
.
When you use the JBossJTA JDBC driver, you may need to set the underlying transaction isolation level on the XA
connection. By default, this is set to TRANSACTION_SERIALIZABLE
, but another value may be
more appropriate for your application. To change it, set the property
com.arjuna.ats.jdbc.isolationLevel
to the appropriate isolation level in string form. Example
values are TRANSACTION_READ_COMMITTED
or TRANSACTION_REPEATABLE_READ
.
Currently, this property applies to all XA connections created in the JVM.