| ||
There are three types of EJBs that can be deployed: entity beans, session beans, and message-driven beans. In addition, one or more AC4J deployments, in which each one makes an EJB available to other AC4J objects, can be included.
In this section you'll look at the following:
The headers for each type of EJB
The common elements that can appear in any of the EJB deployments
Container-managed persistence elements for CMP entity beans
Java Expresso Machine (JEM) elements used to deploy EJBs as AC4J objects
For every session bean declared in ejb-jar.xml , you can specify a <session-deployment> element in orion-ejb-jar .xml with a JNDI location, environment entries, references to other EJBs, resource references, and other optional performance settings.
As an example, consider the following session bean definition in ejb-jar.xml :
<session> <description>Manages customers</description> <ejb-name>CustomerManager</ejb-name> <home>com.apress.ejb.CustomerManagerRemoteHome</home> <remote>com.apress.ejb.CustomerManagerRemote</remote> <local-home>com.apress.ejb.CustomerManagerHome</local-home> <local>com.apress. ejb.CustomerManager</local> <ejb-class>com.apress.ejb.CustomerManagerBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session>
You can map CustomerManager to an actual JNDI location and tweak a few performance settings in orion-ejb-jar.xml like this:
<session-deployment name=" CustomerManager " location= " store/ejb/CustomerManager " cache-timeout=120 min-instances=50 max-instances=200/> ... </session-deployment>
Environment entries, resource references, EJB references, and other subelements of session-deployment are common across EJB types and are described later in this chapter.
The <session-deployment> element allows the following attributes listed in Table 11-2.
Parameter | Description | Default Value |
---|---|---|
session-deployment: pool-cache-timeout | The maximum number of seconds to cache bean instances. When this number is reached, all beans of this type will be flushed from the pool. Specifying "never" or a nonpositive integer will disable timeouts so that instances will never be removed from the pool. | 60 |
session-deployment: call-timeout | The maximum number of milliseconds to wait for a resource (other than a database connection) before throwing a RemoteException . If set to 0, timeouts are disabled and it will wait forever. |
|
session-deployment: copy- by-value | Specifies whether all parameters should be cloned and serialized before being passed in to this bean's methods . If false, objects will be passed by reference, which is faster but may cause problems if the parameter is an object and you modify its state. | true |
session-deployment: location | The JNDI location in which to store this bean's Home . | |
session-deployment: max-instances | The maximum number of instances to keep cached in the pool. | 100 |
session-deployment: min-instances | The minimum number of instances to keep cached in the pool. |
|
session-deployment: max-tx-retries | The maximum number of times to retry transactions that are rolled back because of system failures. When a transaction involves multiple beans, the container will use the value specified for the first bean involved in the transaction. | 3 |
session-deployment:name | The name of the bean as specified in ejb-jar.xml . | |
session-deployment: persistence-filename | Path to the file where session state is stored across server restarts. (Note: the survival of session state across server restarts is a nonportable feature.) | |
session-deployment: timeout | Maximum number of seconds of inactivity before removing a stateful session bean. If zero or negative, stateful session beans are never removed by the container and must be explicitly removed by the application. | 1800 |
session-deployment: wrapper | Used by OC4J to store the name of the generated remote wrapper class. This value should not be edited. | |
session-deployment: local-wrapper | Used by OC4J to store the name of the generated local wrapper class. This value should not be edited. |
For every entity bean declared in the ejb-jar.xml , you can specify an <entity-deployment> element in orion-ejb-jar.xml . This is particularly relevant to entity beans that use CMP, since you'll need to map fields and finders to the corresponding database values, as shown in Table 11-3.
Parameter | Description | Default Value |
---|---|---|
entity-deployment: call-timeout | The maximum number of milliseconds to wait for a resource (other than a database connection) before throwing a RemoteException , or to wait for a SQL query to finish before throwing an SQLException . If set to 0, timeouts are disabled and it will wait forever. | 90000 ms |
entity-deployment: clustering-schema | Not necessary in this release | |
entity-deployment: copy-by-value | Specifies whether all parameters should be cloned or serialized before being passed in to this bean's methods. If false, objects will be passed by reference, which is faster but may cause problems if the parameter is an object and you modify its state. | true |
entity-deployment: exclusive-write-access | If true, the container assumes that it's the only process with write access to the database. This can provide performance boosts, but can ensure that data integrity should never be used when other processes might update the database. | false |
entity-deployment: do-select- before-insert | If true, the container will execute a select before every insert to determine if a record already exists with the same key. If false, it will just attempt the insert, possibly causing a SQLException . | true |
entity-deployment: instance-cache-timeout | The maximum number of seconds that instances are assigned to a particular identity (that is, one primary key) before being returned to the unassigned pool. Specifying a value of "never" means that instances will keep their identity until they are garbage collected. | 60 |
entity-deployment: location | The JNDI location in which to store this bean's Home . | |
entity-deployment: isolation | Sets the database isolation level to serializable, srepeatable_read , committed, uncommitted , or none (only serializable and committed should be used for Oracle databases). | committed |
entity-deployment: locking-mode | Sets the concurrency mode, which controls how to resolve resource contention between components . Possible values are as follows :
| OPTIMISTIC |
entity-deployment: max-instances | The maximum number of instances to keep cached in the pool. | 100 |
entity-deployment: min-instances | The minimum number of instances to keep cached in the pool. |
|
entity-deployment: max-instances-per-pk | The maximum number of instances to keep cached for a single primary key. | |
entity-deployment: min-instances-per-pk | The minimum number of instances to keep cached for a single primary key. | |
entity-deployment: max-tx-retries | The maximum number of times to retry transactions that are rolled back because of system failures. When a transaction involves multiple beans, the container will use the value specified for the first bean involved in the transaction. | 3 |
entity-deployment: disable-wrapper-cache | If true, bean instances aren't cached, but rather created on demand. | false |
entity-deployment:name | The name of the bean as specified in ejb-jar.xml . | |
entity-deployment:pool-cache-timeout | The maximum number of seconds to keep an unassigned entity instance before removing it from the pool. Specifying a value of "never" will disable the timeout. | 60 |
entity-deployment:validity-timeout | For read-only beans, specifies how often to reload data. Useful for cases where data very rarely changes, but needs to be occasionally refreshed. Specifying a value of "never" will disable the timeout so that data may never be reloaded. | |
entity-deployment:force-update | If true, the container will call ejbStore() (and write to the database in the case of CMP) after every invocation, even if no data changes are detected . If false, it will track changes and only write when necessary. | false |
entity-deployment:wrapper | Used by OC4J to store the name of the generated remote wrapper class. This value should not be edited. | |
entity-deployment: local-wrapper | Used by OC4J to store the name of the generated local wrapper class. This value should not be edited. | |
entity-deployment: delay-updates-until-commit | If true, updates aren't written to the database until the current transaction is committed. If false, every incremental update is passed through to the database. | true |
All entity beans share the same header information. However, CMP entity beans use a number of additional attributes in the header, and a number of additional child elements as well. The additional header elements are list in Table 11-4.
Parameter | Description | Default Value |
---|---|---|
entity-deployment: data-source | The JNDI name of a data source to use to access the database for this entity. The name specified here should match the ejb-location configured for the data source in the data-sources.xml file (see Chapter 5). | The value of orion-application:default-data-source in orion-application.xml for this application, or if that isn't specified, the same value in config/application.xml |
entity-deployment: update-changed-fields-only | If true, when an EJB is saved, only the modified fields will be updated. If false, all the EJB fields will be updated no matter what was changed. | true |
entity-deployment: table | The database table this entity should be saved to. | Generated by the server based on the EJB name, JAR name, and so on |
Within the main entity-deployment element, you must specify child elements for the following:
Mapping CMP fields to database columns (note that Primary Key fields must be listed separately from the rest of the CMP fields)
Specifying queries for finders, for EJB 1.1, or when EJB-QL isn't sufficient to specify the query for EJB 2.0 finders
Resolving container-managed relationships to database tables and columns
Caution | Many of the elements and attributes described in this section can be used for different things (for example, depending on whether they're mapping a CMP field or a CMR field). |
The primary key fields must be listed separately from the rest of the CMP fields, but the format is essentially the same. There's one primkey-mapping element that holds a single cmp-field-mapping element, which can hold one or more field mappings, using any of the syntaxes described in the following sections.
For example, for an Order EJB with a single primary key field called OrderId in the EJB mapping to a column named OrderId in the database, the mapping would look like this:
<primkey-mapping> <cmp-field-mapping name="orderId" persistence-name="OrderId" /> </primkey-mapping>
For an OrderLine EJB with a multicolumn primary key, you cannot list all the columns directly under the primkey-mapping element, since it only takes a single child. Instead, you'll use one of the following options to embed multiple CMP field definitions. For example, if the OrderLine primary key consists of OrderId and LineNumber properties in the EJB and OrderId and LineNumber columns in the database, the mapping would look like this:
<primkey-mapping> <cmp-field-mapping> <properties> <cmp-field-mapping name="orderId" persistence-name="OrderId" /> <cmp-field-mapping name="lineNumber" persistence-name="LineNumber" /> </properties> </cmp-field-mapping> </primkey-mapping>
We recommend that you don't use composite primary keys. A primary key should have no relation to any identifiers used in real world. Using a national insurance number, for example, as a primary key isn't the best idea because it mayunder the most obscure circumstanceschange. The same principle can be applied to orders and order lines. You may have to change the line number for some reason and doing so will impact your primary key. Instead, use a synthetic primary key, such as OrderLineId , which doesn't relate in any way to order and line number.
All CMP fields are mapped using the cmp-field-mapping element. Simple fields just specify a CMP field name (in the name attribute) and a database column name (in the persistence-name attribute) directly in the cmp-field-mapping element. For most cases, no child elements are required. The following attributes listed in Table 11-5 are available on the cmp-field-mapping element.
Parameter | Description | Default Value |
---|---|---|
cmp-field-mapping | Holds mapping information for one or more CMP or CMR fields. Simple CMP fields just use attributes, while CMR and more complex arrangements use child elements. | |
cmp-field-mapping: ejb-reference-home | Generally not used. When mapping to other EJBs, you can use the entity-ref:home attribute described in the following CMR sections. | |
cmp-field-mapping: name | Identifies the CMP field or CMR field in question. For CMP fields, this must match the value in the field-name element of the ejb-jar.xml file. For CMR fields, this must match the value in the cmr-field-name element of the ejb-jar.xml file. | |
cmp-field-mapping: persistence-name | The name of the database column this CMP field should map to. | Same as the CMP field name |
cmp-field-mapping: persistence-type | The database-specific data type of the column this field maps to. This is only necessary if the application is configured to create tables for CMP entity beans (see orion-application:autocreate-tables ). | Configured in the XML file for the current DBMS in config/database-schemas/ |
For example, if the User EJB needs to map the FirstName and LastName CMP fields to FirstName and LastName database columns, you could use two simple mappings like this:
<cmp-field-mapping name="firstName" persistence-name="FirstName" /> <cmp-field-mapping name="lastName" persistence-name="LastName" />
However, there are two cases in which the cmp-field-mapping element must contain children. One case is for multicolumn primary keysas discussed previously, the primkey-mapping element only allows one cmp-field-mapping child, so to map to multiple primary key fields that cmp-field-mapping child must have a fields child or a properties child that can enumerate the primary key fields.
The other case in which the cmp-field-mapping element must contain children is when the CMP or CMR field itself is a complex type: an EJB, an array or collection, and so forth. The mappings for these situations are described in the following sections.
In cases in which you want to explicitly indicate that a group of CMP fields should be represented by EJB 1.1-style public fields on the bean implementation class, you can use the fields child of the cmp-field-mapping element.
For example, if the User EJB needs to map the FirstName and LastName CMP fields to FirstName and LastName database columns, and you wanted to indicate that those CMP fields are represented by Java fields (not EJB 2.0-style properties), you could a fields element like this:
<cmp-field-mapping> <fields> <cmp-field-mapping name="firstName" persistence-name="FirstName" /> <cmp-field-mapping name="lastName" persistence-name="LastName" /> </fields> </cmp-field-mapping>
In cases in which you want to explicitly indicate that a group of CMP fields should be represented by EJB 2.0-style abstract properties on the bean implementation class, you can use the properties child of the cmp-field-mapping element.
For example, if the User EJB needs to map the FirstName and LastName CMP fields to FirstName and LastName database columns, and you wanted to indicate that those CMP fields are represented by Java properties (not EJB 1.1style fields), you could define the properties element like this:
<cmp-field-mapping> <properties> <cmp-field-mapping name="firstName" persistence-name="FirstName" /> <cmp-field-mapping name="lastName" persistence-name="LastName" /> </properties> </cmp-field-mapping>
In OC4J, CMP fields aren't restricted to simple data types. For example, the User EJB might include a CMP field whose type is FullName , whereas the FullName object has fields for the first name and the last name as well as a helper method to access the full name as one String . This one CMP field would still map to two separate columns in the databaseone for the first name, and one for the last name. You can use the fields or properties elements described earlier to provide mappings for either fields or properties of the complex object. The elements are listed in Table 11-6.
Parameter | Description |
---|---|
properties | Holds one or more cmp-field-mapping entries that represent Java properties on the complex data type. |
fields | Holds one or more cmp-field-mapping entries that represent Java fields on the complex data type. |
For example, if the FullName looked like this:
public class FullName implements Serializable { public String firstName; public String lastName; }
then you could declare a CMP field (in this example, named fullName ) of type FullName , and use a mapping like this:
<cmp-field-mapping name="fullName"> <fields> <cmp-field-mapping name="firstName" persistence-name="FirstName" /> <cmp-field-mapping name="lastName" persistence-name="LastName" /> </fields> </cmp-field-mapping>
If the FullName used properties instead, like this:
public class FullName implements Serializable { public String getFirstName() { } public String getLastName() { } public void setLastName(String) { } public void setFirstName(String) { }
then you could use a very similar mapping with the properties element , as follows:
<cmp-field-mapping name="fullName"> <properties> <cmp-field-mapping name="firstName" persistence-name="FirstName" /> <cmp-field-mapping name="lastName" persistence-name="LastName" /> </properties> </cmp-field-mapping>
Though it's not commonly used, you can actually map individual CMP fields to a third-party persistence manager, using the field-persistence-manager element. The elements are shown in Table 11-7.
Parameter | Description |
---|---|
field-persistence-manager | Specifies an alternate persistence manager to use for the specified field. |
field-persistence-manager:class | The class name of the persistence manager. |
property | A property used to configure the persistence manager. |
property:name | The name of the property. |
property:value | The value for the properties. |
Note | For a description of the pluggable persistence architecture, see http://otn.oracle.com/tech/java/oc4j/1003/how_to/how-to-ejb-switchPM.html . |
In EJB 1.1, you must specify the query for each finder in the orion-ejb-jar.xml deployment descriptor. In EJB 2.0, you only need to specify a query for a finder or selector here if you can't construct the correct query in EJB-QL, or you want to override the EJB-QL definition. However, the format is the same in either case.
The query syntax uses $fieldName to refer to CMP fields, and $1 to refer to argument one, $2 to refer to the second argument, and so on. Beyond that, the query uses simple SQL syntax, starting with the WHERE clause if the partial attribute is true (the default), or including the entire query if the partial attribute is false and the query is complex (for example, including a join).
The elements used to specify a query for a finder are listed in Table 11-8.
Parameter | Description | Default Value |
---|---|---|
finder-method | The parent element for providing finder queries. | |
finder-method:partial | If true, the query begins with the WHERE clause (the word WHERE should not be included). If false, the entire query must be provided, making sure to include all necessary fields in the SELECT clause. | true |
finder-method:query | The query to use for this finder. | |
method | Indicates which finder method the query is for. | |
ejb-name | The name of the EJB that the method is in. The body of this element holds its value. | |
method-intf | The interface for the finder method. The body of this element must be Home for finders, and Remote for selectors. | |
method-name | The body of this element is the name of the method in question. | |
method-params | A list of parameters to distinguish between overloaded methods. A method with no parameters should include a method-params element with no children. Otherwise, this element has one method-param child for each parameter of the method (in order). | |
method-param | The body of this element is the fully qualified Java name of a single method parameter type. |
If the User EJB has an Integer for a primary key, but still needs to look users up by their username, it might include a finder definition like this:
<finder-method partial="true" query="$username = "> <method> <ejb-name>User</ejb-name> <ejb-intf>Home</ejb-intf> <method-name>findByUsername</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> </finder-method>
If you also wanted a finder to find people whose boss has a specified first name, you might use a more complex query, as follows:
<finder-method partial="false" query="select u.* from users u, users boss where boss.id=u.boss_id and boss.firstName="> <method> <ejb-name>User</ejb-name> <ejb-intf>Home</ejb-intf> <method-name>findByBossFirstName</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> </finder-method>
In a one-to-one relationship, each bean maps to a single bean of the other type. This is usually represented by a foreign key on one table, but you need to take special care with the database design with OC4J.
For example, if a User has an Address , you might naturally include the UserId as a column on the Addresses table. This is good enough for OC4J to create a one-way relationship, but unfortunately in this case, it's probably the wrong wayan Address can find its User , but a User can't find its Address . In order to map the relationship the other way, the foreign key needs to go on the User table, and if you wanted it to be a bidirectional relationship (in which both beans can see the other), you need to include foreign keys on both tables!
Further, in OC4J, CMR fields can't be set before a bean is created, which means that the initial rows will be inserted before the foreign keys are populated , and thus the foreign key columns must be nullable. However, you can get around this requirement if you declare the foreign key column as INITIALLY DEFERRED DEFERRABLE and set the CMR field in the same transaction where you create the bean. Deferred foreign key modifiers instruct the database not to perform data integrity checks to see whether the referenced row exists during the current transaction, but only upon commit. This will allow you to use "proper" foreign keys with NOT NULL constraints.
Finally, the foreign key fields should not be part of the primary key of either bean. So, for example, you couldn't make the UserId act as the primary key for the Address table, even though users are unique and there's exactly one address per user.
So here's a recap of the database requirements:
Each bean that has a CMR field that accesses the referenced bean must declare a foreign key in its table.
It's best if each foreign key has only one column.
The foreign key columns must be declared nullable, or INITIALLY DEFERRED DEFERRABLE .
The foreign key columns should not overlap with the primary key columns of the same bean.
The XML tags used to map a one-to-one relationship are listed in Table 11-9.
Parameter | Description |
---|---|
cmp-field-mapping | This element encloses the CMR field definition. |
cmp-field-mapping:name | The name of the CMR field, which must match the cmr-field-name tag in the ejb-jar.xml file. |
Entity-ref | Indicates that this CMR field maps to a single entity. |
Entity-ref:home | Holds the JNDI name where the server can find the entity that this CMR field maps to. |
cmp-field-mapping | This element appears again to map the CMR field to a specific foreign key column. |
cmp-field-mapping:name | The CMR field name, again. |
cmp-field-mapping:persistence-name | The name of the foreign key column in the table for this entity bean. |
For example, you'll use the User EJB and the Address EJB. You'll assume a bidirectional one-to-one relationship; the Users table has an Address column, and the Addresses table has a User column. Their relationship might look like this in ejb-jar.xml :
<ejb-relation> <ejb-relation-name>UserAddress</ejb-relation-name> <ejb-relationship-role> <ejb-relationship-role-name>User</ejb-relationship-role-name> <multiplicity>One</multiplicity> <cascade-delete/> <relationship-role-source> <ejb-name>User</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>address</cmr-field-name> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <ejb-relationship-role-name>Address</ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source> <ejb-name>Address</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>user</cmr-field-name> </cmr-field> </ejb-relationship-role> </ejb-relation>
Notice that each bean has a CMR field to access the other. To map this relationship to database columns, you must add entries to orion-ejb-jar.xml for each of the two entity beans, as shown here:
<entity-deployment name=" User "> <cmp-field-mapping name="address"> <entity-ref home="Address"> <cmp-field-mapping name=" address " persistence-name=" Address " /> </entity-ref> </cmp-field-mapping> </entity-deployment> <entity-deployment name=" Address "> <cmp-field-mapping name="user"> <entity-ref home="User"> <cmp-field-mapping name=" user " persistence-name=" User " /> </entity-ref> </cmp-field-mapping> </entity-deployment>
Note that the User bean maps the "address" CMR field to the Address foreign-key column, and the Address bean maps the user CMR field to the User foreign-key column.
We've used many different foreign key naming schemas, but the one we're most comfortable using is this parent table: Parents with primary key ParentId and a child table Children with primary key ChildId . We named the foreign key in the Children table that references a row in Parents table Parent . This describes the entity the foreign key is referencing and makes all updates much easier because you cannot use aliases in UPDATE statements.
In a one-to-many relationship, the parent bean maps to a group of child beans, while each child bean maps to only one parent bean. For example, the User bean maps to many PhoneNumbers , but each PhoneNumber is only used by one User . Again, this is typically modelled with a single foreign key in the child table. In this example, the PhoneNumber table would typically have a User field that references Users(UserId) .
Additionally, the same foreign-key considerations apply to one-to-many relationships, as follows:
It's best if each foreign key has only one column.
The foreign key columns must be declared nullable, or INITIALLY DEFERRED DEFERRABLE .
The foreign key columns should not overlap with the primary key columns of the same bean.
To map a one-to-many relationship to database tables, you need to use different syntax for each bean in the relationship. The child bean uses the same syntax as for one-to-many relationships. The elements are listed in Table 11-10.
Parameter | Description |
---|---|
cmp-field-mapping | This element encloses the CMR field definition. |
cmp-field-mapping:name | The name of the CMR field, which must match the cmr-field-name tag in the ejb-jar.xml file. |
entity-ref | Indicates that this CMR field maps to a single entity. |
entity-ref:home | Holds the JNDI name where the server can find the entity that this CMR field maps to. |
cmp-field-mapping | This element appears again to map the CMR field to a specific foreign key column. |
cmp-field-mapping:name | The CMR field name, again. |
cmp-field-mapping:persistence-name | The name of the foreign key column in the table for this entity bean. |
However, the parent bean definition uses new syntax, as shown in Table 11-11.
Parameter | Description | Default Value |
---|---|---|
cmp-field-mapping | This element encloses the CMR field definition. | |
cmp-field-mapping:name | The name of the CMR field, which must match the cmr-field-name tag in the ejb-jar.xml file. | |
collection-mapping | Used to hold the persistence settings for the many side of a relationship when the cmr-field-type declared in ejb-jar.xml is java.util.Collection . Otherwise the same as the set-mapping element. | |
collection-mapping:table | The name of the mapping table (if a mapping table is used) or the table for the many bean (with the foreign key) if no mapping table is used. | |
set-mapping | Used to hold the persistence settings for the many side of a relationship when the cmr-field-type declared in ejb-jar.xml is java.util.Set . Otherwise the same as the collection-mapping element. | |
set-mapping:table | The name of the mapping table (if a mapping table is used) or the table for the many bean (with the foreign key) if no mapping table is used. | |
primkey-mapping | Defines the foreign key column in the "many" table. | |
cmp-field-mapping | Appears within the primkey-mapping element. | |
cmp-field-mapping:name | Holds the name of the foreign key column in the "many" table. | |
cmp-field-mapping: persistence-name | Also holds the name of the foreign key column in the "many" table. | |
value-mapping | Indicates the expected data type of the values in the Collection as well as how to map them to records in the database. | |
value-mapping:immutable | Whether the value can change once added to the collection (mutable) or will always stay the same (immutable). Immutable values are more efficient for the server. | true for Sets , false for Collections |
value-mapping:type | The fully qualified Java class name of the objects stored as values in the collection, which amounts to the Local interface of the "many" bean. | |
cmp-field-mapping | Appears within the value-mapping element. | |
entity-ref | Indicates that the values are mapped to EJBs. | |
entity-ref:home | Holds the JNDI name where the server can find the entity whose interface is listed in value-mapping:type . | |
cmp-field-mapping | Appears within the entity-ref element to provide the primary key of the "many" table. | |
cmp-field-mapping:name | Holds the name of the primary key column in the "many" table. | |
cmp-field-mapping: persistence-name | Also holds the name of the primary key column in the "many" table. |
For example, you'll use the User EJB and the PhoneNumber EJB. You'll assume a bidirectional one-to-many relationship; the PhoneNumber table has PhoneNumberId as its primary key, and a foreign key User that references Users(UserId) . Their relationship might look like this in ejb-jar.xml :
<ejb-relation> <ejb-relation-name>UserPhoneNumbers</ejb-relation-name> <ejb-relationship-role> <ejb-relationship-role-name>User</ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source> <ejb-name>User</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>phoneNumbers</cmr-field-name> <cmr-field-type>java.util.Collection</cmr-field-type> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <ejb-relationship-role-name>PhoneNumber</ejb-relationship-role-name> <multiplicity>Many</multiplicity> <relationship-role-source> <ejb-name>PhoneNumber</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>user</cmr-field-name> </cmr-field> </ejb-relationship-role> </ejb-relation>
Notice that each bean has a CMR field to access the other, and the User bean uses a Collection to access the group of PhoneNumbers for a user. To map this relationship to database columns, you must add entries to orion-ejb-jar.xml for each of the two entity beans. The PhoneNumber bean is the easier "many" bean, while the User bean is the more complex "one" side, as shown here:
<entity-deployment name=" PhoneNumber "> <cmp-field-mapping name="user"> <entity-ref home="User"> <cmp-field-mapping name=" user " persistence-name=" UserId " /> </entity-ref> </cmp-field-mapping> </entity-deployment>
The PhoneNumber bean maps the user CMR field to the User foreign key field.
<entity-deployment name=" User "> <cmp-field-mapping name="phoneNumbers"> <collection-mapping table="PhoneNumbers"> <primkey-mapping> <cmp-field-mapping name=" User " persistence-name=" User "/> </primkey-mapping> <value-mapping type="com.apress.oc4j903.ejb.PhoneNumber"> <cmp-field-mapping> <entity-ref home="PhoneNumber"> <cmp-field-mapping name=" PhoneNumberId " persistence-name=" PhoneNumberId " /> </entity-ref> </cmp-field-mapping> </value-mapping> </collection-mapping> </cmp-field-mapping> </entity-deployment>
The User bean indicates that the primary key in the PhoneNumbers table is the PhoneNumberId column, and the foreign key in the PhoneNumbers table is the User column.
In a many-to-many relationship, each bean maps to a group of beans of the other type. For example, the User may have many Roles , and each Role may include many Users . This relationship is always modelled with a third join table (such as UserRoles ) in which each row in the join table is nothing but a pair of foreign keys, one to each of the bean tables.
The foreign key considerations in the other CMR cases don't apply here because neither bean table needs to include a foreign key. However, it's important to note that the join table cannot have any columns other than the foreign keys.
To map a many-to-many relationship to database tables, you'll use the following syntax for each bean in the relationship, as shown in Table 11-12.
Parameter | Description | Default Value |
---|---|---|
cmp-field-mapping | This element encloses the CMR field definition. | |
cmp-field-mapping:name | The name of the CMR field, which must match the cmr-field-name tag in the ejb-jar.xml file. | |
collection-mapping | Used to hold the persistence settings when the cmr-field-type declared in ejb-jar.xml is java.util.Collection . Otherwise the same as the set-mapping element. | |
collection-mapping:table | The name of the mapping table. | |
set-mapping | Used to hold the persistence settings when the cmr-field-type declared in ejb-jar.xml is java.util.Set . Otherwise the same as the collection-mapping element. | |
set-mapping:table | The name of the mapping table. | |
primkey-mapping | Maps the foreign key in the join table to the primary key in this table. | |
cmp-field-mapping | Appears within the primkey-mapping element. | |
cmp-field-mapping:name | Holds the name of the foreign key column for this bean in the join table. | |
cmp-field-mapping: persistence-name | Holds the name of the primary key column that foreign key maps to in the bean table. | |
value-mapping | Indicates the expected data type of the values in the Collection as well as how to map them to records in the database. | |
value-mapping:immutable | Whether the value can change once added to the Collection (mutable) or will always stay the same (immutable). Immutable values are more efficient for the server. | true for Sets , false for Collections |
value-mapping:type | The fully qualified Java class name of the objects stored as values in the Collection , which amounts to the Local interface of the opposite bean. | |
cmp-field-mapping | Appears within the value-mapping element. | |
entity-ref | Indicates that the values are mapped to EJBs. | |
entity-ref:home | Holds the JNDI name where the server can find the entity whose interface is listed in value-mapping:type . | |
cmp-field-mapping | Appears within the entity-ref element to provide the mappings for the other table. | |
cmp-field-mapping:name | Holds the name of the primary key column in the table for the other bean. | |
cmp-field-mapping: persistence-name | Holds the name of the foreign key column in the join table for the foreign key that maps to the other bean. |
As an example, you'll use the User EJB and the Role EJB. You'll assume a bidirectional many-to-many relationship; the UserRoles table has a User foreign key that references Users(UserId) , and a Role foreign key referencing Roles(RoleId) . The primary key in the Users table is named UserId and RoleId for the Roles table. Their relationship might look like this in ejb-jar.xml :
<ejb-relation> <ejb-relation-name>UserRole</ejb-relation-name> <ejb-relationship-role> <ejb-relationship-role-name>User</ejb-relationship-role-name> <multiplicity>Many</multiplicity> <relationship-role-source> <ejb-name>User</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>roles</cmr-field-name> <cmr-field-type>java.util.Collection</cmr-field-type> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <ejb-relationship-role-name>Role</ejb-relationship-role-name> <multiplicity>Many</multiplicity> <relationship-role-source> <ejb-name>Role</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>users</cmr-field-name> <cmr-field-type>java.util.Collection</cmr-field-type> </cmr-field> </ejb-relationship-role> </ejb-relation>
Notice that each bean has a CMR field to access the other, and both use a Collection . To map this relationship to database columns, you must add entries to orion-ejb-jar.xml for each of the two entity beans, as follows:
<entity-deployment name=" User "> <cmp-field-mapping name="roles"> <collection-mapping table=" UserRoles "> <primkey-mapping> <cmp-field-mapping name=" User " persistence-name=" UserId " /> </primkey-mapping> <value-mapping type="com.apress.oc4j903.ejb.Role"> <cmp-field-mapping> <entity-ref home="Role"> <cmp-field-mapping name=" RoleId " persistence-name=" RoleId " /> </entity-ref> </cmp-field-mapping> </value-mapping> </collection-mapping> </cmp-field-mapping> </entity-deployment>
When you compare the preceding User mapping to the following Role mapping, you see that each side effectively includes all the information required for the relationship, and the positions in which the different field names appear are opposite.
<entity-deployment name=" Role "> <cmp-field-mapping name="users"> <collection-mapping table=" UserRoles "> <primkey-mapping> <cmp-field-mapping name=" Role " persistence-name=" RoleId " /> </primkey-mapping> <value-mapping type="com.apress.oc4j903.ejb.User"> <cmp-field-mapping> <entity-ref home="User"> <cmp-field-mapping name=" UserId " persistence-name=" UserId " /> </entity-ref> </cmp-field-mapping> </value-mapping> </collection-mapping> </cmp-field-mapping> </entity-deployment>
Though EJB 1.1 doesn't include standard features for relationships between entity beans, OC4J provides proprietary methods for mapping relationships to the database. We won't cover those features in detail, since the standard EJB 2.0 CMR features are available, but in short, relationships are mapped more or less the same way as CMR. In addition to individual Entities, Sets , and Collections , these mappings can include Lists, Vectors , and Arrays (using the list-mapping element), and Maps, Hashtables , and java.util.Properties (using the map-mapping element).
The new elements used listed in Table 11-13.
Parameter | Description |
---|---|
list-mapping | Indicates how to persist a List, Vector , or array, with a primkey-mapping child and a value-mapping child. |
map-mapping | Indicates how to persist a Map, Hashtable , or Properties , with a primkey-mapping child, a map-key-mapping child, and a value-mapping child. |
map-key-mapping | Specifies the persistence of the keys of the map. |
map-key-mapping:type | The fully qualified Java class name of the key type. |
For every message-driven bean (MDB) declared in ejb-jar.xml , you need to specify a <message-driven-deployment> element in orion-ejb-jar.xml with the locations of JMS connection factories and destinations. Within this element, you can also declare environment entries, reference other EJBs, reference resources, and configure a few optional performance settings.
For example, if the ejb-jar.xml declares the following message-driven bean:
<message-driven> <description>Order Confirmation Mailer</description> <display-name>OrderConfirmationService</display-name> <ejb-name>OrderConfirmationService</ejb-name> <ejb-class>com.apress.ejb.OrderConfirmationService</ejb-class> <transaction-type>Container</transaction-type> <message-driven-destination> <destination-type>javax.jms.Topic</destination-type> <subscription-durability>Durable</subscription-durability> </message-driven-destination> </message-driven>
Then you need to add something like this to the orion-ejb-jar.xml :
<message-driven-deployment name="OrderConfirmationService" connection-factory-location= "java:comp/resource/storeCF/TopicConnectionFactories/storeCF" destination-location="java:comp/resource/storeCF/Topics/orders" subscription-name="orderSub" listener-threads="25" transaction-timeout="43200"> ... </message-driven-deployment>
Table 11-14 lists the message-driven-deployment elements.
Parameter | Description | Default Value |
---|---|---|
message-driven-deployment | Contains container-specific settings for an individual message-driven bean. In particular, it links the MDB to an actual JMS topic or queue. | |
message-driven-deployment: cache-timeout | Should not be used for MDBs. (For stateless session beans, this specifies the number of seconds to keep instances in the pool.) | |
message-driven-deployment: connection-factory-location | The JNDI location of the JMS connection factory to use. Should be specified in the form: "java:comp/resource" plus the resource-provider name plus the "TopicConnectionFactories" or " QueueConnectionFactories" plus the user-defined name, where the resource-provider name matches the resource provider defined in application.xml . | |
message-driven-deployment: destination-location | The JNDI location of the queue or topic to use. Should be specified in the form "java:comp/resource/" plus the resource-provider name plus the "/Topics/" or "/Queues/" plus the name of the topic or queue. | |
message-driven-deployment: max-instance | Should not be used for MDBsuse listener threads instead. (For other types of beans, this specifies the max number of instances to keep in the pool.) | |
message-driven-deployment: min-instances | Should not be used for MDBsuse listener threads instead. (For other types of beans, this specifies the minimum number of instances to keep in the pool.) | |
message-driven-deployment: name | The name of the message-driven bean, as defined in ejb-jar.xml . | |
message-driven-deployment: subscription-name | For topics, the name of the subscription. | |
message-driven-deployment: listener-threads | The number of threads created to consume messages concurrently. | 1 |
message-driven-deployment: transaction-timeout | The number of seconds before a container-managed MDB transaction is rolled back automatically. | 86400 (1 day) |
Within ejb-jar.xml , EJBs can declare references to other EJBs using the <ejb-ref> element. This allows you to look up the referenced EJBs within the component environment ( java:comp/env ) using a logical name.
These logical names need to be mapped to the actual JNDI locations of the EJBs with the <ejb-ref-mapping> element in orion-ejb-jar.xml .
For example, if the EJB needed to reference an Invoice bean, you might declare the following in its ejb-jar.xml :
<ejb-ref> <ejb-ref-name>ejb/accounting/Invoice</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <home>com.apress.ejb.accounting.InvoiceHome</home> <remote> com.apress.ejb.accounting.InvoiceHome</remote> </ejb-ref>
Then, within its orion-ejb-jar.xml , you would map that name to the JNDI location of an actual deployed EJB, as follows:
<ejb-ref-mapping name="ejb/accounting/Invoice" location="Invoice"/>
Note | If the logical ejb-ref-name matches the actual JNDI location, you don't need to provide a mapping. Even if it doesn't match, you can avoid this mapping by specifying the actual JNDI location in the <ejb-link> element inside <ejb-ref> in the ejb-jar.xml . |
The <ejb-ref-mapping> element can be used inside session, entity, and message-driven deployments, and allows for the specification of the following attributes listed in Table 11-15.
Parameter | Description |
---|---|
ejb-ref-mapping:location | The actual JNDI location where the referenced EJB can be found (as specified in that bean's orion-ejb-jar.xml ). |
ejb-ref-mapping:name | The logical name given to the referenced bean in the <ejb-ref-name> element. |
Within ejb-jar.xml , EJBs can declare references to JDBC data sources, JavaMail sessions, URLs, and other resources using the <resource-ref> element. This element allows the EJB to look up the resource within its component environment ( java:comp/env ) using a logical name.
Unless these logical names match the real JNDI locations exactly, they need to be mapped to actual resource locations using the resource-ref-mapping element in orion-ejb-jar.xml .
For example, if you assign the logical name jdbc/CustomerDS to a DataSource in ejb-jar.xml like this:
<resource-ref> <res-ref-name> jdbc/CustomerDS </res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Application</res-auth> </resource-ref>
Now you define the following data source in the data-sources.xml configuration file, as follows:
<data-source class="com.evermind.sql.DriverManagerDataSource" name="OracleDS" location="jdbc/direct/OracleCustomerDatabase" xa-location="jdbc/xa/OracleCustomerDatabase " ejb-location="jdbc/OracleCustomerDatabase " connection-driver="oracle.jdbc.driver.OracleDriver" username="scott" password="tiger" url="jdbc:oracle:thin:@localhost:1521:oracle" inactivity-timeout="30" />
You need to map the resource reference to the actual data source in orion-ejb-jar.xml like this:
<resource-ref-mapping name="jdbc/CustomerDS" location="jdbc/OracleCustomerDatabase"/>
If you need to look up a resource in a location outside the server, then you can link to an external JNDI context like this:
<resource-ref-mapping name="logicalName"> <lookup-context location="some/external/jndi/location"> <context-attribute name="java.naming.factory.initial" value="someInitialContextFactoryClass"/> </lookup-context> </resource-ref-mapping>
The <resource-ref-mapping> element can be used inside session, entity, and message-driven deployments, and contains the following elements listed in Table 11-16.
Parameter | Description |
---|---|
resource-ref-mapping:name | Matches the logical resource-ref name used in ejb-jar.xml . |
resource-ref-mapping:location | The actual JNDI location of the resource to map to. |
lookup-context | Allows the optional specification of a particular context to be used to look up a resource (useful when the resource is outside the server). |
lookup-context:location | Name where the resource can be found in the specified context. |
context-attribute | Specifies JNDI properties to be used for the specified lookup-context . Only the initial context factory property is required. |
context-attribute:name | Name of the JNDI property (for example, java.naming.factory.initial ). |
context-attribute:value | Value of the JNDI property (for example, the class name of the initial context factory). |
The ejb-jar.xml descriptor can also define <resource-env-ref> elements for administered objects associated with other resources (for example, JMS destinations). These references are mapped very similarly to the resource references described earlier.
For example, if the ejb-jar.xml references a JMS topic like this:
<resource-env-ref> <description>Registration Events</description> <res-env-ref-name>jms/regEventsTopic</res-ref-name> <res-env-ref-type>javax.jms.Topic</res-type> </resource-env-ref>
and you've declared the corresponding topic in the jms.xml like this:
<topic name="Registration Events" location="jms/RegistrationTopic"> <description>Broadcasts user registration events</description> </topic>
then the orion-ejb-jar.xml should contain something like this:
<resource-env-ref-mapping name="jms/regEventsTopic" location="jms/RegistrationTopic"/>
The <resource-env-ref-mapping> element can be used inside session, entity, and message-driven deployments, and contains the following elements in Table 11-17.
Parameter | Description |
---|---|
resource-env-ref-mapping:name | Matches the logical resource-env-ref name used in ejb-jar.xml . |
resource-env-ref-mapping:location | The actual JNDI location of the resource to map to. |
In some cases, you may want to override an environment entry specified in an ejb-jar.xml (perhaps to avoid altering the standard descriptor of a third-party component). OC4J allows you to do this in orion-ejb-jar.xml with the env-entry-mapping element.
For example, if the ejb-jar.xml contains the following environment entry:
<env-entry> <env-entry-name>pastDueDays</env-entry-name> <env-entry-type>java.lang.Integer</env-entry-type> <env-entry-value>15</env-entry-value> </env-entry>
then you can override it to a different value in orion-ejb-jar.xml like this:
<env-entry-mapping name="pastDueDays">30</env-entry-mapping>
The elements are listed in Table 11-18.
Parameter | Description |
---|---|
env-entry-mapping | In session-deployment, message-driven deployment, and entity-driven deployment. Body is the new value. |
env-entry-mapping:name | Matches the env entry name in ejb-jar.xml . |
In addition to the standard J2EE message-driven beans described earlier, OC4J allows asynchronous calls to EJBs using the proprietary AC4J architecture. If you want the beans to be accessible through AC4J, you need to configure the names, Databus, and security that will be used to invoke them.
In the following example (within orion-ejb-jar.xml ) you make the CustomerManager EJB accessible through AC4J as jem/CustMgr , and configure it to use the caller's identity and require that the caller possess the manager role. You also need to specify the data source that contains the AC4J Databus, using the <jem-server-extension> element, as follows:
<jem-server-extension data-source-location=" jdbc/DataBusDS " scheduling-threads="1"> <description>AC4J configuration</description> </jem-server-extension> <jem-deployment jem-name=" jem/CustMgr " ejb-name=" CustomerManager "> <description> CustomerManager bean</description> <called-by> <caller caller-identity="manager"/> </called-by> <security-identity> <description>pass through caller identity</description> <use-caller-identity>true</use-caller-identity> </security-identity> </jem-deployment>
Without changing the CustomerManager implementation in any way, you've now allowed clients to invoke its methods asynchronously through AC4J. For example, to issue a credit check, a client might do something like this:
Context context = new InitialContext(); String dsName = "java:comp/env/jdbc/DataBusDS"; DataSource ds = (DataSource) context.lookup(dsName); Connection dsCon = ds.getConnection("user", "password"); JEMConnection jemCon = new JemConnection(dsCon); JEMSession jemSession = new JEMSession(jemCon); JEMHandle custMgr = (JEMHandle) context.lookup("jem/CustMgr"); Object[] params = new Object[] { accountNumber, expiration }; JEMEmitToken call = jemSession.call(custMgr, "processCreditCheck", null, params, null, 0, 0); dsCon.commit();
At this point, the Data Bus takes control and invokes CustomerManager.processCreditCheck() at the next opportunity without forcing the client to wait for CustomerManager to be available.
For more information about using AC4J, check the OC4J Enterprise JavaBeans Developer Guide and the AC4J JavaDocs, both available at http://otn.oracle.com .
The following elements listed in Table 11-19 may be used inside the <enterprise-beans> element to configure AC4J deployments.
Parameter | Description | Default Value |
---|---|---|
jem-server-extension | Contains AC4J configuration information for JEM deployments. | |
jem-server-extension: data-source-location | JNDI location of the DataSource (defined in data-sources.xml ) that contains the Data Bus. | |
jem-server-extension: scheduling-threads | Number of OC4J threads that can act in parallel. | 1 |
jem-deployment | Contains information about how to locate the EJB and invoke its methods securely. | |
jem-deployment:ejb-name | Specifies the name of the EJB as defined in ejb-jar.xml . | |
jem-deployment:jem-name | Specifies the name AC4J clients will use to find this EJB. | |
called-by | Specifies which callers are allowed to invoke this EJB through AC4J. | |
caller | Represents a single caller allowed to invoke this EJB through AC4J. | |
caller:caller-identity | The specific security role the caller must possess. | |
security-identity | Specifies whether to propagate security identities through AC4J or always use a specified identity. | |
use-caller-identity | If true, caller's identity will be used. | |
run-as-specified-identity | If specified, the identity AC4J will use to invoke this EJB's methods. | |
role-name | The specific security role to use |
Most CORBA configuration takes place at the server level. For each individual CORBA-accessible EJB, however, you need to configure security settings in ejb-jar.xml .
In particular, you need to specify which transport security features are supported or required, whether or not callers must be authenticated, and how caller identities will be propagated.
In the following example, you'll require one transport security element and support all the others. Callers to this EJB will need to authenticate with a username and password against the default realm. Finally, if provided by other servers, delegated identities will be used.
<ior-security-config> <transport-config> <integrity>required</integrity> <confidentiality>supported</confidentiality> <establish-trust-in-target>supported</establish-trust-in-target> <establish-trust-in-client>supported</establish-trust-in-client> </transport-config> <as-context> <auth-method>username_password</auth-method> <realm>default</realm> <required>true</required> </as-context> <sas-context> <caller-propagation>supported</caller-propagation> </sas-context> </ior-security-config>
The <ior-security-config> element may be used within session and entity deployments, and includes the following elements listed in Table 11-20.
Parameter | Description |
---|---|
transport-config | Contains settings for CSIv2 security properties. |
integrity | Specifies whether CSIv2 integrity is required, supported , or not supported ( none ). |
confidentiality | Specifies whether CSIv2 confidentiality is required, supported , or not supported ( none ). |
establish-trust-in-target | Specifies whether CSIv2 establish-trust-in-target is required, supported , or not supported ( none ). |
establish-trust-in-client | Specifies whether CSIv2 establish-trust-in-client is required, supported , or not supported ( none ). |
as-context | Contains settings for caller authentication |
auth-method | Specifies how the caller should authenticate. May be set to username_password or none . |
realm | The realm to use for authentication. Must be set to default in current releases. |
required | Whether authentication is required to use this EJB. |
sas-context | Contains settings for caller identity propagation. |
caller-propagation | Specifies whether this EJB accepts propagated identities. If set to required , then servers must pass in an identity. If supported , then it will allow identities to be propagated. If set to none , then identity propagation isn't supported. |
| ||