Packaging entities (EJB 3)

Can’t you package EJB 3 entities in the same way? Afraid not. We’re sure you’ve noticed that while session and message-driven beans share a lot of characteristics, entities are quite another beast. You may remember from our discussion in topic 1 that JPA can be used directly from the web container. That means entities will need some additional care and feeding with respect to packaging, so that deployment will work as expected.

This section covers some new deployment files, persistence.xml and orm.xml, and provides a slew of tips and information on how to position your entities for maximum deployment enjoyment. You do want deployment enjoyment, don’t you? We know we do. Let’s begin by looking at the packaging structure for entities.

Exposing the persistence module

With EJB 3, entities can be used inside either the EJB or web container, or in a Java SE application. Thus, entities may be packaged in a standard Java EE module such as an EJB-JAR, WAR, or JAR file in the root of the EAR module or as a library module in an EAR. When using entities in your applications, you have to package entity classes into a Java EE module such as an EJB-JAR or WAR of simple JAR files, with a simple deployment descriptor named persistence.xml.

If you are using entities within an EJB module, then the EJB (session beans, MDBs) classes and entities need to be packaged together in the same EJB module. Therefore, the EJB-JAR module will contain a persistence.xml file to designate that the module contains one or more persistence units. Recall from our discussion in topic 9 that a persistence unit is a logical group of entities used together. For example, you may create a persistence unit for all entities in the ActionBazaar application.


Let’s look at the structure of a JAR that contains a simple persistence unit, as shown in listing 11.5.

Listing 11.5 Structure of a sample EJB-JAR file containing entities

Listing 11.5 Structure of a sample EJB-JAR file containing entitiesListing 11.5 Structure of a sample EJB-JAR file containing entities

persistence.xml is the deployment descriptor for a persistence module, which is discussed in the next section. The orm.xml O file defines the object-relational mapping (if you use XML mapping). You may package an additional mapping file C that defines O/R mapping for entities that was not defined in orm.xml. We discuss O/R mapping with XML in section 11.5.2. The JAR also contains entity classes—Category.class and Item.class—and another class, BazaarAdmin.class, that is needed in order to make persistence work. Now that you know the structure of a persistence module, let’s drill down and learn more about persistence.xml.

Describing the persistence module with persistence.xml

In topic 9 we showed you how to group entities as a persistence unit and how to configure that unit using persistence.xml. Now that you know how to package entities, it’s time to learn more about persistence.xml, the descriptor that transforms any JAR module into a persistence module. It’s worth mentioning that per-sistence.xml is the only mandatory deployment descriptor that you have to deal with. We hope the Java EE specification will ease this requirement in future releases of the specification.

At the time of this writing, some EJB containers such as Oracle and JBoss support proprietary extensions of persistence modules without persistence.xml in EJB-JAR modules. Although user-friendly, this feature will not be portable across EJB 3 containers. You can find the schema for persistence.xml online at http:// java.sun.com/xml/ns/persistence/persistence_1_0.xsd.

Listing 11.6 is an example of a simple persistence.xml that we can use with the ActionBazaar application; it should successfully deploy to any Java EE 5 container that supports JPA. The first thing the file does is define a persistence unit and package it to your deployment archive—for example, WAR or EJB-JAR.

Listing 11.6 An example persistence.xml

Listing 11.6 An example persistence.xmlListing 11.6 An example persistence.xml

Let’s run through a quick review of the code before we drill down into the details. We define a persistence unit by using the persistence-unit element O. We can specify an optional factory class for the persistence provider The JPA provider connects to the database to store retrieved entities; we specified the data source for the persistence provider d. If you have multiple persistence units in a single archive, you may want to identify the entity classes that comprise the persistence unit ©. Optionally, you can specify vendor-specific configuration using the properties element f.

We hope you’re ready for a detailed exploration on the use of each of the elements in persistence.xml from listing 11.6—you’ll find it pretty straightforward. After reading the next few pages, you should be fairly comfortable with this new part of the EJB standard.

Naming the persistence unit

