Caucho Technology
documentation
examples
changes

amber (jpa)
ejb
database
ioc
jmx
jsf
messaging
quercus
remoting
servlet
security

field
property
query
many-to-one
one-to-many
many-to-many
inherit
sessions

field-based persistent objects


Amber's persistence based on JPA (the Java Persistence Architecture) supports a field-based relational model: each Java field represents a database column. The tutorial shows the configuration, classes, and client code for a single-table entity.

Demo

Overview

Amber provides a Java model for a relational database, following the Java Persistence standard.

A typical project starts planning with the relational database schema and matching the Java model to that schema. This data-driven approach contrasts with a transparent persistent object approach which starts with Java classes and then tries to create storage to match the Java model, an approach more typical of object-oriented databases. While the transparent persistence model may be appropriate for some applications, the persistence specification wisely leaves transparent persistence to other products and specifications, and concentrates on the relational database model.

In a way, Amber simply provides an extension to SQL queries, returning fully-populated Java objects instead of just returning primitive values like Strings. That somewhat understates Amber's capabilities since the Java objects are live, updating the database in a object-oriented fashion, and also provides caching. Still, viewing Amber as a SQL extension supporting relations and objects is a good starting model.

The tutorial uses "entity" to mean a persistent object.

Files in this tutorial

FILEDESCRIPTION
WEB-INF/resin-web.xmlresin-web.xml configuration
WEB-INF/classes/META-INF/persistence.xmlMETA-INF/persistence.xml configuration
WEB-INF/classes/example/Course.javaThe course bean
WEB-INF/classes/example/CourseServlet.javaThe course servlet

Database Model

The tutorial's design begins with its database model. The table is a collection of school courses, each with an assigned teacher. The table has an integer primary key "id" and two string data fields, "course" and "teacher".

Example: course.sql
CREATE TABLE amber_basic_courses (
  id INTEGER PRIMARY KEY auto_increment,

  course VARCHAR(250),
  teacher VARCHAR(250)
);

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

To judge the complexity of Amber, it's useful to compare the Amber Java model to the simplest possible Java model. The simple model has a single class, Course, for the table and three fields for the table's columns.

Example: Course.java - minimal Java model
package example;

public class Course {
  private int id;
  private String course;
  private String teacher;
}

The minimal class is missing any description of its intended use as a persistent object, information needed for maintainable code. In theory, a persistent object tool could use the minimal class automatically, but without more information, the source doesn't properly describe the class behavior. Fortunately, the JDK 1.5 metadata annotations can describe the persistence information in a maintainable, self-documenting way.

Of course, those annotations might have default values and should be overridable by an optional XML configuration file, but it's necessary to annotate the intended function of the entity in the Java source itself to properly document and validate the Java code.

Overview

To find and enhance a persistent Java bean, Amber follows the following procedure.

  1. <ejb-server> in the resin-web.xml configures Amber to start looking for persistence.xml in the classpath.
  2. The persistence.xml in WEB-INF/classes/META-INF tells Amber to create a persistence-unit named "example".
  3. The "example" persistence-unit contains a class example.CourseBean
  4. Amber enhances example.CourseBean as a persistent object.

By the end of initialization time, Amber has enhanced CourseBean and made it available to the application in the persistence-unit "example".

A servlet will then lookup the CourseBean with the following procedure:

  1. Obtain an EntityManager for the persistence unit either using WebBeans @In (or @Named) or using the @PersistenceUnit injection annotation.
  2. Use the EntityManager to find the instance of the bean.

Persistent Object Implementation

The minimal Java class needs the following annotations to produce a maintainable persistent object:

  • A annotation to mark the class as persistent.
  • An annotation naming the relational table for the class.
  • An annotation to mark the primary key and any auto-generation capability.
  • Annotations to mark each persistent field.
  • Annotations naming the columns for the table.

The following code shows the Amber annotations for the course entity. As a quick comparison with the minimal Java class shows, Amber is close to the simplest possible implementation of the Java model which provides the necessary annotations in the list.

Example: Course.java
package example;

import javax.persistence.*;

@Entity
@Table(name="amber_basic_course")
public class Course {
  @Id
  @Column(name="id")
  @GeneratedValue
  private int _id;

  @Basic
  @Column(name="course")
  private String _course;

  @Basic
  @Column(name="teacher")
  private String _teacher;

  public String course()
  {
    return _course;
  }

  public String teacher()
  {
    return _teacher;
  }
}

The example uses the course() and teacher() methods to emphasize that the field accesses to _course and _teacher are live, i.e. they read and write the database values directly. (Under the covers, Amber uses bytecode enhancement to make this work.)

@Entity - marking the class as persistent

Course uses the @Entity to mark the Java class as a field-based persistent object.

@javax.persistence.Entity declares a Java class as an entity bean.

Since the @Id annotation marks a field, the bean's fields are persistent. Only the bean itself or its children may access the fields. Other classes must use the bean's methods, like getters or setters, to indirectly access the fields.

If the @Id annotation had marked a property method, the methods would be enhanced.

@Table - naming the table

@javax.persistence.Table specifies the SQL database table name to be used. If @Table is unspecified, Amber will use the class name as the table name.

Example: @Table
@javax.persistence.Table(name="amber_basic_course")

@Id - marking the primary key

The @Id attribute marks the bean's primary key. The EntityManager.find method looks up a bean instance with the primary key, and relations use the primary key to link beans together.

