8.6. Writing Database Access Operations (Bean-Managed Persistence)

For bean-managed persistence, data access operations are developed by the bean provider using the JDBC interface. However, getting database connections must be obtained through the javax.sql.DataSource interface on a datasource object provided by the EJB platform. This is mandatory since the EJB platform is responsible for managing the connection pool and for transaction management. Thus, to get a JDBC connection, in each method performing database operations, the bean provider must:

A method that performs database access must always contain the getConnection and close statements, as follows:

public void doSomethingInDB (...) {
    conn = dataSource.getConnection();
    ... // Database access operations
    conn.close();
}

A DataSource object associates a JDBC driver with a database (as an ODBC datasource). It is created and registered in JNDI by the EJB server at launch time (refer also to Chapter 4 Configuring JDBC DataSources).

A DataSource object is a resource manager connection factory for java.sql.Connection objects, which implements connections to a database management system. The Enterprise Bean code refers to resource factories using logical names called Resource manager connection factory references. The resource manager connection factory references are special entries in the Enterprise Bean environment. The bean provider must use resource manager connection factory references to obtain the datasource object as follow:

The deployer binds the resource manager connection factory references to the actual resource factories that are configured in the server. This binding is done in the JOnAS-specific deployment descriptor using the jonas-resource element.

8.6.1. Database Access Operation Example

The declaration of the resource reference in the standard deployment descriptor looks like the following:

<resource-ref>
<res-ref-name>jdbc/AccountExplDs</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

The <res-auth> element indicates which of the two resource manager authentication approaches is used:

The JOnAS-specific deployment descriptor must map the environment JNDI name of the resource to the actual JNDI name of the resource object managed by the EJB server. This is done in the <jonas-resource> element.

<jonas-entity>
  <ejb-name>AccountExpl</ejb-name>
  <jndi-name>AccountExplHome</jndi-name>
  <jonas-resource>
    <res-ref-name>jdbc/AccountExplDs</res-ref-name>
    <jndi-name>jdbc_1</jndi-name>
  </jonas-resource>
</jonas-entity>

The ejbStore method of the same Account example with bean-managed persistence is shown in the following example. It performs JDBC operations to update the database record representing the state of the Entity Bean instance. The JDBC connection is obtained from the datasource associated with the bean. This datasource has been instantiated by the EJB server and is available for the bean through its resource reference name, which is defined in the standard deployment descriptor.

In the bean, a reference to a datasource object of the EJB server is initialized:

it = new InitialContext();

ds = (DataSource)it.lookup("java:comp/env/jdbc/AccountExplDs");

Then, this datasource object is used in the implementation of the methods performing JDBC operations, such as ejbStore, as illustrated in the following:

public void ejbStore
    Connection conn = null;
    PreparedStatement stmt = null;
    try { // get a connection
        conn = ds.getConnection();
        // store Object state in DB
        stmt = conn.prepareStatement("update account 
          set customer=?,balance=? where accno=?");
        stmt.setString(1, customer);
        stmt.setDouble(2, balance);
        Integer pk = (Integer)entityContext.getPrimaryKey();
        stmt.setInt(3, pk.accno);
        stmt.executeUpdate();
     } catch (SQLException e) {
        throw new javax.ejb.EJBException("Failed to store bean 
          to database", e);
     } finally {
        try {
            if (stmt != null) stmt.close();    // close statement
            if (conn != null) conn.close();    // release connection
        } catch (Exception ignore) {}
     }
}

Note that the close statement instruction may be important if the server is intensively accessed by many clients performing Entity Bean access. If the statement is not closed in the finally block, since stmt is in the scope of the method, it will be deleted at the end of the method (and the close will be implicitly done). However, it may be some time before the Java garbage collector deletes the statement object. Therefore, if the number of clients performing Entity Bean access is important, the DBMS may raise a "too many opened cursors" exception (a JDBC statement corresponds to a DBMS cursor). Since connection pooling is performed by the platform, closing the connection will not result in a physical connection close, therefore opened cursors will not be closed. Thus, it is preferable to explicitly close the statement in the method.

It is a good programming practice to put the JDBC connection and JDBC statement close operations in a finally block of the try statement.