Each persistence unit must have a name, and that name must be unique across the Java EE module. The name is important because the container uses it to create an entity manager factory, and then again to create the entity manager instances using the factory to access the entities specified inside the unit. Also, you access the persistence unit by using its name when you attempt to perform CRUD operations with the entities packaged in the module. All other elements in a persistence unit can be defaulted.

You can define more than one persistence unit for an application module in persistence.xml. All entities identified in a persistence unit are managed by a single set of entity instances. Thus, a persistence.xml may have multiple persistence units in a particular JAR module as follows:

tmp6126_thumb

Persistence unit scoping

You can define a persistence unit in a WAR, EJB-JAR, or JAR at the EAR level. If you define a persistence unit in a module, it is only visible to that specific module. However, if you define the unit by placing a JAR file in the lib directory of the EAR, the persistence unit will automatically be visible to all modules in the EAR. For this to work, you must remember the restriction that if the same name is used by a persistence unit in the EAR level and at the module level, the persistence unit in the module level will win.

Assume you have an EAR file structure like this:

tmp6127_thumbtmp6128_thumbhas a persistence unit with the nametmp6129_thumb

tmp6130_thumbhas also a persistence unit with the name tmp6131_thumb

The actionBazaar persistence unit is automatically visible to the web module, and you can use as follows:

tmp6132_thumb

However, if you use this code in the EJB module, the local persistence unit will be accessed because the local persistence unit has precedence. If you want to access the persistence unit defined at the EAR level, you have to reference it with the specific name as follows:

tmp6133_thumb

Again, the name element is important because it is what you use to access the entities. As shown in topic 9, we use unitName to inject a container-managed Entity-Manager as follows:

tmp6134_thumb

Refer to the sidebar "Persistence unit scoping" for more on how a persistence unit is scoped depending on its presence.

Specifying the transaction type

You can specify transaction-type in persistence.xml (as in listing 11.6) by using the transaction-type attribute. transaction-type can either be JTA or RESOURCE_LOCAL. If you do not specify transaction-type, the container will assume the default transaction-type is JTA. You must utilize JTA as the transaction-type for a persistence unit packaged in a Java EE module. RESOURCE_LOCAL should be specified as a transaction type only when you’re exercising JPA outside a Java EE container. As you may recall, we discussed the javax.persis-tence.EntityTransaction interface in topic 9; we recommend you avail yourself of EntityTransaction only when you use EJB 3 persistence outside of a Java EE environment.

Using a specific persistence provider

The provider element specifies the factory class of the EJB 3 persistence provider, such as Hibernate or TopLink. You do not have to specify the persistence provider if you’re using the default persistence provider integrated with your Java EE 5 container. For example, if you want Hibernate’s persistence provider in the JBoss Application Server or TopLink Essentials persistence provider with Sun GlassFish or the Oracle Application Server, you don’t have to define the provider element in persistence.xml. But if you decide to go with the EJB 3 persistence provider from the GlassFish project with either JBoss or Apache Geronimo, then you must specify the provider element as follows:

tmp6135_thumb1

Obviously this example specifies Oracle TopLink as the persistence provider; you can specify the provider element for Hibernate as follows:

tmp6136_thumb1

This is helpful when using JPA outside the container.

Setting up a DataSource

Our entities are persistence objects that access databases. topics 7 through 10 discussed how O/R mappings are defined with metadata annotations, and how an entity interacts with one or more database tables. We have not, however, broached the subject of how entities interact with a database connection. Back in topics 3 and 4 we briefly discussed what a DataSource is and how it can be used in an application server by accessing it through JNDI. In addition, you saw examples of session and message-driven beans accessing a DataSource using resource injection. In spite of this, entities cannot use injection, connect to the database themselves, or perform any operation directly; the persistence provider does all that magic behind the scenes. When you persist an instance of an entity, the persistence provider will open or reuse a pooled connection to the database and execute the SQL on your behalf.

To configure a persistence unit to connect to a database, you first have to create a DataSource in your Java EE container. For scalability, each DataSource is commonly associated with a connection pool, and the connection pool contains the information for connecting to the database.

