Erweiterungen zu APIs für Datenzugriff
Wenn eine einzige API für Datenzugriff keine vollständige Lösung für Ihre Anwendungen darstellt, verwenden Sie Erweiterungen für WebSphere Application Server, um eine Interoperabilität von JCA- und JDBC-APIs zu erreichen.
Für Anwendungen, die unterschiedliche und komplexe Konfigurationen für die Ressourcenverwaltung verwenden, müssen sowohl die JCA-API (Java™ EE Connector Architecture) als auch die JDBC-API (Java Database Connectivity) verwendet werden. In einigen Fällen wird das JDBC-Programmiermodell jedoch nicht vollständig in die JCA integriert (auch wenn die vollständige Integration eine der Grundforderungen der JCA-Spezifikation ist). Solche Inkonsistenzen können die Datenzugriffsoptionen für eine Anwendung, die beide APIs verwendet, einschränken. WebSphere Application Server stellt API-Erweiterungen bereit, mit denen Sie die Kompatibilitätsprobleme lösen können.
Beispiel:
Ohne den Vorteil einer Erweiterung können Anwendungen, die beide APIs verwenden, nach einer Verbindungsanforderung nicht die Eigenschaften einer gemeinsam nutzbaren Verbindung ändern, sofern weitere Handles für diese Verbindung existieren. (Sind der Verbindung keine weiteren Handles zugeordnet, können die Verbindungseigenschaften geändert werden.) Diese Einschränkung ist auf die eine Inkompatibilität der APIs hinsichtlich der Richtlinien für Verbindungskonfiguration zurückzuführen.
Die JCA-Spezifikation (Java EE Connector Architecture) unterstützt die Übermittlung spezifischer Eigenschaftseinstellungen an den Ressourcenadapter zu dem Zeitpunkt, zu dem Sie die Verbindung (mit der Methode getConnection() anfordern, durch Übergabe eines ConnectionSpec-Objekts. Das ConnectionSpec-Objekt enthält die notwendigen Verbindungseigenschaften, die zum Abrufen einer Verbindung verwendet werden. Wenn Sie eine Verbindung aus dieser Umgebung abrufen, ist es nicht erforderlich, dass Ihre Anwendung die Eigenschaften ändert. Das JDBC-Programmiermodell hat jedoch nicht dieselbe Schnittstelle für die Angabe der Verbindungseigenschaften. Stattdessen ruft es zunächst die Verbindung ab und definiert dann die Eigenschaften für die Verbindung.
WebSphere Application Server stellt die folgenden Erweiterungen bereit, um die Lücken zwischen der JDBC- und der JCA-Spezifikation zu füllen:
- Schnittstelle WSDataSource: Diese Schnittstelle erweitert die Klasse "javax.sql.DataSource" und gibt einer Komponente
oder Anwendung die Möglichkeit, die Verbindungseigenschaften mit der Klasse "JDBCConnectionSpec" von
WebSphere Application Server anzugeben,
um eine Verbindung abzurufen.
- getConnection(JDBCConnectionSpec): Diese Methode gibt eine Verbindung mit den in der Klasse "JDBCConnectionSpec" angegebenen Eigenschaften zurück.
- Weitere Informationen hierzu finden Sie im Artikel WSDataSource der API-Dokumentation. (Sie finden diesen Artikel am schnellsten im Index der API-Dokumentation.)
- Schnittstelle "JDBCConnectionSpec": Diese Schnittstelle erweitert die Klasse com.ibm.websphere.rsadapter.WSConnectionSpec, die wiederum die Klasse "javax.resources.cci.ConnectionSpec" erweitert. Die Standardschnittstelle "ConnectionSpec" stellt nur die Schnittstellenmarkierung ohne Getter- und Setter-Methoden bereit. Die Schnittstellen "WSConnectionSpec" und "JDBCConnectionSpec" definieren eine Gruppe von get()- und set()-Methoden, die von der Laufzeit von WebSphere Application Server verwendet werden. Diese Schnittstelle ermöglicht der Anwendung, alle wesentliche Verbindungseigenschaften zum Abrufen einer entsprechenden Verbindung anzugeben. Sie können diese Klasse aus der WebSphere-Klasse WSRRAFactory erstellen. Weitere Informationen hierzu finden Sie im Artikel JDBCConnection der API-Dokumentation. (Sie finden diesen Artikel am schnellsten im Index der API-Dokumentation.)
- Klasse WSRRAFactory: Dies ist die Factory-Klasse des relationalen WebSphere-Ressourcenadapters, die dem Benutzer ermöglicht, ein JDBCConnectionSpec-Objekt oder ein anderes zum Ressourcenadapter gehöriges Objekt zu erstellen. Weitere Informationen hierzu finden Sie im Artikel WSRRAFactory der API-Dokumentation. (Sie finden diesen Artikel am schnellsten im Index der API-Dokumentation.)
- Schnittstelle WSConnection: Mit dieser Schnittstelle können Benutzer die folgenden
WebSphere-spezifischen
Methoden für SQL-Verbindungen aufrufen:
- setClientInformation(Properties props) - Weitere Hinweise und Beispiele zum Konfigurieren von Clientinformationen finden Sie im Artikel "Beispiel: Clientinformationen mit der API 'setClientInformation(Properties)' festlegen".
- Properties getClientInformation(): Diese Methode gibt das mit setClientInformation(Properties) gesetzte Eigenschaftenobjekt zurück. Das zurückgegebene Eigenschaftenobjekt wird nicht von impliziten Einstellungen in den Clientinformationen beeinflusst.
- WSSystemMonitor getSystemMonitor(): Diese Methode gibt das
SystemMonitor-Objekt von der Verbindung zur Back-End-Datenbank zurück, sofern die Datenbank
die Systemüberwachung unterstützt. Die Back-End-Datenbank stellt im
SystemMonitor-Objekt einige Verbindungsstatistiken bereit. Das zurückgegebene SystemMonitor-Objekt wird
in ein WebSphere-Objekt
(com.ibm.websphere.rsadapter.WSSystemMonitor) eingebettet, um die
Anwendungen vor einer Abhängigkeit von Code des Datenbanklieferanten zu bewahren. Weitere Informationen
hierzu finden Sie in der Java-Dokumentation
unter com.ibm.websphere.rsadapter.WSSystemMonitor. Der folgende Beispielcode
demonstriert die Verwendung der Klasse
WSSystemMonitor:
import com.ibm.websphere.rsadapter.WSConnection; ... try{ InitialContext ctx=new InitialContext(); // Namensservice-Lookup zum Abrufen des DataSource-Objekts durchführen. DataSource ds=(javax.sql.DataSource]ctx.lookup("java:comp/jdbc/myDS"); } catch (Exception e) {;} WSConnection conn=(WSConnection)ds.getConnection(); WSSystemMonitor sysMon=conn.getSystemMonitor(); if (sysMon!=null) // zeigt an, dass die aktuelle Back-End-Datenbank die Systemüberwachung unterstützt { sysMon.enable(true); sysMon.start(WSSystemMonitor.RESET_TIMES); // Interaktion mit der Datenbank sysMon.stop(); // Erfassen von Daten aus dem sysMon-Objekt } conn.close();
Die Schnittstelle "WSConnection" ist Teil der Datei Stammverzeichnis_für_Plug-ins/com.ibm.ws.runtime.jar.
Beispiel: Erweiterte IBM APIs für Datenbankverbindungen verwenden.
Mit Hilfe der erweiterten API "WSDataSource" können Sie Ihre JDBC-Anwendung so codieren, dass sie über ein Objekt Verbindungseigenschaften festlegt, bevor eine Verbindung abgerufen wird. Dieses Verhalten erhöht die Chancen dafür, dass die Anwendung eine Verbindung mit einer anderen Komponente, z. B. einer CMP, gemeinsam nutzen kann.
Wenn die Anwendung mit einer gemeinsam nutzbaren Verbindung, die möglicherweise mit anderen CMP-Beans (Container-Managed Persistence) in einer Transaktion verwendet wird, ausgeführt wird, empfiehlt es sich, die Verbindung mit den erweiterten APIs von WebSphere Application Server abzurufen. Wenn Sie diese APIs verwenden, können Sie die Anwendung nicht auf andere Anwendungsserver übertragen.
Sie können die erweiterte API direkt in Ihrer JDBC-Anwendung codieren. Verwenden Sie zum Abrufen einer Verbindung anstelle der Schnittstelle "DataSource" die Schnittstelle "WSDataSource". Das folgende Codesegment veranschaulicht WSDataSource:
import com.ibm.websphere.rsadapter.*;
...
// Eine JDBCConnectionSpec erstellen und Verbindungseigenschaften definieren. Wenn diese
// Verbindung mit der CMP-Bean gemeinsam genutzt wird, vergewissern Sie sich, dass die
// Isolationsstufe mit der Isolationsstufe, die von der für die CMP-Bean definierten
// Zugriffsart zugeordnet wird, übereinstimmt.
JDBCConnectionSpec connSpec = WSRRAFactory.createJDBCConnectionSpec();
connSpec.setTransactionIsolation(CONNECTION.TRANSACTION_REPEATABLE_READ);
connSpec.setCatalog("DEPT407");
// WSDataSource zum Abrufen der Verbindung verwenden
Connection conn = ((WSDataSource)datasource).getConnection(connSpec);
Beispiel: Erweiterte IBM APIs zur gemeinsamen Nutzung von Verbindungen zwischen CMP- und BMP-Beans verwenden.
In einer Anwendungskomponente, die über JDBC-Objekte auf Daten zugreift (z. B. eine BMP-Bean), können Sie eine erweiterte WebSphere-API verwenden, um Verbindungseigenschaften über ein Objekt zu definieren, bevor eine Verbindung abgerufen wird. Dieses Verhalten erhöht die Chancen dafür, dass die BMP-Bean (Bean mit über Java-Beans realisierter Transaktionspersistenz) eine Verbindung mit einer CMP-Bean (Bean mit über Container realisierter Transaktionspersistenz) gemeinsam nutzen kann.
Wenn die BMP-Bean mit einer gemeinsam nutzbaren Verbindung ausgeführt wird, die möglicherweise mit anderen CMP-Beans (Container-Managed Persistence) in einer Transaktion gemeinsam genutzt wird, dann empfiehlt es sich, die Verbindung mit den erweiterten APIs von WebSphere Application Server abzurufen. Wenn Sie diese APIs verwenden, können Sie die Anwendung nicht auf andere Anwendungsserver übertragen.
Verwenden Sie für diesen Fall anstelle der Schnittstelle DataSource die erweiterte API WSDataSource. Wenn Sie sicherstellen möchten, dass CMP- und BMP-Beans dieselbe physische Verbindung nutzen, definieren Sie in beiden Bean-Typen dasselbe Profil für Zugriffsarten. In der BMP-Methode können Sie die richtige Isolationsstufe über die Helper-Klasse des relationalen Ressourcenadapters abrufen.
package fvt.example;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.ejb.CreateException;
import javax.ejb.DuplicateKeyException;
import javax.ejb.EJBException;
import javax.ejb.ObjectNotFoundException;
import javax.sql.DataSource;
// Die folgenden Importe werden von der erweiterten IBM API verwendet
import com.ibm.websphere.appprofile.accessintent.AccessIntent;
import com.ibm.websphere.appprofile.accessintent.AccessIntentService;
import com.ibm.websphere.rsadapter.JDBCConnectionSpec;
import com.ibm.websphere.rsadapter.WSCallHelper;
import com.ibm.websphere.rsadapter.WSDataSource;
import com.ibm.websphere.rsadapter.WSRRAFactory;
/**
* Bean-Implementierungsklasse für Enterprise-Bean: Simple
*/
public class SimpleBean implements javax.ejb.EntityBean {
private javax.ejb.EntityContext myEntityCtx;
// Ausgangskontext für Lookup.
private javax.naming.InitialContext ic = null;
// Eine JDBCConnectionSpec als Instanzvariable definieren
private JDBCConnectionSpec connSpec;
// Einen AccessIntentService, der zum Abrufen
// eines AccessIntent-Objekts verwendet wird, definieren.
private AccessIntentService aiService;
// AccessIntent-Objekt zum Abrufen der Isolationsstufe
private AccessIntent intent = null;
// Tabellenname für Persistenz
private String tableName = "cmtest";
// JNDI-Name von DataSource
private String dsName = "java:comp/env/jdbc/SimpleDS";
// DataSource
private DataSource ds = null;
// Bean-Instanzvariablen.
private int id;
private String name;
/**
* Das AccessIntentService-Objekt muss in der setEntityContext-Methode
* abgerufen werden, damit nachfolgende Methoden das AccessIntent-Objekt
* abrufen können.
* Andere ejb-Methoden rufen die private Methode getConnection() auf, um die
* Verbindung mit allen spezifischen Verbindungseigenschaften abzurufen.
*/
public void setEntityContext(javax.ejb.EntityContext ctx) {
myEntityCtx = ctx;
try {
aiService =
(AccessIntentService) getInitialContext().lookup(
"java:comp/websphere/AppProfile/AccessIntentService");
ds = (DataSource) getInitialContext().lookup(dsName);
}
catch (javax.naming.NamingException ne) {
throw new javax.ejb.EJBException(
"Naming exception: " + ne.getMessage());
}
}
/**
* ejbCreate
*/
public fvt.example.SimpleKey ejbCreate(int newID)
throws javax.ejb.CreateException, javax.ejb.EJBException {
Connection conn = null;
PreparedStatement ps = null;
// SQL-Zeichenfolge einfügen
String sql = "INSERT INTO" + tableName + "(id, name) VALUES (?, ?)";
id = newID;
name = "";
try {
// Die allgemeine Methode zum Abrufen der spezifischen Verbindung aufrufen
conn = getConnection();
}
catch (java.sql.SQLException sqle) {
throw new EJBException("SQLException caught: " + sqle.getMessage());
}
catch (javax.resource.ResourceException re) {
throw new EJBException(
"ResourceException caught: " + re.getMessage());
}
try {
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
ps.setString(2, name);
if (ps.executeUpdate() != 1) {
throw new CreateException("Failed to add a row to the DB");
}
}
catch (DuplicateKeyException dke) {
throw new javax.ejb.DuplicateKeyException(
id + "has already existed");
}
catch (SQLException sqle) {
throw new javax.ejb.CreateException(sqle.getMessage());
}
catch (CreateException ce) {
throw ce;
}
finally {
if (ps != null) {
try {
ps.close();
}
catch (Exception e) {
}
}
}
return new SimpleKey(id);
}
/**
* ejbLoad
*/
public void ejbLoad() throws javax.ejb.EJBException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String loadSQL = null;
try {
// Die allgemeine Methode zum Abrufen der spezifischen Verbindung aufrufen
conn = getConnection();
}
catch (java.sql.SQLException sqle) {
throw new EJBException("SQLException caught: " + sqle.getMessage());
}
catch (javax.resource.ResourceException re) {
throw new EJBException(
"ResourceException caught: " + re.getMessage());
}
// Die zu verwendende SELECT-Anweisung muss basierend auf dem
// AccessIntent-Typ festgelegt werden:
// Für READ eine normale SELECT-Anweisung verwenden. Andernfalls die
// Anweisung SELECT...FORUPDATE verwenden.
// Wenn das Back-End-System SQLServer ist, kann für die FOR-UPDATE-Klausel
// eine andere Syntax verwendet werden.
if ( intent.getAccessType() == AccessIntent.ACCESS _TYPE_READ ){
loadSQL = "SELECT * FROM" + tableName + "WHERE id = ?";
}
else {
loadSQL = "SELECT * FROM" + tableName + "WHERE id = ? FOR UPDATE";
}
SimpleKey key = (SimpleKey) getEntityContext().getPrimaryKey();
try {
ps = conn.prepareStatement(loadSQL);
ps.setInt(1, key.id);
rs = ps.executeQuery();
if (rs.next()) {
id = rs.getInt(1);
name = rs.getString(2);
}
else {
throw new EJBException("Cannot load id = " + key.id);
}
}
catch (SQLException sqle) {
throw new EJBException(sqle.getMessage());
}
finally {
try {
if (rs != null)
rs.close();
}
catch (Exception e) {
}
try {
if (ps != null)
ps.close();
}
catch (Exception e) {
}
try {
if (conn != null)
conn.close(); }
catch (Exception e) {
}
}
}
/**
* Diese Methode verwendet den AccessIntentService zum Abrufen der
* Zugriffsart, ruft dann die Isolationsstufe vom DataStoreHelper
* ab, legt sie in der Verbindungsspezifikation fest, verwendet diese
* Verbindungsspezifikation, um eine Verbindung mit den spezifischen
* Verbindungseigenschaften abzurufen.
**/
private Connection getConnection()
throws java.sql.SQLException, javax.resource.ResourceException, EJBException {
// Aktuelles Objekt für Zugriffsart mit dem EJB-Kontext abrufen
intent = aiService.getAccessIntent(myEntityCtx);
// Diese Bean unterstützt nur Pessimistic Concurrency
if (intent.getConcurrencyControl()
!= AccessIntent.CONCURRENCY_CONTROL_PESSIMISTIC) {
throw new EJBException("Bean supports only pessimistic concurrency");
}
// Richtige Isolationsstufe für gegenwärtig konfigurierte Datenbank
// mit DataStoreHelper festlegen
int isoLevel =
WSCallHelper.getDataStoreHelper(ds).getIsolationLevel(intent);
connSpec = WSRRAFactory.createJDBCConnectionSpec();
connSpec.setTransactionIsolation(isoLevel);
// Verbindung mit Verbindungsspezifikation abrufen
Connection conn = ((WSDataSource) ds).getConnection(connSpec);
return conn;
}