Extensiones a las API de acceso a datos
Si una API de acceso a datos individual no proporciona una solución completa para las aplicaciones, utilice las extesiones de WebSphere Application Server para obtener la interoperatividad entre las API de JCA y JDBC.
Es posible que las aplicaciones que proceden de diferentes configuraciones de gestión de recursos complejas requieran utilizar tanto la API JCA (Java™ Platform, Enterprise Edition (Java EE Connector Architecture) como la API JDBC (Java Database Connectivity). No obstante, en algunos casos el modelo de programación JDBC no se integra completamente con JCA (aún cuando la integración completa sea una base de la especificación de JCA). Estas incoherencias pueden limitar las opciones de acceso a datos para las aplicaciones que utilizan las dos API. WebSphere Application Server proporciona extensiones de la API para solucionar los problemas de compatibilidad.
Por ejemplo:
Sin las ventajas de la extensión, las aplicaciones que utilizan las dos API no pueden modificar las propiedades de conexiones que se pueden compartir después de realizar la solicitud de conexión, si existen otros manejadores para esa conexión. (Si no hay ningún otro manejador asociado a la conexión, entonces se pueden alterar las propiedades de la conexión. Esta limitación proviene de una incompatibilidad entre las políticas de configuración de conexión de las API:
La especificación JCA (Connector Architecture) admite confiar al adaptador de recursos los valores de propiedades específicas en el momento en que se solicita la conexión (utilizando el método getConnection()) pasando un objeto ConnectionSpec. El objeto ConnectionSpec contiene las propiedades de conexión necesarias que se utilizan para obtener una conexión. Después de obtener una conexión desde este entorno, la aplicación no necesita alterar las propiedades. El modelo de programación JDBC, no obstante, no tiene la misma interfaz para especificar las propiedades de la conexión. Obtiene primero la conexión, y luego establece las propiedades en ella.
WebSphere Application Server proporciona las siguientes ampliaciones para completar los huecos entre las especificaciones JDBC y JCA:
- Interfaz WSDataSource: esta interfaz amplía la clase javax.sql.DataSource y permite
a un componente o una aplicación especificar las propiedades de la conexión a través de
la clase JDBCConnectionSpec de WebSphere Application Server para obtener una conexión.
- getConnection(JDBCConnectionSpec) - este método devuelve una conexión con las propiedades especificadas en la clase JDBCConnectionSpec.
- Para obtener más información, consulte el tema WSDataSource de la documentación de la API (como se enumera en el índice de documentación de la API).
- Interfaz JDBCConnectionSpec: esta interfaz amplía la clase com.ibm.websphere.rsadapter.WSConnectionSpec, que a su vez amplía la clase javax.resources.cci.ConnectionSpec. La interfaz ConnectionSpec estándar proporciona sólo el marcador de interfaz, sin los métodos get() y set(). Las interfaces WSConnectionSpec y JDBCConnectionSpec definen un conjunto de métodos get() y set() utilizados por el programa de ejecución de WebSphere Application Server. Esta interfaz permite a la aplicación especificar todas las propiedades básicas para obtener una conexión correcta. Puede crear esta clase a partir de la clase WSRRAFactory de WebSphere. Para obtener más información consulte el tema JDBCConnection de la documentación de la API (como se enumera en el índice de documentación de la API).
- Clase WSRRAFactory: esta es la clase de fábrica del adaptador de recursos relacional de WebSphere que permite al usuario crear un objeto JDBCConnectionSpec u otro objeto relacionado con el adaptador de recursos. Para obtener más información, consulte el tema WSRRAFactory de documentación de la API (como se enumera en el índice de documentación de la API).
- Interfaz WSConnection - permite a los usuarios llamar a métodos propietarios de
WebSphere en conexiones SQL; esos métodos son:
- setClientInformation(Properties props) - Consulte el tema Ejemplo: Definición de la información de cliente con la API setClientInformation(Properties), para obtener más información y ejemplos para definir la información de cliente.
- Properties getClientInformation() - este método devuelve el objeto Properties que se ha establecido con setClientInformation(Properties). Recuerde que el objeto Properties devuelto no se ve afectado por los valores implícitos de la información del cliente.
- WSSystemMonitor getSystemMonitor() - este método devuelve
el objeto SystemMonitor de la conexión de base de datos del programa de
fondo si la base de datos admite supervisores de sistemas. La base de datos de programa de fondo proporcionará algunas estadísticas de conexión
en el objeto SystemMonitor. El objeto SystemMonitor devuelto se envuelve en un
objeto de WebSphere (com.ibm.websphere.rsadapter.WSSystemMonitor)
para proteger a las aplicaciones de la dependencia de código de cualquier distribuidor de base de datos.
Consulte la documentación de com.ibm.websphere.rsadapter.WSSystemMonitor Java para obtener más información. El código siguiente es un ejemplo de uso de la clase
WSSystemMonitor:
import com.ibm.websphere.rsadapter.WSConnection; ... try{ InitialContext ctx=new InitialContext(); // Realizar búsqueda de servicio de denominación para obtener objeto DataSource. 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) // indica que se admite la supervisión de sistemas en la base de datos del programa de fondo actual { sysMon.enable(true); sysMon.start(WSSystemMonitor.RESET_TIMES); // actuar conjuntamente con la base de datos sysMon.stop(); // recopilar datos del objeto sysMon } conn.close();
La interfaz WSConnection forma parte del archivo raíz_plugins/com.ibm.ws.runtime.jar.
Ejemplo: Utilización de las API ampliadas de IBM para las conexiones de base de datos.
Utilizando la API ampliada de WSDataSource, puede codificar la aplicación JDBC para definir las propiedades de conexión mediante un objeto antes de obtener una conexión. Este comportamiento aumenta las posibilidades de que la aplicación pueda compartir una conexión con otro componente como, por ejemplo, un CMP.
Si la aplicación se ejecuta con una conexión que se puede compartir con otros beans CMP (persistencia gestionada por contenedor) dentro de una transacción, se recomienda utilizar las API aplicadas de WebSphere Application Server para obtener la conexión. Cuando se utilizan estas API, la aplicación no se puede transportar a otros servidores de aplicaciones.
Puede codificar con la API ampliada directamente en las aplicaciones JDBC. En lugar de utilizar la interfaz DataSource para obtener una conexión, utilice la interfaz WSDataSource. El siguiente segmento de código ilustra WSDataSource:
import com.ibm.websphere.rsadapter.*;
...
// Crear un JDBCConnectionSpec y establecer las propiedades de la conexión. Si esta conexión
// se comparte con el bean CMP, asegúrese de que el nivel de aislamiento es el mismo que
// el nivel de aislamiento
que se correlaciona mediante el Intento de acceso definido en el bean CMP.
JDBCConnectionSpec connSpec = WSRRAFactory.createJDBCConnectionSpec();
connSpec.setTransactionIsolation(CONNECTION.TRANSACTION_REPEATABLE_READ);
connSpec.setCatalog("DEPT407");
// Utilice WSDataSource para obtener la conexión
Connection conn = ((WSDataSource)datasource).getConnection(connSpec);
Ejemplo: Utilización de las API ampliadas de IBM para compartir conexiones entre beans CMP y beans BMP.
En un componente de aplicación que accede a datos a través de objetos JDBC como, por ejemplo, un bean BMP (persistencia gestionada por bean), puede utilizar una API ampliada de WebSphere para definir las propiedades de la conexión mediante un objeto antes de obtener una conexión. Este comportamiento aumenta las posibilidades de que el bean BMP comparta una conexión con un bean CMP (persistencia gestionada por contenedor).
Si el bean BMP se ejecuta con una conexión que se puede compartir con otros beans CMP (persistencia gestionada por contenedor) dentro de una transacción, se recomienda utilizar las API ampliadas de WebSphere Application Server para obtener la conexión. Cuando se utilizan estas API, la aplicación no se puede transportar a otros servidores de aplicaciones.
En este caso, utilice la interfaz WSDataSource de la API, en lugar de la interfaz DataSource. Para asegurarse de que los beans CMP y BMP (persistencia gestionada por bean) comparten la misma conexión física, defina el mismo perfil de intento de acceso en los beans CMP y BMP. Dentro del método BMP, puede obtener el nivel de aislamiento correcto de la clase de ayudante del adaptador de recursos relacional.
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;
// La API ampliada de IBM utiliza las siguientes importaciones
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;
/**
* Clase de implementación de bean para enterprise bean: Sencilla
*/
public class SimpleBean implements javax.ejb.EntityBean {
private javax.ejb.EntityContext myEntityCtx;
// Contexto inicial utilizado para la búsqueda.
private javax.naming.InitialContext ic = null;
// definir una JDBCConnectionSpec como variable de instancia
private JDBCConnectionSpec connSpec;
// definir un AccessIntentService que se utiliza para obtener
// un objeto AccessIntent.
private AccessIntentService aiService;
// Objeto AccessIntent utilizado para obtener el nivel de aislamiento
private AccessIntent intent = null;
// Nombre de tabla de persistencia
private String tableName = "cmtest";
// Nombre JNDI de DataSource
private String dsName = "java:comp/env/jdbc/SimpleDS";
// DataSource
private DataSource ds = null;
// variables de instancia de bean.
private int id;
private String name;
/**
* En el método setEntityContext, debe obtener el objeto AccessIntentService
* para que los siguientes métodos obtengan el objeto
* AccessIntent.
* Los otros métodos ejb llamarán al método privado getConnection() para obtener la
* conexión que tiene todas las propiedades de conexión específicas
*/
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;
// Insertar serie SQL
String sql = "INSERT INTO" + tableName + "(id, name) VALUES (?, ?)";
id = newID;
name = "";
try {
// llamar al método común para obtener la conexión específica
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 {
// llamar al método común para obtener la conexión específica
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());
}
// Debe determinar qué sentencia select se va a utilizar según el
// tipo de AccessIntent:
// Si es READ, utiliza una sentencia SELECT normal. De lo contrario, utiliza
// una sentencia SELECT...FORUPDATE
// Si el programa de fondo es SQLServer, puede utilizar una sintaxis diferente
// para la cláusula FOR UPDATE.
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){
}
}
}
/**
* Este método utilizará AccessIntentService para obtener el intento de acceso;
* después obtiene el nivel de aislamiento desde el DataStoreHelper
* y lo establece en la especificación de conexión; después utiliza esta
* especificación de conexión para obtener una conexión que tenga las propiedades
* las propiedades personalizadas.
**/
private Connection getConnection()
throws java.sql.SQLException, javax.resource.ResourceException, EJBException {
// obtener objeto de intento de acceso actual utilizando el contexto de EJB
intent = aiService.getAccessIntent(myEntityCtx);
// Supongamos que este bean sólo soporta la simultaneidad pesimista
if (intent.getConcurrencyControl()
!= AccessIntent.CONCURRENCY_CONTROL_PESSIMISTIC) {
throw new EJBException("Bean supports only pessimistic concurrency");
}
// determinar el nivel de aislamiento correcto de la base de datos configurada
// utilizando DataStoreHelper
int isoLevel =
WSCallHelper.getDataStoreHelper(ds).getIsolationLevel(intent);
connSpec = WSRRAFactory.createJDBCConnectionSpec();
connSpec.setTransactionIsolation(isoLevel);
// Obtener la conexión utilizando la especificación de conexión
Connection conn = ((WSDataSource) ds).getConnection(connSpec);
return conn;
}