Configuring an application DataSource

Every Java EE application server provides the ability to create and manage Data-Sources and connection pools. Here is an example of a DataSource and a connection pool used by Sun’s GlassFish open source project:

tmp6137_thumb1

The DataSource uses the JNDI name and connection pool information for the specified database instance. In this example, the DataSource has a jndi-name of jdbc/ActionBazaarDS. Two common naming techniques are to name the pool either the DataSource name without the JNDI reference (ActionBazaarDS), or to use the pool in the DataSource name (ActionBazaarPooledDS). We’ll illustrate the first approach here.

Telling the persistence unit about the DataSource

You can specify the DataSource for a persistence unit using either the jta-data-source or non-jta-data-source element in the persistence.xml (as we did in listing 11.6). Typically, Java EE containers support two types of DataSources: Java Transaction API (JTA) and non-JTA. A JTA (or global) DataSource is one that supports JTA or distributed transactions. A non-JTA (or local) DataSource only supports local transactions that are limited to the process/server where they begin. For example, we can specify the name of the JTA DataSource we created earlier using the jta-data-source element as follows:

tmp6138_thumb1

NOTE You have to specify global JNDI names for the data source in the jta-data-source and non-jta-data-source elements of persistence. xml. If you do not specify a DataSource for the persistence unit, the persistence unit will try to use the default DataSource for the application server. The default DataSource for a Java EE application is typically specified using a proprietary mechanism.

Many application servers such as BEA WebLogic Server and Oracle Application Server also allow the packaging of DataSource configurations in an EAR.

Identifying entity classes

If you are using JPA within a Java EE container, the persistence provider reads the module and determines which entity classes are annotated with the @Entity annotation. You can identify the entity classes that constitute a persistence unit (as we did in listing 11.6). This is useful when you want to divide the packaged entities into more than one persistence unit as follows:

tmp6139_thumb1tmp6140_thumb1

Packaging at this more granular level may seem like more work at first glance. In reality, it makes sharing the persistence units across applications much easier.

Specifying vendor-specific extensions

Most JPA providers will provide extensions such as caching, logging, and automatic table creation. You can use the property element in persistence.xml to specify these vendor-specific extensions. The persistence provider will read such configurations while creating the entity manager factory and configure the persistence unit accordingly.

In listing 11.6 we enabled automatic schema generation for the persistence unit when using TopLink:

tmp6141_thumb

Remember that automatic schema generation is a developer-friendly feature and, when it’s turned on, the JPA provider creates the underlying database schema (tables, sequences, etc.) when the persistence unit is deployed. If you want to turn on automatic schema generation for Hibernate, you can do so by adding the following in persistence.xml:

tmp6142_thumb

Similarly you can pass configuration parameters for caching, logging, and JDBC configuration when using an outside container as a property. Check your vendor documentation for details.

Specifying additional mapping and JAR files

There may be times when you want to use multiple O/R mapping files for your project. Doing this supports the packaging of smaller functional units into separate JAR files to allow a more granular deployment scheme. Of course, regardless of how many JARs make up your application, they will all need to be in the classpath of the application in order for all the components to be found by the class loader.

For example, if you have mapping information in a separate XML file named secondORMap.xml, you can specify as much by using the mapping-file element that we saw in listing 11.6. It is vital to remember that additional mapping files are not packaged in the meta-inf directory of the persistence module, but also that these files may be packaged inside the JAR as a resource (as shown in listing 11.5).

You can include additional JAR files (such as ShippingEntities.jar in listing 11.6). The JAR file location is relative to the persistence module; that is, the JAR file that contains the persistence.xml.

Performing O/R mapping with orm.xml

Topic 8 discussed how to perform O/R mapping using metadata annotations. Believe it or not, for a large application the use of O/R mapping metadata within the code is not a good idea. Using O/R mapping annotations hardwires your relational schema to your object model. Some folks feel it’s perfectly okay to hard-code schema information, because they see it as being similar to JDBC. Others consider it a very bad idea. It is also quite possible that for certain projects you may be directed to implement O/R mapping with an XML file. As mentioned earlier (in listing 11.1), you can specify O/R mapping information in a file named orm.xml packaged in the META-INF directory of the persistence module, or in a separate file packaged as a resource and defined in persistence.xml with the mapping-file element.

