Transaktionen (Datenbank und Java EE / JTA)

+ andere TechDocs
+ EJB
+ Sun JTA
+




Inhalt

  1. Begriffe im Transaktionsumfeld
    1. Transaktion
    2. Begin, Commit, Rollback, Transaktionsklammer, LUW
    3. ACID
    4. Dirty Read, Non-repeatable Read, Phantom Read
    5. Transaction Isolation Level
    6. Transaction Propagation
    7. JTA, Transaktionsmanager, verteilte Transaktionen, XA-Two-Phase-Commit
  2. Datenbank-Transaktionen
    1. Installation
    2. Rollback
    3. Transaction Isolation
  3. Session Beans im Java EE Application Server
  4. CMT (Container Managed Transactions)
  5. BMT (Bean Managed Transactions) über JTA (Java Transaction API)
  6. Client-gesteuerte Transaktion mit JTA
  7. JDBC-Treiber mit XA-Two-Phase-Commit ohne JTA/JTS ansteuern
  8. Transaktionen mit JPA
  9. Transaktionen mit Hibernate
  10. Transaktionen mit Spring
  11. Links auf weiterführende Informationen


Begriffe im Transaktionsumfeld

Konsistenz durch Transaktion

Mit Transaktionen im engeren technischen Sinne ist gemeint, dass mehrere Arbeitsschritte zusammengefasst werden und entweder alle ausgeführt werden, oder alle nicht ausgeführt werden (Alles-oder-nichts-Prinzip).

Wenn Geld von einem Konto auf ein anderes gebucht werden soll, dann darf es nicht vorkommen, dass die Abbuchung vom ersten Konto erfolgreich durchgeführt wird, aber die Gutschrift auf das zweite Konto fehlschlägt.

Transaktionen stellen Konsistenz sicher.

Begin, Commit, Rollback, Transaktionsklammer, LUW

Transaktionen werden durch ein "Begin"-Kommando gestartet. Konnten die einzelnen Arbeitsschritte erfolgreich durchgeführt werden, werden sie durch ein "Commit"-Kommando endgültig bestätigt (und für andere Prozesse sichtbar). Gab es einen Fehler, werden sie durch ein "Rollback"-Kommando zurückgesetzt.

Das Begin-Startkommando und das beendende Commit- bzw. Rollback-Kommando stellt die so genannte Transaktionsklammer dar.

Die Arbeitsschritte innerhalb der Transaktion werden auch schon mal als LUW (Logical Unit of Work) bezeichnet.

ACID

Transaktionen werden an den "ACID"-Kriterien gemessen:

Dirty Read, Non-repeatable Read (Stale Data), Phantom Read

Wenn Transaktionen dem "ACID"-Prinzip entsprechen sollen, müssen sie serialisiert nacheinander durchgeführt werden. Aus Performance-Gründen wird hiervon oft abgewichen und ein niedrigerer "Transaction Isolation Level" eingestellt.

Das kann zu folgenden Fehlern führen:

Transaction Isolation Level

In Datenbanken können verschiedene so genannte "Transaction Isolation Level" eingestellt werden, um den besten Kompromiss zwischen Isolation und Performance vorzugeben.

Folgende "Transaction Isolation Level" sind in ANSI-SQL2 definiert:

Der Oracle WebLogic Java EE Application Server kennt noch weitere spezielle "Transaction Isolation Level", die insbesonders im Zusammenspiel mit der Oracle Datenbank sinnvoll sein können:

Transaction Propagation

In einigen Transaktionsmanagern kann die "Transaction Propagation" pro Methode unterschiedlich eingestellt werden. Es sind nicht immer alle Einstellungen möglich.

Die unterschiedlichen Einstellungen zur Transaction Propagation bewirken beim Eintritt in die jeweilige Methode Folgendes:

JTA, Transaktionsmanager, verteilte Transaktionen, XA-Two-Phase-Commit

Während für einfache Datenbank-Transaktionen mit nur einer Datenbank die Begin-, Commit- und Rollback-Kommandos ausreichen, wird für komplexere Transaktionssteuerungen und bei verteilten Transaktionen über mehrere Ressourcen (z.B. mehrere Datenbanken oder auch DB + JMS + JCA + ...) ein Transaktionsmanager benötigt.

Zu diesem Themenbereich sind folgende Begriffe wichtig:



Datenbank-Transaktionen

Installation

Downloaden Sie ein aktuelles Java SE JDK von http://www.oracle.com/technetwork/java/javase/downloads und installieren Sie es wie beschrieben unter java-install.htm.

Die folgenden Beispiele verwenden MySQL als Datenbank, können aber durch leichte Modifikationen an beliebige andere transaktionsfähige SQL-Datenbanken angepasst werden.

Installieren Sie MySQL wie beschrieben unter: mysql.htm#InstallationUnterWindows. Die folgenden Beispiele gehen davon aus, dass Sie als 'Database-Name' "MeineDb" wählen ("CREATE DATABASE MeineDb;") (Sie können auch stattdessen 'dbUrl' anpassen).

MySQL verwendet defaultmäßig die 'MyISAM'-Engine, die aber keine Transaktionen unterstützt und deshalb für die folgenden Beispiele ungeeignet ist. Wir wollen die 'InnoDB'-Engine verwenden: Öffnen Sie die zu MySQL gehörende 'my.ini'- oder 'my.cnf'-Datei, zum Beispiel in den Verzeichnissen '/etc/mysql', 'C:\', 'C:\Windows' oder 'C:\Programme\MySQL\MySQL Server 4.1'. Stellen Sie sicher dass nicht 'skip-innodb' gesetzt ist und kontrollieren Sie die anderen 'InnoDB Specific options' (z.B. 'innodb_data_file_path=ibdata:30M').

Downloaden Sie den MySQL-JDBC-Treiber (z.B. 'mysql-connector-java-5.1.16-bin.jar' aus 'mysql-connector-java-5.1.16.zip').

Legen Sie eine Projektverzeichnisstruktur an, zum Beispiel so:

[\MeinWorkspace]
  '- [TxnDb]
       |- [bin]
       |- [lib]
       |    '- mysql-connector-java-5.1.16-bin.jar
       '- [src]
            '- [dbtransactions]
                 '- ... (Java-Klassen)