Example: key configuration
@Id
@Column(name="id")
@GeneratedValue
private int _id;

The optional @GeneratedValue specifies automatic generation of primary keys when beans are created. The default strategy AUTO generates primary keys depending on the database. Postgres, for example, will use a SEQUENCE, while Resin's built-in database will use an auto_increment IDENTITY.

The optional @Column annotation specifies the SQL column name. The default SQL column for an @Id is the property name.

@Basic - marking a persistent field

The @Basic attribute marks a basic data column like a string or integer or double.

Example: data column definition
@Basic 
@Column (name="course")
private String _course;

@Column - naming the column

The optional @Column annotation specifies SQL column name. For a @Basic field, the default column name is the field name.

Creating an entry

With Amber, you create a new database row by creating the model object and then calling persist() on the EntityManager. The EntityManager is the main facade for creating, querying and removing JPA objects. Normally, the application will use injection to get an EntityManager.

Example: Creating an instance
import javax.persistence.EntityManager;
import javax.webbeans.In;

public class MyServlet {
  @In EntityManager _manager;

  public void create()
  {
    _manager.getTransaction().begin();
    try {
      _manager.persist(new Course("Potions", "Severus Snape"));
    } finally {
      _manager.getTransaction().commit();
    }
  }
}

As mentioned above, the servlet gets its EntityManager using WebBeans injection annotated by @In since there is only one EntityManager in the application.

The persist() must be enclosed in a transaction, as required by the JPA specification. The transaction ensures that the database update is safe and consistent. In this example, we've used an explicit transaction. Many applications will use session beans to handle the transactions automatically.

Finding a row by the primary key

Once the Course is in the database, we can read it by its primary key using the EntityManagerfind() method.

Example: Find by primary key
import javax.persistence.EntityManager;
import javax.webbeans.In;

public class MyServlet {
  @In EntityManager _manager;

  public void find(PrintWriter out)
  {
    Course course = _manager.find(Course.class, 1);

    out.println(course.course + " " + course.teacher());
  }
}

Notice that the read doesn't require a transaction. In fact, Amber generally generally caches reads and only accesses the database when the cache expires or the data changes.

Of course, the find method is generally only useful if you already know the primary key. Usually, your application will need to query the database using some other criteria, like the name of the Course.

Example: Querying the data

The client servlet queries the database for all courses and lists them. It uses the EntityManager API to create a Query and uses the Query to obtain the results.

CourseServlet.java
import javax.persistence.*;
import javax.webbeans.In;

public class CourseServlet extends HttpServlet {
  @In
  private EntityManager _manager;

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

    res.setContentType("text/html");

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

    Query query = _manager.createQuery("SELECT o FROM Course o");
    
    for (Course course : (List<Course>) query.listResults()) {
      out.println("course: " + course.course() + "<br>");
      out.println("teacher: " + course.teacher() + "<br>");
      out.println("<br>");
    }
  }
}
<h3>Course Details</h3>
course: Potions
instructor: Severus Snape

course: Transfiguration
instructor: Minerva McGonagall

EntityManager

EntityManager is the primary interface for finding, querying, adding and deleting persistent beans. You can use the WebBeans @In annotation or use the older @PersistenceContext annotation.

@PersistenceContext(name="example")

Query

Query acts like a PreparedStatement in JDBC. It saves a parsed SQL query and allows for parameters.

Query query = _manager.createQuery("SELECT o FROM Course o");

The SQL used for EJB 3.0 is an enhanced database SQL. The query can return objects directly ("SELECT o") and it can traverse relations ("o.next.data"). In most other respects, it can be thought of as regular SQL.

List list = query.listResults();

The query returns its values with listResults(). Queries which return a single value can use getSingleResult().

Removing an entry

Finally, the application will use remove() in the EntityManager object to remove the entry.

Example: Removing an instance
import javax.persistence.EntityManager;
import javax.webbeans.In;

public class MyServlet {
  @In EntityManager _manager;

  public void remove(Course course)
  {
    _manager.getTransaction().begin();
    try {
      _manager.remove(course);
    } finally {
      _manager.getTransaction().commit();
    }
  }
}

The remove() must be enclosed in a transaction, just like the persist() call. Again, the transaction ensures that the database update is safe and consistent.

Resin Configuration

The Resin configuration is fairly straightforward. Resin needs to start the ejb-server, configure the JDBC data-source, and list the beans that will be used.

Example: WEB-INF/resin-web.xml
<web-app>
  <!-- server configuration -->
  <persistence-manager data-source="jdbc/resin"/>

  <servlet servlet-name="basic" servlet-class="example.CourseServlet"/>

  <servlet-mapping url-pattern="/basic" servlet-name="basic"/>
</web-app>

The <persistence-manager> configures Amber support.

persistence.xml

The persistence.xml lives in META-INF/persistence.xml. Since we're developing in WEB-INF/classes, the file will be in WEB-INF/classes/persistence.xml.

WEB-INF/classes/META-INF/persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
  <persistence-unit name="example">
    <class>example.CourseBean</class>

    <exclude-unlisted-classes/>
  </persistence-unit>
</persistence>

Demo


Copyright © 1998-2008 Caucho Technology, Inc. All rights reserved.
Resin ® is a registered trademark, and Quercustm, Ambertm, and Hessiantm are trademarks of Caucho Technology.