Basic CMP
Resin 3.0

Features
Installation
Configuration
Web Applications
IOC/AOP
Resources
JSP
Servlets and Filters
Portlets
Databases
Admin (JMX)
CMP
EJB
Amber
EJB 3.0
Security
XML and XSLT
XTP
JMS
Performance
Protocols
Third-party
Troubleshooting/FAQ

User's Guide
Reference
Tutorials
Scrapbook

Basic CMP
Find with EJB-QL
Creating and Removing
1-n Relationship
ejbSelect EJB-QL
1-1 Relationship
n-m Relationship
Map/Compound PK
Tutorials
Tutorials
Find with EJB-QL

Find this tutorial in: /usr/local/resin/webapps/resin-doc/cmp/tutorial/cmp-basic
Try the Tutorial

Scenario: Headmaster Dumbledore needs a database of Courses.

This example focuses on:

  • Introduces Container Managed Persistance (CMP) fundamental concepts
  • Setting up the database to work with Resin-CMP
  • Developing the local-home, local, and EntityBean classes
  • Writing the deployment descriptor (cmp-basic.ejb)
  • Developing a Servlet to lookup and use the entity bean
  • Configuring Resin to deploy the EJB and use JNDI

  1. Database Schema
  2. Implementation Classes
    1. Local Home Interface
    2. Local Interface
    3. Bean Implementation
  3. Deployment Descriptor
    1. EJB to SQL Mapping
  4. Resin Configuration
    1. XADataSource
  5. Client Servlet
  6. Conclusion

Note: The EJB 3.0 Basic tutorial describes the same example for EJB 3.0. You can compare the two tutorials to help evaluate which technology best suits your application.

Resin-CMP manages tables in a relational database using a Java bean interface. Each database table corresponds to a single "entity bean". (Since Resin-CMP uses the EJB specification, most of its jargon comes from EJB.) By creating an entity bean with container managed persistence, you let Resin-CMP generate the SQL to load, store, and cache entity beans from the database. Avoiding SQL is an advantage in itself, but the primary advantage is the increased flexiblity of your application code. Maintenance and code-refactoring can focus on the beans instead of changing lots of SQL statements in the program.

In this example, Hogwarts School of Witchcraft and Wizardry uses Resin-CMP to manage its list of courses offered for the term. Each course has a name and a teacher for the course. The example has been simplified as much as possible; following examples show how to query the database and create and remove database entries.

Database Schema

The database table uses the course name as the primary key. Each bean in Resin-CMP needs its own primary key. It is possible to let the database generate primary keys, e.g. automatically generated integers. Since this example only reads data from the table, we'll prepopulate it with the courses.

Each table maps to a single object, using the abstract-schema-name and sql-table in the ejb.xml. If you look at the distribution's database schema (in cmp/WEB-INF/sql/default.sql), you'll notice that each example has its own table, e.g. basic_courses and find_courses, even though the examples often share the same table. Generally a Resin-CMP bean has complete control over a database table. Because it controls the table, the bean can cache data without worrying that the database will change without its knowledge.

Database columns map to accessor methods in the bean, using standard rules. The id field maps to getId. The instructor column maps to getInstructor and setInstructor.

course.sql
CREATE TABLE basic_courses (
  id VARCHAR(250) NOT NULL,
  teacher VARCHAR(250),

  PRIMARY KEY(id)
);

INSERT INTO basic_courses VALUES('Potions', 'Severus Snape');
INSERT INTO basic_courses VALUES('Transfiguration', 'Minerva McGonagall');

Implementation Classes

Each database table corresponds to a single "entity bean". Since Resin-CMP uses the EJB specification, most of its the jargon comes from EJB. The developer needs to write three classes for each entity bean:

  • The local home factory interface for clients to find and create beans.
  • The local interface for clients to use.
  • The implementation bean to write business methods.

Why three classes? The interfaces add code clarity for the developer and flexibility for Resin-CMP. It's conceptually cleaner to separate the interfaces of the bean from its implementation. It's also cleaner to separate the factory pattern interface from the object interface. Separating the classes gives Resin-CMP the flexibility to add its caching and transaction code without requiring any client changes.

Resin-CMP focuses on local interfaces, as opposed to the remote interfaces of traditional EJB. Local interfaces are used in a single servlet web-application and avoid the complexities of distributed computing. In addition, local interfaces are faster since they can use normal pass-by-reference Java calls. In this way, Resin-CMP turns EJB on its head. EJB is a distributed computing interface with support for object-managed databases. Resin-CMP provides an object view to relational database, and has support for distributed calls for those rare applications which really need it.

Local Home Interface