Rollback

Erzeugen Sie im Package-Verzeichnis 'dbtransactions' folgende Java-Datei 'TxnRollback.java':

package dbtransactions;

import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TxnRollback
{
  static final String dbDrv = "com.mysql.jdbc.Driver";
  static final String dbUrl = "jdbc:mysql://localhost:3306/MeineDb";
  static final String dbUsr = "root";
  static final String dbPwd = "mysqlpwd";
  static final String dbTyp = "ENGINE=InnoDB";
  static final String dbTbl = "MeineTestTabelle1";

  public static void main( String[] args ) throws ClassNotFoundException, SQLException
  {
    Connection cn = null;
    Statement  st = null;
    try {
      Class.forName( dbDrv );
      cn = DriverManager.getConnection( dbUrl, dbUsr, dbPwd );
      cn.setAutoCommit( false );
      System.out.println( "AutoCommit=" + cn.getAutoCommit() +
              ", TransactionIsolation=" + cn.getTransactionIsolation() );
      st = cn.createStatement();
      try { st.executeUpdate( "drop table " + dbTbl ); } catch( SQLException ex ) {/*ok*/}
      st.executeUpdate( "create table " + dbTbl + " ( t VARCHAR(12) ) " + dbTyp );
      st.executeUpdate( "insert into " + dbTbl + " values ( '" + getTime() + "' )" );
      readData( "Nach Insert:", st );
      cn.rollback();  // 'cn.commit();' bzw. 'cn.rollback();'
      readData( "Nach Commit bzw. Rollback:", st );
    } finally {
      try { if( null != st ) st.close(); } catch( Exception ex ) {/*ok*/}
      try { if( null != cn ) cn.close(); } catch( Exception ex ) {/*ok*/}
    }
  }

  private static final void readData( String s, Statement st ) throws SQLException
  {
    System.out.println( s );
    ResultSet rs = st.executeQuery( "select * from " + dbTbl );
    if( rs.next() ) System.out.println( "  " + rs.getString( 1 ) );
              else  System.out.println( "  Keine Daten." );
    try { if( null != rs ) rs.close(); } catch( Exception ex ) {/*ok*/}
  }

  private static final String getTime()
  {
    return (new SimpleDateFormat( "HH:mm:ss.SSS" )).format( new Date() );
  }
}

Passen Sie die String-Konstanten 'dbDrv', 'dbUrl', 'dbUsr' und 'dbPwd' an.

Kompilation und Ausführung können z.B. folgendermaßen aus dem Sourceverzeichnis 'TxnDb\src' heraus erfolgen (passen Sie die Pfade an):

cd \MeinWorkspace\TxnDb\src

javac -d ../bin dbtransactions/TxnRollback.java

java -cp ../bin;../lib/mysql-connector-java-5.1.16-bin.jar dbtransactions.TxnRollback

Vor dem Rollback-Kommando werden die Daten korrekt gelesen, nach dem Rollback-Kommando gibt es keine Daten mehr (falls das bei Ihnen nicht so ist, stimmt etwas mit der MySQL-InnoDB-Konfiguration nicht).

Wenn Sie 'cn.rollback();' durch 'cn.commit();' ersetzen, bleiben die Daten persistent gespeichert.

Ersetzen Sie 'cn.commit();' wieder durch 'cn.rollback();', aber entfernen Sie beim 'create table'-Kommando den Zusatz 'ENGINE=InnoDB': Auch dann bleiben die Daten persistent gespeichert, weil der Rollback nicht funktioniert.

Transaction Isolation

Das folgende Beispiel stellt verschiedene Transaction Isolation Level ein und untersucht die Ergebnisse auf Dirty Reads, Non-repeatable Reads und Phantom Reads.

Erzeugen Sie im Package-Verzeichnis 'dbtransactions' folgende Java-Hilfsdatei 'DbUtil.java':

package dbtransactions;

import java.sql.*;

public class DbUtil
{
  public static final void showTransactionSupport( String dbDrv, String dbUrl, String dbUsr, String dbPwd )
  throws ClassNotFoundException, SQLException
  {
    Connection cn = null;
    try {
      Class.forName( dbDrv );
      cn = DriverManager.getConnection( dbUrl, dbUsr, dbPwd );
      DatabaseMetaData md = cn.getMetaData() ;
      System.out.println( "supportsTransactions():        " + md.supportsTransactions() );
      System.out.println( "TRANSACTION_READ_UNCOMMITTED:  " + md.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED) );
      System.out.println( "TRANSACTION_READ_COMMITTED:    " + md.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED) );
      System.out.println( "TRANSACTION_REPEATABLE_READ:   " + md.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ) );
      System.out.println( "TRANSACTION_SERIALIZABLE:      " + md.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE) );
      System.out.println( "Default transaction isolation: " + md.getDefaultTransactionIsolation() );
      System.out.println( "Actual  transaction isolation: " + cn.getTransactionIsolation() );
      System.out.println( "Actual  AutoCommit:            " + cn.getAutoCommit() );
    } finally {
      try { if( null != cn ) cn.close(); } catch( Exception ex ) {/*ok*/}
    }
  }

  public static final void showDbTbl( String s, Statement st, String dbTbl ) throws SQLException
  {
    ResultSet rs = null;
    try {
      rs = st.executeQuery( "select * from " + dbTbl );
      ResultSetMetaData rsmd = rs.getMetaData();
      int i, n = rsmd.getColumnCount();
      System.out.println( s );
      for( i=0; i<n; i++ )
        System.out.print( "+---------------" );
      System.out.println( "+" );
      for( i=1; i<=n; i++ )
        System.out.print( "| " + extendStringTo14( rsmd.getColumnName( i ) ) );
      System.out.println( "|" );
      for( i=0; i<n; i++ )
        System.out.print( "+---------------" );
      System.out.println( "+" );
      while( rs.next() ) {
        for( i=1; i<=n; i++ )
          System.out.print( "| " + extendStringTo14( rs.getString( i ) ) );
        System.out.println( "|" );
      }
      for( i=0; i<n; i++ )
        System.out.print( "+---------------" );
      System.out.println( "+" );
    } finally {
      try { if( null != rs ) rs.close(); } catch( Exception ex ) {/*ok*/}
    }
  }

  // Extend String to length of 14 characters
  private static final String extendStringTo14( String s )
  {
    if( null == s ) s = "";
    final String sFillStrWithWantLen = "              ";
    final int iWantLen = sFillStrWithWantLen.length();
    final int iActLen  = s.length();
    if( iActLen < iWantLen )
      return (s + sFillStrWithWantLen).substring( 0, iWantLen );
    if( iActLen > 2 * iWantLen )
      return s.substring( 0, 2 * iWantLen );
    return s;
  }

  public static final int countRows( Statement st, String dbTbl ) throws SQLException
  {
    int c = 0;
    ResultSet rs = st.executeQuery( "select count(*) from " + dbTbl );
    if( rs.next() ) c = rs.getInt( 1 );
    rs.close();
    return c;
  }
}

