Container Persistent Relations
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
Creating and Removing
Tutorials
ejbSelect EJB-QL

  1. Database Schema
  2. Client JSP
  3. Implementation Classes
    1. House Implementation
    2. Student Implementation
  4. Deployment Descriptor
  5. Conclusion

EJB 2.0 containers can manage relationships between entity beans. In this example, each boarding school house has a number of students. Each house and student has its own entity bean and each student belongs to a house.

Resin-CMP generates the classes and SQL to maintain the relationships automatically. If a student changes houses, Resin-CMP updates both the student and house records. If a student is expelled, both the student and house records get update automatically.

Relationships are updated using the local interface. The second draft of the EJB 2.0 specification distinguishes local from remote interfaces. Local interfaces are useful when using EJB as a database interface or for fast calls within the same JVM.

The tutorial focuses on the classes and configuration for a simple bean/bean relationship. Additional details, like changing houses or creating and deleting students are deferred to another tutorial.

Database Schema

The example has two tables: houses and students. Each student belongs to a single house. It's an n-1 relation like a parent-children relationship.

The database stores the student/house relationship using an field in the student record. The student table points to the house with the house's primary key. The generated code will create the query to list all students belonging to a house.

one2many.sql
CREATE TABLE students (
  name VARCHAR(250) NOT NULL,
  house VARCHAR(250),

  PRIMARY KEY(name)
);

CREATE TABLE houses (
  name VARCHAR(250) NOT NULL

  PRIMARY KEY(name)
);

INSERT INTO houses VALUES('Gryffindor');
INSERT INTO houses VALUES('Slytherin');

INSERT INTO students VALUES('Harry Potter', 'Gryffindor');
INSERT INTO students VALUES('Ron Weasley', 'Gryffindor');
INSERT INTO students VALUES('Hermione Granger', 'Gryffindor');

INSERT INTO students VALUES('Draco Malfoy', 'Slytherin');
INSERT INTO students VALUES('Millicent Bulstrode', 'Slytherin');

Client JSP

The example client loops through the houses and prints each student from the house. The student beans are returned in a collection.

As in the previous tutorial, Resin-EJB stores the home interfaces in a JNDI context. The student home is stored in java:comp/env/cmp/students and the house home is stored at java:comp/env/cmp/houses. The ejb-local is used since these are the local interfaces for the beans.

The list of students is passed using the JDK 1.2 Collection class and taking students out requires an Iterator. Since we are using the local interface, the collection is live. The application could use students.add(student) to add a new sutdent.

Servlet Fragment
...

Collection houses = houseHome.findAll();

// iterate through the Houses
iter = houses.iterator();
while (iter.hasNext()) {
  House house = (House) iter.next();
  out.println("<h4>" + house.getName() + "</h4>");

  students = (Collection) house.getStudentList();
      
  // iterate through the students for that House
  Iterator studentIter = students.iterator();
      
  while (studentIter.hasNext()) {
    Student student = (Student) studentIter.next();
    out.println("<li>" + student.getName());
  }
}

..

<h4>Gryffindor</h4>

<li>Harry Potter
<li>Hermione Granger
<li>Ron Weasley

<h4>Slytherin</h4>

<li>Draco Malfoy
<li>Millicent Bulstrode

Implementation Classes

Both the house and students beans have the three EJB classes: local home interface, local interface, and implementation class. Although the client only sees the local home and local interfaces, most of the interesting work happens in the implementation class. With container managed persistence, most of the work is done automatically, so the classes themselves are short.

House Implementation

The home interface for the house bean implements the minimal method, findByPrimaryKey.

HouseHome.java
package test.entity.students;

import java.rmi.*;
import javax.ejb.*;

public interface HouseHome extends EJBLocalHome {
  House findByPrimaryKey(String name)
    throws FinderException;
}

The remote interface returns the name of the house and the collection of students. getStudents does not return the container-managed student collection directly. The implementation must first store the managed collection into a concrete implementation.

House.java
package test.entity.students;

import java.rmi.*;
import javax.ejb.*;

public interface House extends EJBLocalObject {
  String getName();

  Collection getStudentList();
}

The getStudentList collection is active. Calling the collection add and remove methods will add and remove students from the house. Adding a student to a house will automatically remove it from any other house. Resin-EJB keeps the relationship consistent.

The house's student list gets contructed from the student records. In the SQL schema, the houses table has no reference to students. So getStudentList needs to query the student records to find the students in a house. In other words, House's getStudentList is paired with Student's getHouse.

The deployment descriptor connects the paired fields getStudentList and getHouse. Resin-EJB can't determine the pairings of relation fields by introspection alone. So you'll need to tell Resin-EJB in the ejb-relationship elements.

HouseBean.java
package example.cmp.relations.one2many;

abstract public class HouseBean
  extends com.caucho.ejb.AbstractEntityBean {
  /**
   * Returns the primary key.
   */
  abstract public String getName();
  /**
   * Container-managed relationship can't return directly.
   */
  abstract public Collection getStudentList();
}