The home interface is reponsible for factory pattern methods: finding existing objects and creating new objects. At minimum, each home interface lets you find an object using its primary key. The findByPrimaryKey method exists for any entity bean. Like all the find methods, Resin-CMP will generate the implementation code and SQL for findByPrimaryKey. We just need to add the method to the interface.

Find methods always return the local interface for the bean or a collection of local interfaces and always throw the FinderException.

Each home interface must extend the EJBLocalHome interface. Local home interfaces can only be used same web-application as the server. This makes it a perfect solution for servlet-based database applications.

CourseHome.java
package example.cmp.basic;

import javax.ejb.*;

public interface CourseHome extends EJBLocalHome {
  Course findByPrimaryKey(String name)
    throws FinderException;
}

Local Interface

The local interface, Course, is where all the action is. We expose three methods to clients, getCourseId, getInstructor and setInstructor. Local interfaces always extend EJBLocalObject.

Clients always use the home interface to get the local interface. The Resin-CMP generated stub (the class implementing the interface) will call the underlying implementation bean and SQL, adding transaction management as necessary.

Course.java
package example.cmp.basic;

import javax.ejb.*;

public interface Course extends EJBLocalObject {
  String getCourseId();

  String getInstructor();
  void setInstructor(String instructor);
}

Bean Implementation

Since Resin-CMP provides most of the implementation code, the Bean implementation just has a bunch of abstract methods. More complicated beans will add business methods to the entity bean's implementation class. Because business methods run in a single transaction context, you can use them to ensure database consistency without having to write any transaction code yourself.

The field accessors are abstract since Resin-CMP will generate the code to call JDBC and execute the SQL queries. Like all entity beans, clients never use a CourseBean directly, but always work through a stub generated by Resin-CMP.

The AbstractEntityBean class is a convenience class in Resin-CMP. Each entity bean must implement the EntityBean interface which has several methods. Since most applications don't need to customize the methods, AbstractEntityBean simplifies the implementation code.

CourseBean.java
package example.cmp.basic;

public abstract class CourseBean
  extends com.caucho.ejb.AbstractEntityBean {
  public abstract String getCourseId();

  public abstract String getInstructor();
  public abstract void setInstructor(String val);
}

With Resin-EJB, all the Java source can be dropped in WEB-INF/classes. Resin will automatically compile any changes and regenerate the persistence classes, stubs and skeletons.

Deployment Descriptor

The deployment descriptor configures the entity bean. It specifies the home, local, and implementation classes.

The *.ejb file is generally defined by the bean provider, i.e. whoever creates the bean. Later, we'll also need to attach the bean to the webserver as described in the following section. Deploying the bean is as easy as dropping the *.ejb in WEB-INF. When the web-app reloads, EJBServer will automatically pick up the *.ejb. (You may need to force a reload by touching a class file.)

WEB-INF/cmp-basic.ejb
<ejb-jar>
<enterprise-beans>
  <entity>
    <ejb-name>basic_CourseBean</ejb-name>
    <local-home>example.cmp.basic.CourseHome</local-home>
    <local>example.cmp.basic.Course</local>
    <ejb-class>example.cmp.basic.CourseBean</ejb-class>

    <prim-key-class>String</prim-key-class>
    <primkey-field>courseId</primkey-field>

    <persistence-type>Container</persistence-type>
    <reentrant>True</reentrant>

    <abstract-schema-name>courses</abstract-schema-name>
    <sql-table>basic_courses</sql-table>

    <cmp-field><field-name>courseId</field-name></cmp-field>

    <cmp-field>
      <field-name>instructor</field-name>
      <sql-column>teacher</sql-column>
    </cmp-field>
  </entity>
</enterprise-beans>
</ejb-jar>

TagMeaning
ejb-jartop-level containing element
enterprise-beansbean provider configuration
entitydefines an entity bean
ejb-nameThe name of the ejb. Used to tie ejbs together and used in contructing the url.
local-homeclass name of the local home interface
localclass name of the local interface
ejb-classthe bean's implementation class
prim-key-classclass of the primary key
persistence-typecontainer persistence in this example
reentrantthe bean can call itself
abstract-schema-nameabstract name for the bean
sql-tableactual SQL table name for the bean
primkey-fieldwhich field is the primary key
cmp-fieldall container managed persistence fields should be listed
field-namethe field's name
sql-columnthe SQL table name for the field

EJB to SQL Mapping

abstract-schema-name names the abstract table for the entity bean. If no other mapping is specified, it will be used for the SQL table. In the example, it's set to "basic_courses". If the abstract-schema-name is missing, Resin-CMP will use the ejb-name for the database table. You can add a sql-table to specify a different SQL table. sql-table is a Resin-CMP extension.