Erzeugen Sie im Package-Verzeichnis 'dbtransactions' folgende Java-Datei 'TxnIsol.java':

package dbtransactions;

import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TxnIsol
{
  static final String dbDrv = "com.mysql.jdbc.Driver";
  static final String dbUrl = "jdbc:mysql://localhost:3306/MeineDb";
  static final String dbUsr = "root";
  static final String dbPwd = "mysqlpwd";
  static final String dbTyp = "ENGINE=InnoDB";
  static final String dbTbl = "MeineTestTabelle1";

  public static void main( String[] args )
  throws ClassNotFoundException, SQLException, InterruptedException
  {
    DbUtil.showTransactionSupport( dbDrv, dbUrl, dbUsr, dbPwd );
    testTransactionIsolation( Connection.TRANSACTION_READ_UNCOMMITTED );
    testTransactionIsolation( Connection.TRANSACTION_READ_COMMITTED );
    testTransactionIsolation( Connection.TRANSACTION_REPEATABLE_READ );
    testTransactionIsolation( Connection.TRANSACTION_SERIALIZABLE );
  }

  public static void testTransactionIsolation( int txnisol )
  throws ClassNotFoundException, SQLException, InterruptedException
  {
    System.out.println( "\n===========================================================" );
    prepareTable();
    System.out.println( "\n" + getTime() + " prepareTable() ready." );
    (new MyThread1( txnisol )).start();
    (new MyThread2( txnisol )).start();
    Thread.sleep( 1000 );
  }

  private static final void prepareTable() throws ClassNotFoundException, SQLException
  {
    Connection cn = null;
    Statement  st = null;
    try {
      Class.forName( dbDrv );
      cn = DriverManager.getConnection( dbUrl, dbUsr, dbPwd );
      st = cn.createStatement();
      try { st.executeUpdate( "drop table " + dbTbl ); } catch( SQLException ex ) {/*ok*/}
      st.executeUpdate( "create table " + dbTbl + " ( i INT, s VARCHAR(12), t VARCHAR(12) ) " + dbTyp );
      st.executeUpdate( "insert into " + dbTbl + " values ( 0, 'Null', '" + getTime() + "' );" );
    } finally {
      try { if( null != st ) st.close(); } catch( Exception ex ) {/*ok*/}
      try { if( null != cn ) cn.close(); } catch( Exception ex ) {/*ok*/}
    }
  }

  public static final String getTime()
  {
    return (new SimpleDateFormat( "HH:mm:ss.SSS" )).format( new Date() );
  }
}


class MyThread1 extends Thread
{
  int txnisol;

  MyThread1( int txnisol )
  {
    this.txnisol = txnisol;
  }

  @Override
  public void run()
  {
    Connection cn1 = null;
    Statement  st1 = null;
    int c1=0, c2=0;
    try {
      Thread.sleep( 100 );
      Class.forName( TxnIsol.dbDrv );
      cn1 = DriverManager.getConnection( TxnIsol.dbUrl, TxnIsol.dbUsr, TxnIsol.dbPwd );
      cn1.setTransactionIsolation( txnisol );
      cn1.setAutoCommit( false );
      System.out.println( "\nThread1: AutoCommit=" + cn1.getAutoCommit() +
                         ", TransactionIsolation=" + cn1.getTransactionIsolation() + "\n" );
      st1 = cn1.createStatement();
      st1.executeUpdate( "insert into " + TxnIsol.dbTbl + " values ( 1, 'Thread1', '" + TxnIsol.getTime() + "' );" );
      DbUtil.showDbTbl( TxnIsol.getTime() + ", Thread1:", st1, TxnIsol.dbTbl );
      c1 = DbUtil.countRows( st1, TxnIsol.dbTbl );
      Thread.sleep( 400 );
      c2 = DbUtil.countRows( st1, TxnIsol.dbTbl );
      System.out.println( "\nc1=" + c1 + ", c2=" + c2 );
      DbUtil.showDbTbl( TxnIsol.getTime() + ", Thread1:", st1, TxnIsol.dbTbl );
      System.out.println( "\n" + TxnIsol.getTime() + ", Thread1: Commit" );
      cn1.commit();
    } catch( Exception ex ) {
      ex.printStackTrace();
    } finally {
      try { if( null != st1 ) st1.close(); } catch( Exception ex ) {/*ok*/}
      try { if( null != cn1 ) cn1.close(); } catch( Exception ex ) {/*ok*/}
    }
  }
}


class MyThread2 extends Thread
{
  int txnisol;

  MyThread2( int txnisol )
  {
    this.txnisol = txnisol;
  }