Student Implementation

The student bean adds the getHouse accessor to a basic bean. Since getHouse is paired with the house's getStudentList, setting a house will automatically change the house's student list to match.

StudentHome.java
package example.cmp.relations.one2many;

import java.rmi.*;
import javax.ejb.*;

/**
 * Remote interface for the student home.
 */
public interface StudentHome extends EJBLocalHome {
  /**
   * Returns the named student.
   */
  Student findByPrimaryKey(String name)
    throws FinderException;
}

The student interface returns the student's name and the remote interface of the house. As always, beans never refer to the implementation classes, always the local interface. The student interface must return House and never HouseBean.

Student.java
package example.cmp.relations.one2many;

import java.rmi.*;
import javax.ejb.*;

public interface Student extends EJBLocalObject {
  String getName();

  House getHouse();
}

The StudentBean implementation is simple since all the real code is created automatically.

StudentBean.java
package example.cmp.relations.one2many;

abstract public class StudentBean
  extends com.caucho.ejb.AbstractEntityBean {
  abstract public String getName();

  abstract public House getHouse();
  abstract public void setHouse(House house);
}

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 has three sections:

  • House definition
  • Student definition
  • The relationship between the house and student beans.

References to the bean in the relationships section use the ejb-name definition.

WEB-INF/one2many.ejb -- overview
<ejb-jar>
<enterprise-beans>
  <entity>
    <ejb-name>houses</ejb-name>
    ...
  </entity>

  <entity>
    <ejb-name>students</ejb-name>
    ...
  </entity>
</enterprise-beans>

<relationships>
  <ejb-relation>
    ...
  </ejb-relation>
</relationships>
</ejb-jar>

The house definition is just like the simple entity bean in the previous house tutorial. The main addition is the cmr-field section. The houses definition needs to define the studentList field and can specify its return type.

WEB-INF/one2many.ejb -- houses
...
<entity>
  <ejb-name>houses</ejb-name>

  <local-home>example.cmp.relations.one2many.HouseHome</local-home>
  <local>example.cmp.relations.one2many.House</local>
  <ejb-class>example.cmp.relations.one2many.HouseBean</ejb-class>

  <abstract-schema-name>houses</abstract-schema-name>

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

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

  <cmp-field><field-name>name</field-name></cmp-field>
</entity>
...

The house definition is also like the simple entity bean in the previous house tutorial. The main addition is the cmr-field section.

WEB-INF/student.ejb -- students
...
<entity>
  <ejb-name>students</ejb-name>

  <local-home>example.cmp.relations.one2many.StudentHome</local-home>
  <local>example.cmp.relations.one2many.Student</local>
  <ejb-class>example.cmp.relations.one2many.StudentBean</ejb-class>

  <abstract-schema-name>student</abstract-schema-name>

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

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

  <cmp-field><field-name>name</field-name></cmp-field>
</entity>
...

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-namemaps to the SQL table name
primkey-fieldwhich field is the primary key
cmp-fieldall container managed persistence fields should be listed
field-namethe name of a cmp-field
cmr-fielddefines a relationship field
cmr-field-namethe name of the relationship field
cmr-field-typethe name of the collection items

The relationship connects the studentList field of the House to the house field of the Student. Although the XML looks a bit forbidding, it's almost entirely boilerplate. You can cut and paste a standard example, only filling in a few fields. In this example, there are only four actual values, surrounded by the verbosity of XML.

WEB-INF/student.ejb -- relationships
...
<relationships>
  <ejb-relation>
    <ejb-relationship-role>
      <relationship-role-source>
         <ejb-name>students</ejb-name>
      </relationship-role-source>
      <cmr-field>
        <cmr-field-name>house</cmr-field-name>
      </cmr-field>
    </ejb-relationship-role>

    <ejb-relationship-role>
      <relationship-role-source>
        <ejb-name>houses</ejb-name>
      </relationship-role-source>

      <cmr-field>
        <cmr-field-name>studentList</cmr-field-name>
      </cmr-field>
    </ejb-relationship-role>
  </ejb-relation>
</relationships>
...

TagMeaning
relationshipscontains all bean/bean relationships
ejb-relationa single relation
ejb-relationship-roleone end of the relation
relationship-role-sourcespecifying the bean
ejb-namethe ejb-name of the bean
cmr-fieldspecifying the bean's relation
cmr-field-namethe relation's name

Conclusion

The student houses example just touches the simplest example of using container managed relations. Even in this basic case, it's easy to see how the EJB 2.0 persistent model takes the drudgery out of accessing databases.

But more important than saving a bit of work, the EJB 2.0 persistence model makes code more flexible and less brittle. Projects can explore data models and refactor code without worrying that the refactoring will break years-old SQL.


Creating and Removing
Tutorials
ejbSelect 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.