<abstract-schema-name>courses</abstract-schema-name>
<sql-table>basic_courses</sql-table>

Database fields are converted from the get and set accessor names using a beans-like mapping. "get" is removed and the uppercased character is lower cased. Resin-EJB will convert any xY pattern to "x_y". So getId maps to the SQL column "id". You can specify a sql-column to specify the SQL column:

<cmp-field>
  <field-name>instructor</field-name>
  <sql-column>teacher</sql-column>
</cmp-field>

Resin Configuration

Now that we've built the bean, we need to attach it to Resin. The entity bean is deployed using the EJBServer resource.

WEB-INF/web.xml
<web-app>
  <!-- jdbc configuration -->
  <resource-ref>
    <res-ref-name>jdbc/test</res-ref-name>
    <res-type>javax.sql.XADataSource</res-type>
    <driver-name>oracle.jdbc.driver.OracleDriver</driver-name>
    <url>jdbc:oracle:thin:@gryffindor:1521:hogwarts</url>
  </resource-ref>

  <!-- server configuration -->
  <resource-ref>
    <res-ref-name>java:comp/env/cmp</res-ref-name>
    <class-name>com.caucho.ejb.EJBServer</class-name>
    <init-param data-source="java:comp/env/jdbc/test"/>
  </resource-ref>
</web-app>

EJBServer configurationmeaning
resource-refconfigures a JNDI resource
res-ref-nameThe JNDI name where the resource will be stored
class-nameThe name of the bean implementing the resource
init-parambean-style configuration for the resource
data-sourceJNDI reference to JDBC driver
JDBC tagmeaning
resource-refconfigures a resource stored in JNDI
res-ref-namethe JNDI suffix after the standard java:comp/env
res-typejavax.sql.DataSource is for JDBC database drivers
driver-nameclassname of the driver
urlJDBC URL for the driver

XADataSource

The database needs to be configured using XADataSource instead of the usual DataSource. The reason is that XADataSource enables transactions in the database. DataSource is not transaction aware. So if there's a conflict or some other need to roll back the transaction, you need XADataSource and the database's transaction ability to protect the database consistency.

Client Servlet

Now that we've defined the EJB, we should go ahead and use it. Because we haven't defined any method to find all the courses, we need to know them beforehand.

Because everything is defined in the web.xml, we just need to know that the CourseHome is at java:comp/env/cmp/house. So servlet code can be completely independent of the server deployment. Because the JNDI lookup is relatively slow, applications generally lookup the Home interface in the init() interface and store it as a servlet variable.

CourseServlet.java
package example.cmp.basic;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.naming.*;
import javax.ejb.*;

public class CourseServlet extends HttpServlet {
  // cache the home interface so the JNDI only happens once
  private CourseHome home = null;

  public void init()
    throws ServletException
  {
    try {
      // The JNDI context containing local EJBs
      Context cmp = (Context) new InitialContext().lookup("java:comp/env/cmp");

      // Get the house stub
      home = (CourseHome) cmp.lookup("basic_CourseBean");
    } catch (NamingException e) {
      throw new ServletException(e);
    }
  }

  public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws IOException, ServletException
  {
    PrintWriter out = res.getWriter();

    res.setContentType("text/html");

    out.println("<h3>Course Details</h3>");

    String []courses = new String[] { "Potions", "Transfiguration" };

    try {
      for (int i = 0; i < courses.length; i++) {
        // Find the course using the home interface
        Course course = home.findByPrimaryKey(courses[i]);

        out.println("course: " + course.getCourseId() + "<br>");
        out.println("instructor: " + course.getInstructor() + "<br>");
        out.println("<br>");
      }
    }
    catch (FinderException e) {
      throw new ServletException(e);
    }
  }
}

Course Details

course: Potions instructor: Severus Snape course: Transfiguration instructor: Minerva McGonagall

Conclusion

The core of Resin-CMP's database management is its management of a single table. Much of the work underlying the database management is hidden from the applicaton. Transaction management and caching happen automatically. For example, once the course has been loaded from the database, Resin-CMP does not need to query the database again until the course changes. So read-only requests, the most common, can avoid all database traffic.

More complicated applications build on the single table management. The following examples add more realistic features to this example: using queries to find all courses and creating new database rows.

Try the Tutorial


Tutorials
Tutorials
Find with EJB-QL
Copyright © 1998-2005 Caucho Technology, Inc. All rights reserved.
Resin® is a registered trademark, and HardCoretm and Quercustm are trademarks of Caucho Technology, Inc.