  @Override
  public void run()
  {
    Connection cn2 = null;
    Statement  st2 = null;
    try {
      Thread.sleep( 300 );
      Class.forName( TxnIsol.dbDrv );
      cn2 = DriverManager.getConnection( TxnIsol.dbUrl, TxnIsol.dbUsr, TxnIsol.dbPwd );
      cn2.setTransactionIsolation( txnisol );
      cn2.setAutoCommit( false );
      System.out.println( "\nThread2: AutoCommit=" + cn2.getAutoCommit() +
                         ", TransactionIsolation=" + cn2.getTransactionIsolation() + "\n" );
      st2 = cn2.createStatement();
      DbUtil.showDbTbl( TxnIsol.getTime() + ", Thread2:", st2, TxnIsol.dbTbl );
      st2.executeUpdate( "insert into " + TxnIsol.dbTbl + " values ( 2, 'Thread2', '" + TxnIsol.getTime() + "' );" );
      System.out.println( "\n" + TxnIsol.getTime() + ", Thread2: Commit insert i=2" );
      cn2.commit();
      st2.executeUpdate( "update " + TxnIsol.dbTbl + " set s = 'Thread2', t = '" + TxnIsol.getTime() + "' where i = 0;" );
      System.out.println( TxnIsol.getTime() + ", Thread2: Commit update i=0\n" );
      cn2.commit();
      DbUtil.showDbTbl( TxnIsol.getTime() + ", Thread2:", st2, TxnIsol.dbTbl );
      cn2.commit();
    } catch( Exception ex ) {
      ex.printStackTrace();
    } finally {
      try { if( null != st2 ) st2.close(); } catch( Exception ex ) {/*ok*/}
      try { if( null != cn2 ) cn2.close(); } catch( Exception ex ) {/*ok*/}
    }
  }
}

Passen Sie die String-Konstanten 'dbDrv', 'dbUrl', 'dbUsr' und 'dbPwd' an.

Kompilation und Ausführung können z.B. folgendermaßen aus dem Sourceverzeichnis 'TxnDb\src' heraus erfolgen (passen Sie die Pfade an):

cd \MeinWorkspace\TxnDb\src

javac -d ../bin dbtransactions/TxnIsol.java

java -cp ../bin;../lib/mysql-connector-java-5.1.16-bin.jar dbtransactions.TxnIsol

Der erste Ergebnisblock für 'TRANSACTION_READ_UNCOMMITTED' kann zum Beispiel so aussehen:

20:00:00.078 prepareTable() ready.

Thread1: TransactionIsolation=1  <-- TRANSACTION_READ_UNCOMMITTED

20:00:00.234, Thread1:
+---+---------+--------------+
| i | s       | t            |
+---+---------+--------------+
| 0 | Null    | 20:00:00.046 |
| 1 | Thread1 | 20:00:00.234 |
+---+---------+--------------+

Thread2: TransactionIsolation=1  <-- TRANSACTION_READ_UNCOMMITTED

20:00:00.484, Thread2:
+---+---------+--------------+
| i | s       | t            |
+---+---------+--------------+
| 0 | Null    | 20:00:00.046 |
| 1 | Thread1 | 20:00:00.234 |   <-- Dirty Read
+---+---------+--------------+

20:00:00.484, Thread2: Commit insert i=2

c1=2, c2=3                       <-- Phantom Read
20:00:00.656, Thread1:
+---+---------+--------------+
| i | s       | t            |
+---+---------+--------------+
| 0 | Thread2 | 20:00:00.546 |   <-- Non-repeatable Read
| 1 | Thread1 | 20:00:00.234 |
| 2 | Thread2 | 20:00:00.484 |   <-- Phantom Read
+---+---------+--------------+

20:00:00.671, Thread1: Commit
20:00:00.671, Thread2: Commit update i=0

20:00:00.750, Thread2:
+---+---------+--------------+
| i | s       | t            |
+---+---------+--------------+
| 0 | Thread2 | 20:00:00.546 |
| 1 | Thread1 | 20:00:00.234 |
| 2 | Thread2 | 20:00:00.484 |
+---+---------+--------------+

Die Ergebnisse sind etwas mühsam zu interpretieren, aber Sie können sehen, dass bei 'TRANSACTION_READ_UNCOMMITTED' alle drei Fehlerarten (Dirty Reads, Non-repeatable Reads und Phantom Reads) auftreten und bei 'TRANSACTION_SERIALIZABLE' keine mehr.



Session Beans im Java EE Application Server

