- A Closer Look at Entity Beans
- Developing a CMP Bean
- Developing a BMP Bean
- Advanced Container-Managed Persistence
- Transactions
- Container Security
- Design Guidelines
- In Practice
- Summary
Advanced Container-Managed Persistence
Using the EJB 1.1 specifications, container managed persistence beans were not very powerful. In most cases, bean developers went quickly beyond the capabilities of CMP and had to convert to use bean managed persistence. Many developers shied away from using entity beans altogether. The EJB 2.0 quickly addressed many of these issues. This now allows the bean developer to implement most persistence requirements using container managed persistence. The four features that facilitate this are the following:
EJB query language
Finder methods
Relationship mapping of multiple entity beans
Business methods implemented within the home interface
The Enterprise JavaBeans Query Language
The Enterprise JavaBeans Query Language (EJB QL) specification defines a query language to be used within finder methods of an entity bean with container managed persistence. Rather than develop a completely new query language, the specification is based on a subset of SQL92. The EJB QL gives you the features to allow navigation over the relationships defined in an entity bean's abstract schema.
Your EJB QL queries will be contained within your deployment descriptor of the entity bean. The container will then translate these queries into the target language of the underlying data store. For example, EJB QL will be translated to true SQL for JDatastore. Using this translation allows entity beans using container managed persistence to be portable. In other words, the code is not tied to a specific type of data store or application server vendor.
EJB Query Restrictions
EJB QL has a few restrictions:
Commenting is not supported within EJB QL.
Date and time values are always represented in milliseconds and contained within a Java long.
Container managed persistence does not support inheritance. In other words, you cannot compare two entity beans of different types with each other.
Query Syntax
An EJB QL query has three components associated with it: SELECT, FROM, and WHERE. The SELECT and FROM clauses are the only required portions of EJB QL; the WHERE clause is optional. For example, SELECT OBJECT(e) FROM Employee e selects all the instances contained within the Employee entity.
The SELECT component defines the values or objects that will be returned by the created query. The return values can either be a local interface or a remote interface contained within an entity. The FROM component defines the scope of the query. It may declare one or more entities. Finally, the WHERE component contains a conditional expression that restricts the values retrieved by the query. As mentioned earlier, this portion of the query is optional; it is normally required to meet most entity finder requirements.
Examples Described
SELECT OBJECT(e) FROM Employee e
This method returns all the instances of employee contained within the entity. In this case, we refer to the Employee entity with an alias e to make the definition of the query shorter.
SELECT DISTINCT OBJECT(e) FROM Employee e WHERE e.empNo = ?1
The query returns an employee instance specified by an empNo. This query is typically used to find an instance by either a primary key or alternate key. The DISTINCT keyword can be used to remove duplicates from the query but this should not happen if you are using a primary key.
SELECT DISTINCT OBJECT(e) FROM Employee e, IN (e.salaryHistory)
as s WHERE s.Country
= ?1
This query navigates to related beans using the defined relationship. The e variable relates to the Employee entity bean, and the s variable represents the related bean instances SalaryHistory. The IN keyword specifies that SalaryHistory is a collection of beans related to the Employee entity. The query then navigates the relationship to identify all the salary histories within a specific country.
SELECT DISTINCT OBJECT(e) FROM Employee e WHERE e.salary BETWEEN ?1 AND ?2
The employees whose salaries fall within a given range are returned. The BETWEEN keyword returns all salaries within the given range. This expression is useful when using range finder methods.
Expressions and Functions
Just like SQL92 specifications of which EJB QL specifications is a subset, EJB QL describes the functionality of the expressions and functions. The specification does not include a full list of functions like that of SQL92 but simply a subset that should fit most requirements (see Table 23.5).
Table 23.5 Expressions and Functions Contained Within EJB QL
Type |
Description |
Standard comparisons |
Just like SQL, you have the operations =, >, >=, <, <=, and <>. |
Arithmetic |
+, -, *, / |
Logical |
NOT, AND, OR |
Between |
The BETWEEN expression determines whether a value falls within a range. |
In |
The IN expression determines whether a value is contained within a given set. |
Like |
The LIKE expression evaluates whether a string matches a pattern. For a single character wildcard, use _. To use a wildcard that represents zero or more characters, use %. |
Null |
The NULL comparison expression evaluates whether an attribute or set is null. This is not used to evaluate a null collection; we use the EMPTY expression to evaluate collections. |
EJB QL also includes several functions, both arithmetic and string, listed in Table 23.6. Most EJB vendors, including Borland's Application Server, allow you to define and implement your own custom functions.
Table 23.6 Functional Expressions
Function Syntax |
Description |
CONCAT(String, String) |
Concatenate two strings together and return into a third. |
SUBSTRING(String, start, length) |
Return a substring based on the start and length passed in. |
LOCATE(String, String [, start]) |
Locate a string within another and return its location. |
LENGTH(String) |
Return the length of a string. |
ABS(number) |
Return the absolute version of the number. |
SQRT(double) |
Return the square root of a number. |
Finder Methods
The ability to find an entity is a key requirement of all entity beans. The implementation of finder methods, within EJB specification 1.1, required us to quickly move to bean managed persistence rather than use the simpler container managed persistence beans. Using EJB QL in conjunction with finder methods within a container managed persistent bean, you can meet most requirements for finding an individual or collection of entities.
We'll continue to look at our Employee entity and add a more complex finder other than findByPrimaryKey(). The requirement we need to implement is the capability to find employees based on a salary range. The following steps are required to build this finder method implementation:
-
Right-click on the Employee entity and choose Add, Finder. The results of which display the finder editor.
-
Add the finder properties required to accomplish the given task.
-
Set the following properties (see Figure 23.6):
Figure
23.6 Finder editor within the EJB Designer.
-
Method name: findBySalaryRange
-
Return type: java.util.Collection
-
Input parameters: int low, int high
-
Home interface: local home
-
Query: SELECT OBJECT(e) FROM Employee e WHERE e.Salary BETWEEN ?1 AND ?2
-
Save and compile your project.
The deployment descriptor now contains the information for this finder method (see Listing 23.18). When the bean is deployed to the container, it translates the information contained within the deployment descriptor EJB QL to standard SQL for JDatastore.
Listing 23.18 Portion of the Deployment Descriptor for Defining a Finder Method with EJB QL
<query> <query-method> <method-name>findBySalaryRange</method-name> <method-params> <method-param>int</method-param> <method-param>int</method-param> </method-params> </query-method> <ejb-ql>SELECT OBJECT(e) FROM Employee e WHERE e.Salary BETWEEN ?1 AND ?2</ejb-ql> </query>
Relationship Mapping
As stated earlier the introduction of relationships in EJB 2.0 specifications makes a huge difference. These relationship mappings allow for not only providing the mechanics for managing relationships, but also the capability to model and implement complex business relationships with unidirectional and bidirectional traversal.
Each entity bean typically is not isolated in itself; it relates to other entities within our system. For example, the Employee entity we have been working with relates to many other entities within our system such as the salary history. To demonstrate relationships, we will start with one-to-many and move to more complex relationships, such as many-to-many.
One-to-Many Relationships
We can use a simple example for a one-to-many relationship. Using the sample database provided with JBuilder, each employee has a lengthy salary history that we want to make available through the instance (see Figure 23.7).
Figure 23.7 Model of employee to salary history.The process for creating this one-to-many relationship within JBuilder is accomplished by using the EJB Designer. The relationship ultimately is exposed via a local interface defined on the Employee entity bean. Ultimately, you would access this local interface by way of a session façade bean, but to make the example simple, we will use the remote interface with a test client.
-
Expand the EmployeeData source and verify that the SALARY_HISTORY table is contained within the source's definition.
-
Right-click on the SALARY_HISTORY table and select Create Entity Bean. The bean then appears in the EJB Designer. Make sure that the version property is set to EJB 2.0 compliant.
-
Creating an EJB relationship is accomplished by right-clicking on the parent entity. In this class, right-click on our Employee entity and select Add, Relationship. An arrow used to graphically represent our relationship appears within the EJB Designer.
-
Drag the end of the relationship arrow onto the SalaryHistory entity.
-
Select the newly created method that contains the relationship. The name assigned by JBuilder's EJB Designer is salaryHistory.
-
The Relationship Property editor appears to allow you to change the behavior of the relationship (see Figure 23.8).
Figure
23.8 Relationship Property editor for the Employee to SalaryHistory
relationship.
-
For the relationship properties, Multiplicity should be set to one-to-many, and Navigability should be set to unidirectional. The Navigability is set to unidirectional because it does not make sense to access Employee entity information through the SalaryHistory entity.
-
Click on the Edit Table Reference button to open the Table Reference editor (see Figure 23.9). We will use this editor to provide the link/keys between the two entities.
Figure
23.9 Table Reference editor for the Employee to SalaryHistory
relationship.
-
Adding a relationship is important because it indicates all the important keys that define the relationship between parent and child entities. Double-click on the empNo in the Employee entity and drag it onto the empNo of the SalaryHistory entity. Click OK.
-
You should have a new attribute that contains the relationship between the Employee and the employee's salary history. Using the property editor, shown in Figure 23.8, make sure that the return type of the salaryHistory method within the Employee entity is java.util.Collection.
Many-to-Many Relationships
A many-to-many relationship appears frequently in relationship database designs. It is usually implemented through the use of a resolution table because most relational database servers do not support this many-to-many relationship directly, although object modeling supports this relationship well. Although we can create this same resolution entity within our container, why not implement a many-to-many relationship that more accurately represents our actual instances?
For example, our employee system contains a many-to-many relationship implemented through a resolution table (see Figure 23.10).
Figure 23.10 Employee relational data model.Let's now create a many-to-many relationship using the existing employeeData source. The relationship will be exposed via local interfaces of the Employee and Project entity beans. To access these interfaces, we will use the local interfaces of the beans created. The following tasks help us create the required relationships and deployment descriptor for these entity beans:
-
Right-click on the PROJECT table within the defined data module within the EJB Designer and select Create CMP 2.0 Entity Bean.
-
Create the many-to-many relationship between the Employee and Project entity beans using the EJB Designer. This is accomplished by right-clicking on the Employee bean and selecting Add, Relationship; then drop the other end of the relationship on the Project entity.
-
Select the newly created relationship entity called Project, and the relationship editor is created (see Figure 23.11):
Figure
23.11 Relationship editor for a many-to-many relationship.
-
Relationship name: employee-project
-
Multiplicity: many to many
-
Navigability: bidirectional
-
Field name: project
-
Return type: java.util.Collection
-
Getters/setters: local
-
Click on Edit Table Reference; this launches the Table Reference editor.
-
Click on Add Cross Table (see Figure 23.12). After you make the appropriate selections, the Add Cross Table button becomes available.
-
Select the EMPLOYEE_PROJECT table as the cross table (see Figure 23.13).
-
Drag EMPLOYEE EMP_NO to the EMPLOYEE_PROJECT EMP_NO and drag EMPLOYEE_PROJECT PROJ_ID to PROJECT PROJ_ID (see Figure 23.14).
-
Click OK and compile and save your project.
Figure 23.12 Table Reference editor to establish a many-to-many relationship.
Figure 23.13 Table Reference editor to establish a many-to-many relationship using a cross table.
Figure 23.14 Build the relationship between the resolution table and the Employee and Project tables with the connected keys.
Home Business Methods
One of the new changes for the specification is the addition and support for collection methods and business methods contained within the home interface. For example, we might want to calculate the total current salary of all employees. The process for building this is simple:
-
Add a new method to the Employee entity bean calling it calculateTotalSalary.
-
Enter the return type to be a java.math.BigDecimal and the target interface should be local home.
-
Create another finder method with the following parameters:
-
Finder name: findAll
-
Return type: java.util.Collection
-
Parameter: leave empty
-
Method interface: local home
-
Query: SELECT OBJECT(e) FROM Employee AS e
-
Create a select method with the following properties:
-
Select name: selectAll
-
Return type: java.util.Collection
-
Parameter: leave empty
-
Result type mapping: local
-
Query: SELECT OBJECT(e) FROM Employee AS e
-
Most of the code for the Employee entity has been generated by the EJB Wizard. The piece we need to add is the implementation of CalculateTotalSalary.
-
Locate the EmployeeBean class and double-click to view the source.
-
Locate the ejbHomeCalculateTotalSalary() method in the class.
-
Implement the method as follows:
public java.math.BigDecimal ejbHomeCalculateTotalSalary() { float res = 0; try { java.util.Iterator iter = ejbSelectAll().iterator(); while(iter.hasNext()){ Employee emp = (Employee)iter.next(); res = res + emp.getSalary().floatValue(); } } catch (FinderException ex) { throw new javax.ejb.EJBException(ex); } return new java.math.BigDecimal(new Float(res).toString()); }