Introduction
The EJB 3.0 specification, available from Sun
Microsystems introduces many changes to the way entity beans are
coded by an application developper. This article provides a brief
overview of how to code EJB 3.0 entity beans. The target audience
is for experienced EJB 2.1 or EJB 3.0 developers who would like
a refresher of EJB 3.0 entity beans.
The EJB 3.0 API is part of the "Persistence API".
The specification document called "JSR 220: Enterprise JavaBeansTM,Version
3.0, Java Persistence API" is available from java.sun.com. This
document clearly states that the persistence API can be used outside
J2EE containers. Hence we can expect web applications as well as
fully fledged JEE5 application to make use of the facilities offered
in the EJB 3.0 specification.
The Entity Class
The entity class is annotated with @Entity. If the
bean data is to be available remotely, the class must implement
java.io.Serializable.
Transient Fields
Fields that are not persisted should be annotated
as @Transient.
Entity Fields
The fields should be declared "private" (but can
be protected). Bean accessor methods are used to get and set the
fields.
The field datatypes worth noting:
- Java primitive types (int, short, float....)
- Primitive type wrappers, including java.sql.Date, java.sql.Timestamp
and java.sql.Time.
- String
- enums
- other entity types or collections of entity types.
Collections of entities use the java.util.collection interfaces:
- java.util.Collection
- java.util.List
- java.util.Map
It is usaually a good idea to use the java generic interfaces when a
field is a collection.
Primary Key(s)
Ideally the entity has a single field that represents
the primary key. A primary key can be one of the following types:
- Primitve type
- Primitive wrapper class
- java.sql.Date or java.util.Date
A simple primary key is annotated with @Id, on the getter.
Composite keys should be enclapsulated in a java class. Such a class
must implement java.io.Serializable and have the following methods:
- public no-arg constructor
- if no getters and setters are used, the properties must be public
or protected.
- equals() and hashcode() methods must be implemented.
Composite keys are annotated with @EmbeddedId or @IdClass
Accessor Methods
The entity field get and set can have some code
added, for example to perform some validation, however, it should
be noted that this is not reccomended, as the order in which these
methods are called is not defined.
Entity Relationships
An entity's field (which is another entity class,
or collection of entities) can be annotated, on the getter, as:
- @OneToOne
- @OneToMany
- @ManyToOne
- @ManyToMany
The reverse path from the entity to the owning one (bidirectional relationship)
uses the same annotations as above, with the mappedBy element which
names the field name in the owning entity. The reverse path is
not always there (unidirectional relationship).
Lifecycle Callabcks
Some callback methods can be coded in an entity,
and annotated so that the container can determine that they exist.
The annotations are:
@PrePersist: the method
can be used to validate the data and throw an exception is there
is an error.
@PostPersist: any generated primary key values are
available
@PreRemove
@PostRemove
@PreUpdate
@PostUpdate
@PostLoad
The callback method is defined in the entity as
void methodname().
It is possible to code callbacks in listener
classes, in which case the callback is of the form:
void methodname(Object
entityForCallback)
The entity listener class is annotated on the Entity
class:
@EntityListeners(MyListenerClassName.class)
Using Entities
The interface javax.presistence.EntityManager is
used to interact with the database, and work with entity EJB's.
An Entity Manager instance is declared with the
@PersistenceContext annotation:
@PersistenceContext EntityManager em;
Once declared it can be used to perform operations on the database,
queries can be entered in eirther native SQL or EJB sql.
The transaction can span one or more database opterations, the behaviour
is determined at the time the EntityManager is created.
public enum PersistenceContextType {
TRANSACTION,
EXTENDED
}
The EXTENDED value means that the transaction is not commited until
the EntityManager is closed. The annotation used to define an EXTENDED
transaction entity manager is:
@PersistenceContext(type=PersistenceContextType.EXTENDED)
EntityManager em;
Entity managers can also be created using a JNDI lookup.
To the unitiated , getting entity managers to work with EJB's can be
fraught with problems. The following has been found to work using
JBoss and MySQL:
In the stateless
EJB class that wants to access the entity EJB:
@PersistenceContext(unitName="shoestringPU")
EntityManager em;
The EJB's are packaged in an archive. In the META-INF folder of the
archive is a file called persistence.xml, its contents are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<persistence>
<persistence-unit name="shoestringPU">
<jta-data-source>java:/ShoestringDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
</properties>
</persistence-unit>
</persistence>
A datasource has been defined in JBoss called ShoestringDS. It's done
by deploying the shoestring-ds.xml file in the deploy folder of
the default server. The contents of this file are below:
<datasources>
<local-tx-datasource>
<jndi-name>ShoestringDS</jndi-name>
<connection-url>jdbc:mysql://192.168.0.6:3306/shoestring</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>user</user-name>
<password>password</password>
</local-tx-datasource>
</datasources>
The jdbc driver for MySQL has been copied to the lib folder of the default
server folder in JBoss.
Queries
The EntitiyManager interface supplies several query
methods which return a javax.presistence.Query instance. In essence
three types of queries can be defined:
- Named Query: the query is defined in the entity class' annotation,
the query can be ejb ql or sql.
@NamedQuery(name="queryName", query="ejb ql query")
- Native Query: the query uses sql native to the database
- Query: the query uses EJB QL.
Note: In most cases it is a good idea to use
EJB QL queries. Native sql queries inevitably end up making
use of something which is not only specific to the DBMs being
used, but also the the particular database naming scheme/layout.
It is therefore more likely to be difficult to code against
a test database and move to the live database (and vice versa)
without any effort.
Mapping Java Names To DBMS Names
An entity class is mapped to a database table. The
@Entity annotation does this. Optionally the @Table() annotation
can be used to specify the table name, catalog, schema and other
database specific details.
A class property is mapped to a database column.
Unless the column name in the database is not the same as the column
name, no annotation is necessary. Otherwise the @Column(name="dbms
column name") annotation can precede the PropertyType getMyPropertyName() method.
For primary keys, the Primary key getter is preceded
by @Id. If the primary key is generated, then @Id(generate = GeneratorType.AUTO)
is entered.
More details and very good examples can be found
the the "JSR 220: Enterprise JavaBeansTM,Version 3.0,
Java Persistence API" document available from java.sun.com. Chapter
9, "Metadata For Object/Relational Mapping".
|