Eine Java-EE-Webanwendungen mit EJBs (Enterprise JavaBeans) kann typischerweise etwa folgende vereinfachte Struktur haben (genaueres hierzu finden Sie unter sw-patterns.htm#JavaEE-Patterns):


                               Client    
                                  |
                                  |HTTP
    ............................. | .............................
    .                             |                Web Container.
    .                     Front Controller                      .
    .                             |                             .
    .                     Business Delegate                     .
    ............................. | .............................
                                  |RMI-IIOP
    ............................. | .............................
    .                             |                EJB Container.
    .               Session Facade (Session EJB)                .
    .         ____________________|____________________         .
    .        |             |             |             |        .
    .   Entity EJB    Entity EJB    Entity EJB       MDB       .
    .        |             |             |             |        .
    ........ | ........... | ........... | ........... | ........
             |             |             |             |
              SQL-Datenbank        ERP, Legacy       JMS    

Java-EE-Transaktionen werden fast nie von Entity Beans, sondern meistens von Session Beans verwaltet (oder von externen Komponenten). Java-EE-Transaktionen können nicht nur einzelne Aktionen beinhalten, sondern auch mehrere RMI-Aufrufe, mehrere Datenbankoperationen und mehrere JMS-Nachrichten umfassen. Unter bestimmten Voraussetzungen können auch JCA-Konnektoren an Transaktionen teilnehmen.

Die Transaktionsklammer wird üblicherweise in der Session EJB in der Session Facade gesetzt.

Bei der Transaktionssteuerung wird unterschieden zwischen:



CMT (Container Managed Transactions)

Bei CMT (Container Managed Transactions) (mit '<transaction-type>Container</transaction-type>' in der 'ejb-jar.xml') bilden in der Regel die Remote-Methoden der Session-Beans eine Transaktionseinheit.

Bei Container Managed Transactions übernimmt der EJB-Container die Verwaltung der Transaktionen. Es brauchen keine Begin-, Commit- und Rollback-Kommandos programmiert zu werden. In der 'ejb-jar.xml' wird deklarativ eingestellt, ob alle oder welche Methoden transaktionsgesteuert werden sollen und mit welchem '<trans-attribute>' (meistens 'Required').

In Session Beans kann ein Rollback auch explizit ausgelöst werden. Dabei muss beachtet werden, dass bei CMT nicht 'ctx.getUserTransaction().setRollbackOnly()', sondern 'ctx.setRollbackOnly()' aufgerufen werden muss (wie weiter unten das Beispiel 'CmtSbImpl.java' zeigt).

Das folgende Beispiel demonstriert einen Rollback in einer CMT-Session-Bean unter JBoss zusammen mit MySQL.

  1. MySQL

    Installieren Sie MySQL wie oben beschrieben (inklusive 'InnoDB'-Engine).

  2. JBoss

    Installieren Sie JBoss wie beschrieben unter jee-ejb2.htm#InstallationJBoss.

  3. Projektverzeichnis, Lib, build.xml, jndi.properties, log4j.properties

    Legen Sie ein Projektverzeichnis an, z.B. 'D:\MeinWorkspace\MeinEjbProjekt' (im Folgenden '<MeinEjbProjekt>' genannt).

    Legen Sie im Projektverzeichnis '<MeinEjbProjekt>' das Unterverzeichnis 'lib' sowie das Unterverzeichnis 'src' und darin die Unterverzeichnisse 'src\meinclient' und 'src\meintransactiontest' an:

    [\MeinWorkspace]
      '- [MeinEjbProjekt]
           |- [lib]
           |    '- jbossall-client.jar
           |- [src]
           |    |- [meinclient]
           |    '- [meintransactiontest]
           |- build.xml
           |- jndi.properties
           '- log4j.properties
    
  4. Kopieren Sie in das Unterverzeichnis '<MeinEjbProjekt>\lib' die zum Java EE Application Server gehörende .jar-Library. Für JBoss ist dies die 'jbossall-client.jar' aus dem JBoss-'client'-Verzeichnis (z.B. 'C:\JBoss\client'). Für andere Java EE Application Server siehe: jee-jndi.htm#AppClient-Lib.

  5. Speichern Sie im Verzeichnis '<MeinEjbProjekt>' die drei Dateien 'build.xml', 'jndi.properties' und 'log4j.properties'.

  6. Container Managed Persistence Entity Bean Home Interface

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'CmpEbHomeIF.java':

    package meintransactiontest;
    
    import java.rmi.RemoteException;
    import javax.ejb.*;
    
    public interface CmpEbHomeIF extends EJBHome
    {
      public CmpEbIF create() throws RemoteException, CreateException;
      public CmpEbIF findByPrimaryKey( Object oid ) throws RemoteException, FinderException;
    }
    
  7. Container Managed Persistence Entity Bean Remote Interface

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'CmpEbIF.java':

    package meintransactiontest;
    
    import java.rmi.RemoteException;
    import javax.ejb.EJBObject;
    
    public interface CmpEbIF extends EJBObject
    {
      String getEntityValue() throws RemoteException;
      void   setEntityValue( String entityValue ) throws RemoteException;
    }
    
  8. Container Managed Persistence Entity Bean Implementation

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'CmpEbImpl.java':

    package meintransactiontest;
    
    import javax.ejb.*;
    
    public abstract class CmpEbImpl implements EntityBean
    {
      private EntityContext ctx;
    
      public abstract String getEntityValue();
      public abstract void   setEntityValue( String entityValue );
    
      public Object ejbCreate() throws CreateException { return null; }
      public void ejbPostCreate() {}
      public void ejbActivate() {}
      public void ejbPassivate() {}
      public void ejbLoad() {}
      public void ejbStore() {}
      public void ejbRemove() {}
    
      public void setEntityContext( EntityContext entityContext )
      {
        ctx = entityContext;
      }
    
      public void unsetEntityContext()
      {
        ctx = null;
      }
    }
    
  9. Container Managed Transaction Session Bean Home Interface

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'CmtSbHomeIF.java':

    package meintransactiontest;
    
    import java.rmi.RemoteException;
    import javax.ejb.*;
    
    public interface CmtSbHomeIF extends EJBHome
    {
      public CmtSbIF create() throws RemoteException, CreateException;
    }
    
  10. Container Managed Transaction Session Bean Remote Interface

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'CmtSbIF.java':

    package meintransactiontest;
    
    import java.rmi.RemoteException;
    import javax.ejb.*;
    
    public interface CmtSbIF extends EJBObject
    {
      void   insertTestEntry() throws RemoteException, CreateException;
      void   testRollback( boolean rollback ) throws RemoteException, FinderException;
      String showEntityValue( String s ) throws RemoteException, FinderException;
    }
    
  11. Container Managed Transaction Session Bean Implementation

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'CmtSbImpl.java':

    package meintransactiontest;
    
    import java.rmi.RemoteException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import javax.ejb.*;
    import javax.rmi.PortableRemoteObject;
    
    public class CmtSbImpl implements SessionBean
    {
      private static final SimpleDateFormat DF = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS" );
      private SessionContext ctx;
      private CmpEbHomeIF cmpEbHome;
      private Object pk1;
    
      public void setSessionContext( SessionContext sessionContext )
      {
        ctx = sessionContext;
      }
    
      public void insertTestEntry() throws RemoteException, CreateException
      {
        Object cmpEbRef = ctx.lookup( "CmpEb" );  // "java:comp/env/ejb/CmpEb"
        cmpEbHome = (CmpEbHomeIF) PortableRemoteObject.narrow( cmpEbRef, CmpEbHomeIF.class );
        CmpEbIF cmpEb = cmpEbHome.create();
        pk1 = cmpEb.getPrimaryKey();
        cmpEb.setEntityValue( "Urspruenglicher Wert" );
      }
    
      public void testRollback( boolean rollback ) throws RemoteException, FinderException
      {
        CmpEbIF cmpEb = cmpEbHome.findByPrimaryKey( pk1 );
        cmpEb.setEntityValue( "Geaendert: " + DF.format( new Date() ) );
        if( rollback )
          ctx.setRollbackOnly();
      }
    
      public String showEntityValue( String s ) throws RemoteException, FinderException
      {
        if( null == s ) s = "";
        CmpEbIF cmpEb = cmpEbHome.findByPrimaryKey( pk1 );
        s += ":\n" + cmpEb.getEntityValue();
        return s;
      }
    
      public void ejbCreate() {}
      public void ejbActivate() {}
      public void ejbPassivate() {}
      public void ejbRemove() {}
    }
    
  12. Standard EJB Deployment-Descriptor

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'ejb-jar.xml':

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE ejb-jar PUBLIC
      "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
      "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
    <ejb-jar>
      <enterprise-beans>
        <session>
          <ejb-name> CmtSb </ejb-name>
          <home> meintransactiontest.CmtSbHomeIF </home>
          <remote> meintransactiontest.CmtSbIF </remote>
          <ejb-class> meintransactiontest.CmtSbImpl </ejb-class>
          <session-type> Stateless </session-type>
          <transaction-type> Container </transaction-type>
        </session>
        <entity>
          <display-name> CmpEb </display-name>
          <ejb-name> CmpEb </ejb-name>
          <home> meintransactiontest.CmpEbHomeIF </home>
          <remote> meintransactiontest.CmpEbIF </remote>
          <ejb-class> meintransactiontest.CmpEbImpl </ejb-class>
          <persistence-type> Container </persistence-type>
          <prim-key-class> java.lang.Object </prim-key-class>
          <reentrant> false </reentrant>
          <cmp-version> 2.x </cmp-version>
          <abstract-schema-name> CmpEb </abstract-schema-name>
          <cmp-field><field-name> entityValue </field-name></cmp-field>
          <resource-ref>
            <res-ref-name> jdbc/MeinDatasourceJndiName </res-ref-name>
            <res-type> javax.sql.DataSource </res-type>
            <res-auth> Container </res-auth>
          </resource-ref>
        </entity>
      </enterprise-beans>
      <assembly-descriptor>
        <container-transaction>
          <method>
            <ejb-name> CmtSb </ejb-name>
            <method-name> * </method-name>
          </method>
          <trans-attribute> Required </trans-attribute>
        </container-transaction>
      </assembly-descriptor>
    </ejb-jar>
    
  13. JBoss JDBC Deployment-Descriptor

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meintransactiontest' die folgende Datei 'jbosscmp-jdbc.xml':

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE jbosscmp-jdbc PUBLIC "-//JBoss//DTD JBOSSCMP-JDBC 4.0//EN"
              "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_4_0.dtd">
    <jbosscmp-jdbc>
      <defaults>
        <datasource> java:/MeinDatasourceJndiName </datasource>
        <datasource-mapping> mySQL </datasource-mapping>
        <create-table> true </create-table>
        <remove-table> true </remove-table>
        <unknown-pk>
          <unknown-pk-class> java.lang.Integer </unknown-pk-class>
          <field-name> oid </field-name>
          <column-name> oid </column-name>
          <jdbc-type> INTEGER </jdbc-type>
          <sql-type> INT(11) </sql-type>
          <auto-increment />
        </unknown-pk>
        <entity-command name="mysql-get-generated-keys" />
      </defaults>
    </jbosscmp-jdbc>
    
  14. Connector Service

    Registrieren Sie in JBoss (bei gestopptem JBoss) einen geeigneten Connector Service, indem Sie aus dem '<JBOSS_HOME>\docs\examples\jca'-Verzeichnis die passende '...-ds.xml'-Datei, also für die MySQL-Datenbank die 'mysql-ds.xml'-Datei, in das '<JBOSS_HOME>\server\default\deploy'-Verzeichnis kopieren und zum Beispiel folgendermaßen anpassen:

    <?xml version="1.0" encoding="UTF-8"?>
    <datasources>
      <local-tx-datasource>
        <jndi-name>MeinDatasourceJndiName</jndi-name>
        <connection-url>jdbc:mysql://localhost:3306/MeineDb</connection-url>
        <driver-class>com.mysql.jdbc.Driver</driver-class>
        <user-name>root</user-name>
        <password>mysqlpwd</password>
        <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
        <metadata>
          <type-mapping>mySQL</type-mapping>
        </metadata>
      </local-tx-datasource>
    </datasources>
    

    Passen Sie die Einträge an Ihre Datenbankverbindung an.

  15. Test-Client

    Speichern Sie im Verzeichnis '<MeinEjbProjekt>\src\meinclient' die folgende Datei 'CmtTestApp.java':

    package meinclient;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.rmi.PortableRemoteObject;
    import meintransactiontest.CmtSbHomeIF;
    import meintransactiontest.CmtSbIF;
    
    public class CmtTestApp
    {
      public static void main( String[] args ) throws Exception
      {
        Context ctx = new InitialContext();
        Object  ref = ctx.lookup( "CmtSb" );
        CmtSbHomeIF home = (CmtSbHomeIF) PortableRemoteObject.narrow(
                                               ref, CmtSbHomeIF.class );
        CmtSbIF cmtSb = home.create();
        cmtSb.insertTestEntry();
        System.out.println( cmtSb.showEntityValue( "Nach insertTestEntry()" ) );
        cmtSb.testRollback( true );
        System.out.println( cmtSb.showEntityValue( "Nach testRollback(true)" ) );
        cmtSb.testRollback( false );
        System.out.println( cmtSb.showEntityValue( "Nach testRollback(false)" ) );
      }
    }
    
  16. Verzeichnisstruktur

    Die Verzeichnisstruktur muss jetzt in etwa folgendermaßen aussehen:

    [\MeinWorkspace]
      '- [MeinEjbProjekt]
           |- [bin]
           |    '- ...
           |- [lib]
           |    '- jbossall-client.jar
           |- [src]
           |    |- [meinclient]
           |    |    '- CmtTestApp.java
           |    '- [meintransactiontest]
           |         |- CmpEbHomeIF.java
           |         |- CmpEbIF.java
           |         |- CmpEbImpl.java
           |         |- CmtSbHomeIF.java
           |         |- CmtSbIF.java
           |         |- CmtSbImpl.java
           |         |- ejb-jar.xml
           |         '- jbosscmp-jdbc.xml
           |- build.xml
           |- jndi.properties
           '- log4j.properties
    

    Wichtig: Im '<MeinEjbProjekt>'-Verzeichnis müssen sich die Dateien 'build.xml', 'jndi.properties' und 'log4j.properties' befinden, im '<MeinEjbProjekt>\lib'-Verzeichnis die 'jbossall-client.jar' und im '<JBOSS_HOME>\server\default\deploy'-Verzeichnis eine geeignete 'mysql-ds.xml'-Datei. .

  17. Aufruf

    Bei laufendem JBoss öffnen Sie ein Kommandozeilenfenster und geben die folgenden Kommandos ein:

    cd \MeinWorkspace\MeinEjbProjekt

    ant -Dclntapp=meinclient.CmtTestApp -Dsrvjar=CmtTest.jar -Dsrvpkg=meintransactiontest

  18. Eclipse

    Falls Sie das Projekt in Eclipse bearbeiten wollen, gehen Sie vor wie hier beschrieben.
    Falls Sie außerhalb von Eclipse Dateien verändert oder hinzugefügt haben, müssen Sie in Eclipse die entsprechenden Projekte im 'Package Explorer' markieren und mit 'F5' einen 'Refresh' durchführen.
    Außerdem müssen Sie für dieses Beispiel die build.xml-Properties anders einstellen:
    'clntapp=meinclient.CmtTestApp', 'srvjar=CmtTest.jar' und 'srvpkg=meintransactiontest'.

  19. Transaktionsattribut 'Required'

    Ersetzen Sie testweise das Transaktionsattribut 'Required' in der 'ejb-jar.xml' durch andere Werte ('NotSupported', 'Supports', 'Required', 'RequiresNew', 'Mandatory', 'Never'), zum Beispiel so:

    <trans-attribute> NotSupported </trans-attribute>

    statt

    <trans-attribute> Required </trans-attribute>

    und sehen Sie sich die Ergebnisse bzw. Fehlermeldungen an.

  20. JBoss Management Web Console

    Um einige Statistikinformationen zu erhalten, öffnen Sie die JBoss Management Web Console über http://localhost:8080/web-console. Doppelklicken Sie auf 'J2EE Domains' | 'Manager' | 'JBoss' | 'CmtTest.jar' und 'CmtSb' bzw. 'CmpEb'.



BMT (Bean Managed Transactions) über JTA (Java Transaction API)

Bei BMT (Bean Managed Transactions) (mit '<transaction-type>Bean</transaction-type>' in der 'ejb-jar.xml') werden Transaktionen von der Session-Bean über das JTA (Java Transaction API) gestartet und beendet.

Dies kann zum Beispiel folgendermaßen aussehen:

import javax.ejb.*;
import javax.transaction.*;

public class MyTransactionEjb implements SessionBean
{
  private SessionContext ctx = null;

  public void setSessionContext( SessionContext sessionContext )
  {
    ctx = sessionContext;
  }

  public void doSomething() throws SystemException
  {
    UserTransaction utx = ctx.getUserTransaction();
    int initialTxStat = utx.getStatus();
    beginnTransactionIfRequired( utx, initialTxStat );
    try {
      // ...
      // ... call EJBs, JMS, JDBC, XA-compliant-JCA, ...
      // ...
    } catch( Exception ex ) {
      utx.setRollbackOnly();
    }
    completeTransactionIfRequired( utx, initialTxStat );
  }

  void beginnTransactionIfRequired( UserTransaction utx, int initialTxStat )
  {
    switch( initialTxStat ) {
      case Status.STATUS_ACTIVE:
        break;
      case Status.STATUS_NO_TRANSACTION:
        try {
          utx.begin();
        } catch( Exception ex ) {
          throw new EJBException( "Error at Transaction Beginn." );
        }
        break;
      default:
        throw new EJBException( "Unexpected Transaction Status." );
    }
  }

  void completeTransactionIfRequired( UserTransaction utx, int initialTxStat )
  {
    if( Status.STATUS_NO_TRANSACTION == initialTxStat ) {
      try {
        if( Status.STATUS_MARKED_ROLLBACK == utx.getStatus() )
          utx.rollback();
        else
          utx.commit();
      } catch( Exception ex ) {
        throw new EJBException( "Error at Transaction Completion." );
      }
    }
  }

  public void ejbCreate() {}
  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void ejbRemove() {}
}


Client-gesteuerte Transaktion mit JTA

Programmgerüst für Transaktions-Client

Ein Programmgerüst für eine von einem Client außerhalb des EJB-Containers gesteuerte JTA-Transaktion könnte zum Beispiel so aussehen:

import javax.naming.*;
import javax.transaction.*;

public class MyTransactionClient1
{
  public static void main( String[] args ) throws Exception
  {
    Context ctx = new InitialContext();
    // Je nach Application Server:
    //   "java:comp/UserTransaction" bzw.
    //   "java:comp/TransactionManager" bzw.
    //   "java:/TransactionManager" bzw.
    //   "javax.transaction.UserTransaction":
    UserTransaction utx = (UserTransaction) ctx.lookup( "/UserTransaction" );
    utx.begin();
    try {
      // ...
      // ... call EJBs, JMS, JDBC, XA-compliant-JCA, ...
      // ...
      if( Status.STATUS_MARKED_ROLLBACK != utx.getStatus() )
        utx.commit();
      else
        utx.rollback();
    } catch( Exception ex ) {
      utx.rollback();
      throw ex;
    }
  }
}

Beispiel für einen Client, der von extern das JTA/JTS und eine DataSource eines Java EE Application Servers verwendet

Die Verwendung von DataSourcen unter JTA könnte mit Oracle WebLogic zum Beispiel so aussehen (mit der Lib: weblogic.jar) (passen Sie 'meinAppServer...' und 'MeineTabelle' an) (zu JNDI-Properties siehe auch: jee-jndi.htm#jndi.properties):

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.Status;
import javax.transaction.UserTransaction;

public class MyTransactionClient2
{
   public static void main( String[] args ) throws Exception
   {
      Properties prp = new Properties();
      prp.put( Context.PROVIDER_URL,            "t3://meinAppServer:7001" );
      prp.put( Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory" );
      prp.put( Context.SECURITY_PRINCIPAL,      "meinAppSrvBenutzername" );
      prp.put( Context.SECURITY_CREDENTIALS,    "meinAppSrvKennwort" );
      InitialContext  ctx = new InitialContext( prp );
      UserTransaction utx = (UserTransaction) ctx.lookup( "javax.transaction.UserTransaction" );
      DataSource      ds  = (DataSource)      ctx.lookup( "jdbc/MeinDatasourceJndiName" );
      Connection cn = null;
      Statement  st = null;
      ResultSet  rs = null;
      utx.begin();
      try {
        cn = ds.getConnection();
        st = cn.createStatement();
        rs = st.executeQuery( "Select * from MeineTabelle" );
        if( rs.next() ) System.out.println( "Ergebnis: " + rs.getString( 1 ) );
        System.out.println( cn.getMetaData().getDatabaseProductName() );
        System.out.println( cn.getMetaData().getDatabaseProductVersion() );
        if( Status.STATUS_MARKED_ROLLBACK != utx.getStatus() ) {
          utx.commit();
        } else {
          utx.rollback();
        }
      } catch( Exception ex ) {
        utx.rollback();
        throw ex;
      } finally {
        try { if( null != rs ) rs.close(); } catch( Exception ex ) {/*ok*/}
        try { if( null != st ) st.close(); } catch( Exception ex ) {/*ok*/}
        try { if( null != cn ) cn.close(); } catch( Exception ex ) {/*ok*/}
      }
   }
}

Trace der XA-Two-Phase-Commit-Kommunikation

Falls Sie Probleme im Zusammenhang mit XA-Two-Phase-Commit analysieren wollen: Aktivieren Sie Trace-Ausgaben sowohl des JTA/JTS als auch des Ressourcen-Treibers (z.B. XA-DataSource im JDBC-Treiber).

Zum Beispiel für Oracle WebLogic schalten Sie Tracing für JTA/JTS ein, indem Sie dem Startkommando folgende Kommandozeilenoption hinzufügen (z.B. in startWeblogic.cmd):

-Dweblogic.Debug=weblogic.JDBCConn,weblogic.JDBCSQL,weblogic.JTA2PC,weblogic.JTAXA,weblogic.JTAJDBC

Zum Beispiel für den AS/400-DB2-JDBC-Treiber jt400-5.4.0.2.jar schalten Sie Tracing ein, indem Sie "Trace=true" und einen geeigneten "server trace"-Level setzen (wie unter JDBC server trace property beschrieben ist).

Die Tracing-Ausgaben können entweder auf der Application-Server-Konsole oder in der Application-Server-Log-Datei erscheinen.

Die XA Specification der Open Group finden Sie unter http://www.opengroup.org/bookstore/catalog/c193.htm.



JDBC-Treiber mit XA-Two-Phase-Commit ohne JTA/JTS ansteuern

Für Diagnose- oder Testzwecke kann es sinnvoll sein, das XA-Two-Phase-Commit-Protokoll über direkte Kommandos zum Ressourcenmanager statt über den JTA/JTS-Transaktionsmanager anzusteuern. Hierzu könnte ein Programmgerüst folgendermaßen aussehen:

import java.sql.Connection;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import com.ibm.as400.access.AS400JDBCXADataSource; // jt400-5.4.0.2.jar fuer DB2/400 auf AS/400 V5R4

public class JdbcXaDataSourceTest
{
  public static void main( String[] args ) throws Exception
  {
    String url = "meinDbServer", usr = "meinDbBenutzername", pwd = "meinDbKennwort";
    // Je nach Datenbank andere XADataSource einsetzen
    // (hier fuer DB2/400 auf AS/400 V5R4):
    AS400JDBCXADataSource xaDataSource = new AS400JDBCXADataSource( url );
    xaDataSource.setUser( usr );
    xaDataSource.setPassword( pwd );
    XAConnection xaConnection = xaDataSource.getXAConnection();
    XAResource xaResource = xaConnection.getXAResource();
    Connection connection = xaConnection.getConnection();
    Xid xid = new XidImpl( 100, new byte[]{0x0A}, new byte[]{0x03} );
    xaResource.start( xid, XAResource.TMNOFLAGS );
    System.out.println( connection.getMetaData().getDatabaseProductName() );
    System.out.println( connection.getMetaData().getDatabaseProductVersion() );
    // connection.createStatement().executeQuery( "..." );
    xaResource.end( xid, XAResource.TMSUCCESS );
    if( xaResource.prepare( xid ) == XAResource.XA_OK )
      xaResource.commit( xid, false );
    xaConnection.close();
  }
}

// Entweder folgende XidImpl oder alternativ auch XidImpl vom Application Server, z.B.:
//   weblogic.transaction.internal.XidImpl
//   org.jboss.tm.XidImpl
class XidImpl implements Xid
{
  protected int    formatId;
  protected byte[] gtrid;
  protected byte[] bqual;

  public XidImpl( int formatId, byte gtrid[], byte bqual[] )
  {
    this.formatId = formatId;
    this.gtrid    = gtrid;
    this.bqual    = bqual;
  }

  public int    getFormatId()            { return formatId; }
  public byte[] getGlobalTransactionId() { return gtrid; }
  public byte[] getBranchQualifier()     { return bqual; }
}

Die XA Specification der Open Group finden Sie unter http://www.opengroup.org/bookstore/catalog/c193.htm.

Die Javadoc zu den Java-Interfaces finden Sie unter XADataSource, XAConnection, XAResource und Xid.

Die Javadoc zum im Beispiel verwendeten XA-Ressourcenmanager zur DB2/400 auf AS/400 V5R4 finden Sie unter http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=/rzahh/javadoc/com/ibm/as400/access/AS400JDBCXAResource.html.



Transaktionen mit JPA

Siehe:
JPA mit Java SE und "EntityTransaction",
JPA-Servlet-Filter mit "EntityTransaction",
JPA-Servlet-Filter mit "JTA Transaction",
JPA mit EJB 3 und "JTA Transaction",
JPA in JSP-Webanwendung mit geteilter Transaktion,
Optimistic Locking bei geteilter Transaktion (z.B. in Webanwendung).



Transaktionen mit Hibernate

... siehe unter java-hibernate.htm#Transaktionen.



Transaktionen mit Spring

... siehe unter jee-spring-db-tx.htm.



Links auf weiterführende Informationen





Weitere Themen: andere TechDocs | EJB | SQL | Hibernate
© 1998-2007 Torsten Horn, Aachen