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:
Call the getConnection(...) method of the DataSource object to obtain a connection to perform the JDBC operations in the current transactional context (if there are JDBC operations)
Call the close() method on this connection after the database access operations so that the connection can be returned to the connection pool (and be dissociated from the potential current transaction).
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:
Declare the resource reference in the standard deployment descriptor using a resource-ref element.
Look up the datasource in the Enterprise Bean environment using the JNDI interface (refer to Chapter 12 Enterprise Bean Environment).
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.
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:
Container: the deployer sets up the sign-on information.
Bean: the bean programmer should use the getConnection method with user and password parameters.
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.