The source that takes precedence is always the deployment descriptor. EJB 3 persistence specifies that the deployment descriptor can override O/R mapping specified using annotations, the orm.xml file, or any other XML mapping. Listing 11.7 shows an example of an O/R mapping file (orm.xml) used in ActionBazaar.

Listing 11.7 An orm.xml that specifies default values for a persistence unit and O/R H mapping information

Listing 11.7 An orm.xml that specifies default values for a persistence unit and O/R H mapping information

Listing 11.7 An orm.xml that specifies default values for a persistence unit and O/R H mapping information

The orm.xml file defines the actual O/R mapping with XML for the entities packaged in an EAR. Listing 11.7 O shows how to define defaults for a persistence unit using the persistence-unit-defaults element. This element defines schema, catalog, and access, default entity listeners, and cascade type. We mentioned schema and catalog types in topic 8 when we discussed the @Table and @SecondaryTable annotations. You can define the default values for the schema and catalog type in persistence-unit-defaults, and this can be overridden by each entity.

The access type may either be FIELD or PROPERTY.

In topic 9 you learned that entity listeners can be defined to handle lifecy-cle callbacks for entities, and that a default listener for all entities in a persistence module can be defined by using the entity-listeners subelement in persistence-unit-defaults Q. Use @ExcludeDefaultListener on the entity or a mapped superclass if you need to exclude the default entity listener. The name element d identifies the name of the entity and is the equivalent of the name in @Entity. This value is used in the from clause in JPQL queries.

The other O/R mapping elements in orm.xml are somewhat self-explanatory, and we won’t discuss them in detail.

Table 11.5 lists the one-to-one mapping between the most often used annotations and their associated deployment descriptors. You’ll probably notice immediately that the XML element is usually quite similar to its annotation cousin.

Table 11.5 Mapping of persistence annotations to associated deployment descriptor elements

Annotations Grouped by Type

XML Element

Object type

tmp6-145 tmp6-146
tmp6-147 tmp6-148
tmp6-149 tmp6-150
tmp6-151 tmp6-152

Table mapping

tmp6-153
tmp6-154 tmp6-155
tmp6-156 tmp6-157

Annotations Grouped by Type

XML Element

Query

tmp6-158 tmp6-159
tmp6-160 tmp6-161
tmp6-162 tmp6-163

Primary key and column mapping

tmp6-164 tmp6-165
tmp6-166 tmp6-167
tmp6-168 tmp6-169
tmp6-170 tmp6-171
tmp6-172 tmp6-173
tmp6-174 tmp6-175
tmp6-176 tmp6-177
tmp6-178 tmp6-179

Relationship mapping

tmp6-180 tmp6-181
tmp6-182 tmp6-183
tmp6-184 tmp6-185
tmp6-186 tmp6-187
tmp6-188 tmp6-189
tmp6-190 tmp6-191
tmp6-192 tmp6-193

Listeners

tmp6-194 tmp6-195
tmp6-196 tmp6-197
tmp6-198 tmp6-199

Annotations Grouped by Type

XML Element

Listeners

tmp6-200 tmp6-201
tmp6-202 tmp6-203
tmp6-204 tmp6-205
tmp6-206 tmp6-207
tmp6-208 tmp6-209
tmp6-210 tmp6-211

 

Manually performing O/R mapping using XML can be quite arduous, error-prone, and difficult to troubleshoot. You may want to investigate tools that will assist with this effort.

NOTE The goal of the Eclipse Dali project (http://wiki.eclipse.org/index.php/ Dali_Project) is to provide developer support for O/R mapping of EJB 3 persistence objects, and help generate O/R mappings with annotations and XML descriptors.

Our trek through packaging EJB 3 is nearing the end. Before we finish, we want to highlight some things you should keep in mind when packaging your shiny new EJB 3 applications.

Next post:

Previous post: