데이터 액세스 API에 대한 확장기능
단일 데이터 액세스 API가 사용자 애플리케이션의 전체 솔루션을 제공하지 않는 경우, WebSphere® Application Server 확장기능을 사용하여 JCA 및 JDBC API 간 상호 운용성을 얻습니다.
다양한 복합 관리 구성으로 그려진 애플리케이션은 Java™ EE(Java Platform, Enterprise Edition) Connector Architecture(JCA) API 및 JDBC(Java Database Connectivity) API 모두를 사용해야 할 수 있습니다. 그러나 일부 경우 JDBC 프로그래밍 모델은 JCA와 전체적으로 통합되지 않습니다(전체 통합이 JCA 스펙의 기반이더라도). 이 불일치는 두 API를 사용하는 애플리케이션에 대한 데이터 액세스 옵션을 제한할 수 있습니다. WebSphere Application Server는 API 확장기능을 제공하여 호환성 문제를 해결합니다.
예를 들어 다음과 같습니다.
확장기능의 장점이 없이 기타 핸들이 해당 연결에 존재하는 경우, 두 API를 사용하는 애플리케이션은 연결 요청을 작성한 후 공유 가능한 연결의 특성을 수정할 수 없습니다. (연결과 연관된 핸들이 없는 경우 연결 특성을 변경할 수 있습니다.) 이 제한사항은 API의 연결 구성 정책 간 비호환성에서 생깁니다.
커넥터 아키텍처(JCA) 스펙은 ConnectionSpec 오브젝트에서 전달하여 연결을 요청하는 경우에(getConnection() 메소드 사용) 자원 어댑터 특정 특성 설정에 전달을 지원합니다. ConnectionSpec 오브젝트는 연결하는 데 사용된 필요한 연결 특성을 포함합니다. 이 환경에서 연결을 얻은 후, 애플리케이션은 특성을 변경하지 않아도 됩니다. JDBC 프로그래밍 모델은 연결 특성을 지정하는 동일한 인터페이스가 없습니다. 대신, 먼저 연결한 다음 연결에 대한 특성을 설정합니다.
WebSphere Application Server는 JDBC 및 JCA 스펙 간의 이러한 차이를 채우도록 다음 확장기능을 제공합니다.
- WSDataSource 인터페이스 - 이 인터페이스는 javax.sql.DataSource
클래스를 확장하며 컴포넌트나 애플리케이션을 사용하여 WebSphere Application Server
JDBCConnectionSpec 클래스를 통해 연결 특성을 지정하여 연결합니다.
- getConnection(JDBCConnectionSpec) - 이 메소드는 JDBCConnectionSpec 클래스에서 지정된 특성으로 연결을 리턴합니다.
- 자세한 정보는 WSDataSource API 문서 주제를 참조하십시오(API 문서 색인에 나열된 대로).
- JDBCConnectionSpec 인터페이스 - 이 인터페이스는 com.ibm.websphere.rsadapter.WSConnectionSpec 클래스를 확장하며, 이 클래스는 javax.resources.cci.ConnectionSpec 클래스를 확장합니다. 표준 ConnectionSpec 인터페이스는 get() 및 set() 메소드 없이 인터페이스 마커만을 제공합니다. WSConnectionSpec 및 JDBCConnectionSpec 인터페이스는 WebSphere Application Server 런타임에서 사용된 get() 및 set() 메소드 세트를 정의합니다. 이 인터페이스는 애플리케이션을 사용하여 적절하게 연결하도록 모든 필수 연결 특성을 지정합니다. WebSphere WSRRAFactory 클래스에서 이 클래스를 작성할 수 있습니다. 자세한 정보는 JDBCConnection API 문서 주제를 참조하십시오(API 문서 색인에 나열된 대로).
- WSRRAFactory 클래스 - WebSphere Relational Resource Adapter용 팩토리 클래스이며, 사용자가 JDBCConnectionSpec 오브젝트나 오브젝트 관련 다른 자원 어댑터를 작성할 수 있습니다. 자세한 정보는 WSRRAFactory API 문서 주제를 참조하십시오(API 문서 색인에 나열된 대로).
- WSConnection 인터페이스 - 사용자가 SQL 연결에서 WebSphere
독점 메소드를 호출할 수 있습니다. 이 메소드는 다음과 같습니다.
- setClientInformation(Properties props) - 클라이언트 정보 설정의 자세한 정보 및 예는 주제, 예: setClientInformation(Properties) API로 클라이언트 정보 설정을 참조하십시오.
- Properties getClientInformation() - 이 메소드는 setClientInformation(Properties)를 사용하여 설정된 특성 오브젝트를 리턴합니다. 리턴된 특성 오브젝트는 클라이언트 정보의 내재적 설정에 영향을 받지 않습니다.
- WSSystemMonitor getSystemMonitor() - 데이터베이스가 시스템 모니터를 지원하는 경우 이 메소드는 백엔드 데이터베이스 연결에서
SystemMonitor 오브젝트를 리턴합니다. 백엔드 데이터베이스는 SystemMonitor 오브젝트에서 일부 연결 통계를 제공합니다. 리턴된 SystemMonitor 오브젝트는
WebSphere 오브젝트
(com.ibm.websphere.rsadapter.WSSystemMonitor)에 랩핑되어 데이터베이스 공급업체 코드에 대한 종속성에서 애플리케이션을 표시합니다.
자세한 정보는 com.ibm.websphere.rsadapter.WSSystemMonitor Java 문서를 참조하십시오. 다음 코드는
WSSystemMonitor 클래스의 사용 예입니다.
import com.ibm.websphere.rsadapter.WSConnection; ... try{ InitialContext ctx=new InitialContext(); // Perform a naming service lookup to get the DataSource object. 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) // indicates that system monitoring is supported on the current backend database { sysMon.enable(true); sysMon.start(WSSystemMonitor.RESET_TIMES); // interact with the database sysMon.stop(); // collect data from the sysMon object } conn.close();
WSConnection 인터페이스는 plugins_root/com.ibm.ws.runtime.jar 파일의 일부입니다.
예: 데이터베이스 연결을 위한 IBM 확장 API 사용.
WSDataSource 확장 API를 사용하면, JDBC 애플리케이션을 코딩하여 연결을 얻기 전에 오브젝트를 통한 연결 특성을 정의할 수 있습니다. 이 동작은 애플리케이션이 CMP와 같이 다른 컴포넌트와 연결을 공유할 수 있는 가능성을 늘립니다.
사용자 애플리케이션이 트랜잭션 내 다른 CMP(Container-Managed Persistence) Bean과 공유될 수 있는 공유 가능한 연결로 실행하는 경우, WebSphere Application Server 확장 API를 사용하여 연결하는 것이 좋습니다. 이 API를 사용하면, 애플리케이션을 다른 애플리케이션 서버로 포트할 수 없습니다.
JDBC 애플리케이션에서 직접 확장 API로 코딩할 수 있습니다. 연결하기 위해 DataSource 인터페이스를 사용하는 대신 WSDataSource 인터페이스를 사용합니다. 다음 코드 세그먼트는 WSDataSource를 보여줍니다.
import com.ibm.websphere.rsadapter.*;
...
// Create a JDBCConnectionSpec and set connection properties. If this connection
// is shared with the CMP bean, make sure that the isolation level is the same as
// the isolation level that is mapped by the Access Intent defined on the CMP bean.
JDBCConnectionSpec connSpec = WSRRAFactory.createJDBCConnectionSpec();
connSpec.setTransactionIsolation(CONNECTION.TRANSACTION_REPEATABLE_READ);
connSpec.setCatalog("DEPT407");
// Use WSDataSource to get the connection
Connection conn = ((WSDataSource)datasource).getConnection(connSpec);
예: IBM 확장 API를 사용하여 CMP Bean과 BMP Bean 사이 연결 공유.
JDBC 오브젝트를 통해 데이터에 액세스하는 애플리케이션 컴포넌트 내에서(BMP(Bean-Managed Persistence) Bean), WebSphere 확장 API를 사용하여 연결을 얻기 전에 오브젝트를 통해 연결 특성을 정의할 수 있습니다. 이 동작으로 BMP Bean은 CMP(Container-Managed Persistence) Bean으로 연결을 공유할 수 있는 가능성이 늘어납니다.
BMP Bean이 트랜잭션 내 다른 CMP(Container-Managed Persistence) Bean과 공유될 수 있는 공유 가능한 연결로 실행하는 경우, WebSphere Application Server 확장 API를 사용하여 연결하는 것이 좋습니다. 이 API를 사용하면, 애플리케이션을 다른 애플리케이션 서버로 포트할 수 없습니다.
이 경우, DataSource 인터페이스가 아닌 확장 API WSDataSource 인터페이스를 사용합니다. CMP 및 BMP(Bean-Managed Persistence) Bean 모두가 동일한 실제 접속을 하도록 하려면, CMP 및 BMP Bean 모두에서 동일한 액세스 인텐트 프로파일을 정의합니다. BMP 메소드 내에서, 관계형 자원 어댑터 헬퍼 클래스에서 올바른 격리 레벨을 얻을 수 있습니다.
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;
// following imports are used by the IBM extended API
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 implementation class for Enterprise Bean: Simple
*/
public class SimpleBean implements javax.ejb.EntityBean {
private javax.ejb.EntityContext myEntityCtx;
// Initial context used for lookup.
private javax.naming.InitialContext ic = null;
// define a JDBCConnectionSpec as instance variable
private JDBCConnectionSpec connSpec;
// define an AccessIntentService which is used to get
// an AccessIntent object.
private AccessIntentService aiService;
// AccessIntent object used to get Isolation level
private AccessIntent intent = null;
// Persitence table name
private String tableName = "cmtest";
// DataSource JNDI name
private String dsName = "java:comp/env/jdbc/SimpleDS";
// DataSource
private DataSource ds = null;
// bean instance variables.
private int id;
private String name;
/**
* In setEntityContext method, you need to get the AccessIntentService
* object in order for the subsequent methods to get the AccessIntent
* object.
* Other ejb methods will call the private getConnection() to get the
* connection which has all specific connection properties
*/
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;
// Insert SQL String
String sql = "INSERT INTO" + tableName + "(id, name) VALUES (?, ?)";
id = newID;
name = "";
try {
// call the common method to get the specific connection
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 {
// call the common method to get the specific connection
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());
}
// You need to determine which select statement to be used based on the
// AccessIntent type:
// If READ, then uses a normal SELECT statement. Otherwise uses a
// SELECT...FORUPDATE statement
// If your backend is SQLServer, then you can use different syntax for
// the FOR UPDATE clause.
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){
}
}
}
/**
* This method will use the AccessIntentService to get the access intent;
* then gets the isolation level from the DataStoreHelper
* and sets it in the connection spec; then uses this connection
* spec to get a connection which has the specific connection
* properties.
**/
private Connection getConnection()
throws java.sql.SQLException, javax.resource.ResourceException, EJBException {
// get current access intent object using EJB context
intent = aiService.getAccessIntent(myEntityCtx);
// Assume this bean only supports the pessimistic concurrency
if (intent.getConcurrencyControl()
!= AccessIntent.CONCURRENCY_CONTROL_PESSIMISTIC) {
throw new EJBException("Bean supports only pessimistic concurrency");
}
// determine correct isolation level for currently configured database
// using DataStoreHelper
int isoLevel =
WSCallHelper.getDataStoreHelper(ds).getIsolationLevel(intent);
connSpec = WSRRAFactory.createJDBCConnectionSpec();
connSpec.setTransactionIsolation(isoLevel);
// Get connection using connection spec
Connection conn = ((WSDataSource) ds).getConnection(connSpec);
return conn;
}