Example: Share connections between CMP and BMP beans

 

You can the WSDataSource interface to establish connections between CMP and BMP beans in a transaction.

Note that applications using WSDataSource cannot be ported to non-WAS appservers..

To ensure your CMP and BMP beans are sharing the same physical connection, define the same Access Intent profile on both. Inside your BMP method, you can get the correct isolation level from the resource adapter.

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;

// IBM extended APIs
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 xEntityContext;

 // Initial context used for lookup.
 private javax.naming.InitialContext ic = null;

 // Define a JDBCConnectionSpec as instance variable
 private JDBCConnectionSpec xConnSpec;

 // Define an AccessIntentService which is used to get 
 // an AccessIntent object.
 private AccessIntentService ai;

 // AccessIntent object used to get Isolation level
 private AccessIntent intent = null;
 
 // Persistence table name
 private String tableName = "XTABLE";

 // DataSource JNDI name
 private String dsName = "java:comp/env/jdbc/MyDataSource";

 // 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) 
 {
  xEntityContext = ctx;

  try 
  {
   ai = (AccessIntentService) getInitialContext().lookup("java:co../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 String

  String sql = "INSERT INTO " + tableName + " (id, name) VALUES (?, ?)";

  id = newID;
  name = "";

  try 
  {
   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 
  {
   javax.ejb.DuplicateKeyException(id + "has already existed");
  }
  catch (SQLException sqle) 
  {
   throw new javax.ejb.CreateException(sqle.getMessage());
  }
  catch (CreateException ce) 
  {
   throw ce;
  }
  finally 
  {
   if  Exception(e) 
    {
    }
   }
  }
  return new SimpleKey(id);
 }

  /**
  * ejbLoad
  */

 public void ejbLoad() throws javax.ejb.EJBException 
 {

  Connection conn = null;
  PreparedStatement ResultSet rs = null;

  String loadSQL = null;

  try 
  {
   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());
  }

  // Determine which select statement to use based on the
  // AccessIntent type...
  // 
  // If READ, then use a SELECT statement. 
  // Otherwise use 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 
  {
   Exception(e) 
   {
   }
   try 
   {
    if  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 = ai.getAccessIntent(xEntityContext);
  
  // Assume this bean only supports the pessimistic concurrency
  if (intent.getConcurrencyControl() != AccessIntent.CONCURRENCY_CONTROL_PESSIMISTIC) 
  {
   throw new EJBException("Bean supports only pessimistic concurency");
  }

  // Determine correct isolation level for currently 
  // configured database using DataStoreHelper
  int isoLevel = WSCallHelper.getDataStoreHelper(ds).getIsolationLevel(intent);
  xConnSpec = WSRRAFactory.createJDBCConnectionSpec();
  xConnSpec.setTransactionIsolation(isoLevel);

  // Get connection using connection spec
  Connection conn = ((WSDataSource) ds).getConnection(xConnSpec);
  return conn;
 }

 

See Also

Unshareable and shareable connections
Data access application programming interface support