4 Creating a Business Domain Layer Using Entity Objects

This chapter describes how to use entity objects to create a reusable layer of business domain objects for use in your Java EE applications.

This chapter includes the following sections:

4.1 Introduction to Entity Objects

An entity object is the ADF Business Components component that represents a row in the specified data source and simplifies modifying its associated attributes. Importantly, it allows you to encapsulate domain business logic to ensure that your business policies and rules are consistently validated.

Entity objects support numerous declarative business logic features to enforce the validity of your data. You will typically complement declarative validation with additional custom application logic and business rules to cleanly encapsulate a maximum amount of domain business logic into each entity object. Your associated set of entity objects forms a reusable business domain layer that you can exploit in multiple applications.

The key concepts of entity objects are the following:

  • You define an entity object by specifying the database table whose rows it will represent.

  • You can create associations to reflect relationships between entity objects.

  • At runtime, entity rows are managed by a related entity definition object.

  • Each entity row is identified by a related row key.

  • You retrieve and modify entity rows in the context of an application module that provides the database transaction.

4.2 Creating Entity Objects and Associations

If you already have a database schema to work from, the simplest way to create entity objects and associations is to reverse-engineer them from existing tables. When needed, you can also create an entity object from scratch, and then generate a table for it later.

4.2.1 How to Create Multiple Entity Objects and Associations from Existing Tables

To create one or more entity objects, use the Business Components from Tables wizard, which is available from the New Gallery.

To create one or more entity objects and associations from existing tables:

  1. In the Application Navigator, right-click the project in which you want to create the entity objects and choose New.

  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Business Components from Tables, and click OK.

    If this is the first component you're creating in the project, the Initialize Business Components Project dialog appears to allow you to select a database connection.

  3. In the Initialize Business Components Project dialog, select the database connection or choose New to create a connection. Click OK.

  4. On the Entity Objects page, do the following to create the entity objects:

    • Enter the package name in which all of the entity objects will be created.

    • Select the tables from the Available list for which you want to create entity objects.

      If the Auto-Query checkbox is selected, then the list of available tables appears immediately. In the Name Filter field, you can optionally enter a full or partial table name to filter the available tables list in real time. As an alternative to the auto-query feature, click the Query button to retrieve the list based on an optional table name filter. When no name filter is entered, JDeveloper retrieves all table objects for the chosen schema.

    • Click Filter Types if you want to see only a subset of the database objects available. You can filter out tables, views, or synonyms.

    Once you have selected a table from the Available list, the proposed entity object name for that table appears in the Selected list with the related table name in parenthesis.

    • Select an entity object name in the Selected list and use the Entity Name field to change the default entity object name.

    Best Practice:

    Since each entity object instance represents a single row in a particular table, name the entity objects with a singular noun (like Address, Order, and Person), instead of their plural counterparts. Figure 4-1 shows what the wizard page looks like after selecting the ADDRESSES table in the FOD schema, setting a package name of oracle.fodemo.storefront.entities, and renaming the entity object in the singular.

    Figure 4-1 Create Business Components from Tables Wizard, Entity Objects Page

    Step 1 of Create Business Components from Tables wizard
  5. When you are satisfied with the selected table objects and their corresponding entity object names, click Finish.

The Application Navigator displays the entity objects in the package you specified.

Best Practice:

After you create associations, move all of your associations to a separate package so that you can view and manage them separately from the entity objects. In Figure 4-2, the associations have been moved to a subpackage (associations) and do not appear in the entities package in the Application Navigator. For more information, see Section 4.3.4, "How to Rename and Move Associations to a Different Package."

Figure 4-2 New Entity Objects in Application Navigator

Image shows how Application Navigator sorts entity objects

4.2.2 How to Create Single Entity Objects Using the Create Entity Wizard

To create a single entity object, you can use the Create Entity Object wizard, which is available in the New Gallery.

To create a single entity object and association:

  1. In the Application Navigator, right-click the project in which you want to create the entity object and choose New.

  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Entity Object, and click OK.

    If this is the first component you're creating in the project, the Initialize Business Components Project dialog appears to allow you to select a database connection.

  3. In the Initialize Business Components Project dialog, select the database connection or choose New to create a connection. Click OK.

  4. On the Name page, do the following to create the entity object:

    • Enter the package name in which the entity object will be created.

    • Click Browse (next to the Schema Object field) to select the table for which you want to create the entity object.

      Or, if you plan to create the table later, you can enter a name of a table that does not exist.

  5. If you manually entered a table name in the Schema Objects field, you will need to define each attribute on the Attributes page of the wizard. Click Next.

    You can create the table manually or generate it, as described in Section 4.2.6, "How to Create Database Tables from Entity Objects."

  6. When you are satisfied with the table object and its corresponding entity object name, click Finish.

4.2.3 What Happens When You Create Entity Objects and Associations from Existing Tables

When you create an entity object from an existing table, first JDeveloper interrogates the data dictionary to infer the following information:

  • The Java-friendly entity attribute names from the names of the table's columns (for example, USER_ID -> UserId)

  • The SQL and Java data types of each attribute based on those of the underlying column

  • The length and precision of each attribute

  • The primary and unique key attributes

  • The mandatory flag on attributes, based on NOT NULL constraints

  • The relationships between the new entity object and other entities based on foreign key constraints

Note:

Since an entity object represents a database row, it seems natural to call it an entity row. Alternatively, since at runtime the entity row is an instance of a Java object that encapsulates business logic for that database row, the more object-oriented term entity instance is also appropriate. Therefore, these two terms are interchangeable.

JDeveloper then creates the XML component definition file that represents its declarative settings and saves it in the directory that corresponds to the name of its package. For example, when an entity named Order appears in the genericbcmodel.entities package, JDeveloper will create the XML file genericbcmodel/entities/Order.xml under the project's source path. This XML file contains the name of the table, the names and data types of each entity attribute, and the column name for each attribute.

You can inspect the XML description for the entity object by opening the object in the overview editor and clicking the Source tab.

Note:

If your IDE-level Business Components Java generation preferences so indicate, the wizard may also create an optional custom entity object class (for example, OrderImpl.java).

4.2.3.1 What Happens When Tables Have Foreign Key Relationships

In addition to the entity objects, the wizard also generates named association components that capture information about the relationships between entity objects. For example, the database diagram in Figure 4-3 shows that JDeveloper derives default association names like OrderItemsProductsFkAssoc by converting the foreign key constraint names to a Java-friendly name and adding the Assoc suffix. For each association created, JDeveloper creates an appropriate XML component definition file and saves it in the directory that corresponds to the name of its package.

By default the associations reverse-engineered from foreign keys are created in the same package as the entities. For example, for the association OrderItemsProductsFkAssoc with entities in the fodemo.storefront.entities package, JDeveloper creates the association XML file named ./fodemo/storefront/entities/OrderItemsProductsFkAssoc.xml.

Figure 4-3 ORDER_ITEMS and PRODUCTS_BASE Tables Related by Foreign Key

Images shows tables related by foreign keys

4.2.3.2 What Happens When a Table Has No Primary Key

If a table has no primary key constraint, then JDeveloper cannot infer the primary key for the entity object. Since every entity object must have at least one attribute marked as a primary key, the wizard will create an attribute named RowID and use the database ROWID value as the primary key for the entity. If appropriate, you can edit the entity object later to mark a different attribute as a primary key and remove the RowID attribute. When you use the Create Entity Object wizard and you have not set any other attribute as primary key, you will be prompted to use RowID as the primary key.

4.2.4 What Happens When You Create an Entity Object for a Synonym or View

When you create an entity object using the Business Components from Tables wizard or the Create Entity Object wizard, the object can represent an underlying table, synonym, or view. The framework can infer the primary key and related associations for a table or synonym by inspecting database primary and foreign key constraints in the data dictionary.

However, when your selected schema object is a database view, then neither the primary key nor associations can be inferred since database views do not have database constraints. In this case, if you use the Business Components from Tables wizard, the primary key defaults to RowID. If you use the Create Entity Object wizard, you'll need to specify the primary key manually by marking at least one of its attributes as a primary key. For more information, see Section 4.2.3.2, "What Happens When a Table Has No Primary Key."

When your selected schema object is a synonym, there are two possible outcomes. If the synonym is a synonym for a table, then the wizard and editor behave as if you had specified a table. If instead the synonym refers to a database view, then they behave as if you had specified a view.

4.2.5 How to Edit an Existing Entity Object or Association

After you've created a new entity object or association, you can edit any of its settings in the overview editor. To launch the editor, choose Open from the context menu for the entity object or association in the Application Navigator or double-click the object. By clicking the different tabs of the editor, you can adjust the settings that define the object and govern its runtime behavior.

4.2.6 How to Create Database Tables from Entity Objects

To create database tables based on entity objects, right-click the package in the Application Navigator that contains the entity objects and choose Create Database Objects from the context menu. A dialog appears to let you select the entities whose tables you'd like to create. This tool can be used to generate a table for an entity object you created from scratch, or to drop and re-create an existing table.

Caution:

This feature does not generate a DDL script to run later. It performs its operations directly against the database and will drop existing tables. A dialog appears to confirm that you want to do this before proceeding. For entities based on existing tables, use with caution.

In the overview editor for an association, the Use Database Key Constraints checkbox on the Association Properties page controls whether the related foreign key constraint will be generated when creating the tables for entity objects. Selecting this option does not have any runtime implications.

4.2.7 How to Synchronize an Entity with Changes to Its Database Table

Inevitably you (or your DBA) might alter a table for which you've already created an entity object. Your existing entity will not be disturbed by the presence of additional attributes in its underlying table; however, if you want to access the new column in the table in your Java EE application, you'll need to synchronize the entity object with the database table.

For example, suppose you had done the following at the SQL*Plus command prompt to add a new SECURITY_QUESTION column to the PERSONS table:

ALTER TABLE PERSONS ADD (security_question VARCHAR2(60));

Then you can use the synchronization feature to add the new column as an attribute on the entity object.

To synchronize an entity with changes to its database table:

  1. In the Application Navigator, right-click the desired entity object and choose Synchronize with Database.

    The Synchronize with Database dialog shows the list of the actions that can be taken to synchronize the business logic tier with the database.

  2. Select the action you want to take:

    • Select one or more actions from the list, and click Synchronize to synchronize the selected items.

    • Click Synchronize All to perform all actions in the list.

    • Click Write to File to save the action list to a text file. This feature helps you keep track of the changes you make.

  3. When finished, click OK to close the dialog.

4.2.7.1 Removing an Attribute Associated with a Dropped Column

The synchronize feature does not handle dropped columns. When a column is dropped from the underlying database after an entity object has been created, you can delete the corresponding attribute from the entity object. If the attribute is used in other parts of your application, you must remove those usages as well.

To remove an entity attribute:

  1. In the Application Navigator, double-click the entity.

  2. In the overview editor, click the Attributes navigation tab.

  3. On the Attributes page, right-click the attribute, and choose Delete Safely.

    If there are other usages, the Delete Attributes dialog displays the message "Usages were found."

  4. If usages were found, click View Usages.

    The Log window shows all usages of the attribute.

  5. Work through the list in the Log window to delete all usages of the entity attribute.

4.2.7.2 Addressing a Data Type Change in the Underlying Table

The synchronize feature does not handle changed data types. For a data type change in the underlying table (for example, precision increased), you must locate all usages of the attribute and manually make changes, as necessary.

To locate all usages of an entity attribute:

  1. In the Application Navigator, double-click the entity.

  2. In the overview editor, click the Attributes navigation tab.

  3. On the Attributes page, right-click the attribute and choose Find Usages.

    If there are other usages, they are displayed in the Log window.

4.2.8 How to Store Data Pertaining to a Specific Point in Time

Effective dated tables are used to provide a view into the data set pertaining to a specific point in time. Effective dated tables are widely used in applications like HRMS and Payroll to answer queries like:

  • What was the tax rate for an employee on August 31st, 2005?

  • What are the employee's benefits as of October 2004?

In either case, the employee's data may have changed to a different value since then.

The primary difference between the effective dated entity type and the dated entity type is that the dated entity does not cause row splits during update and delete.

When you create an effective dated entity object, you identify the entity as effective dated and specify the attributes of the entity that represent the start and end dates. The start date and end date attributes must be of the Date type.

Additionally, you can specify an attribute that represents the sequence for the effective dated entity and an attribute that represents a flag for the sequence. These attributes allow for tracking of multiple changes in a single day.

To create an effective dated entity object

  1. In the Application Navigator, double-click the entity on which you want enable effective dating.

  2. In the Property Inspector, expand the Type category.

    If necessary, choose Property Inspector from the View menu to display the Property Inspector.

    If the Type category is not displayed in the Property Inspector, click the General tab in the overview editor to set the proper focus.

  3. From the context menu for the Effective Date Type property, choose Edit.

    To display the context menu, click the down arrow next to the property field.

  4. In the Edit Property dialog, specify the following settings:

    • For Effective Date Type, select EffectiveDated.

    • For Start Date Attribute, select the attribute that corresponds to the start date.

    • For End Date Attribute, select the attribute that corresponds to the end date.

  5. You can optionally specify attributes that allow for tracking of multiple changes in a single day.

    • For Effective Date Sequence, select the attribute that stores the sequence of changes.

    • For Effective Date Sequence Flag, select the attribute that stores a flag indicating the most recent change in the sequence.

    Without specifying the Effective Date Sequence and Effective Date Sequence Flag attributes, the default granularity of effective dating is one day. For this reason, multiple changes in a single day are not allowed. An attempt to update the entity a second time in a single day will result in an exception being thrown. After these two attributes are specified, the framework inserts and updates their values as necessary to track multiple changes in a single day.

  6. Click OK.

Note:

You can also identify the start and end date attributes using the Property Inspector for the appropriate attributes. To do so, select the appropriate attribute in the overview editor and set the IsEffectiveStartDate or IsEffectiveEndDate property to true in the Property Inspector.

4.2.9 What Happens When You Create Effective Dated Entity Objects

When you create an effective dated entity object, JDeveloper creates a transient attribute called SysEffectiveDate to store the effective date for the row. Typically the Insert, Update, and Delete operations modify the transient attribute while the ADF Business Components framework decides the appropriate values for the effective start date and the effective end date.

Example 4-1 show some sample XML entries that are generated when you create an effective dated entity. For more information about working with effective dated objects, see Section 5.4, "Limiting View Object Rows Using Effective Date Ranges."

Example 4-1 XML Entries for Effective Dated Entities

// In the effective dated entity 
<Entity
  ...
  EffectiveDateType="EffectiveDated">
 
// In the attribute identified as the start date
  <Attribute
    ...
    IsEffectiveStartDate="true">
 
// In the attribute identified as the end date
  <Attribute
    ...
    IsEffectiveEndDate="true">
 
// The SysEffectiveDate transient attribute
  <Attribute
    Name="SysEffectiveDate"
    IsQueriable="false"
    IsPersistent="false"
    ColumnName="$none$"
    Type="oracle.jbo.domain.Date"
    ColumnType="$none$"
    SQLType="DATE"/>

4.2.10 What You May Need to Know About Creating Entities from Tables

The Business Components from Tables wizard makes it easy to quickly generate many business components at the same time. In practice, this does not mean that you should use it to immediately create entity objects for every table in your database schema just because it is possible to do so. If your application requires all of the tables, then that strategy might be appropriate. But because you can use the wizard whenever needed, you should create the entity objects for the tables that you know will be involved in the application.

Section 9.4, "Defining Nested Application Modules," describes a use case-driven design approach for your business services that can assist you in understanding which entity objects are required to support your application's business logic needs. You can always add more entity objects later as necessary.

4.3 Creating and Configuring Associations

If your database tables have no foreign key constraints defined, JDeveloper won't be able to infer the associations between the entity objects that you create. Since several ADF Business Components runtime features depend on the presence of entity associations, create them manually if the foreign key constraints don't exist.

4.3.1 How to Create an Association

To create an association, use the Create New Association wizard, which is available in the New Gallery.

To create an association:

  1. In the Application Navigator, right-click the project in which you want to create the association and choose New.

  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Association, and click OK.

  3. On the Name page, do the following to create the association:

    • Enter the package name in which the association will be created.

    • Enter the name of the association component.

    • Click Next.

  4. On the Entity Objects page, select the source and destination entity attributes:

    • Select a source attribute from one of the entity objects that is involved in the association to act as the master.

    • Select a corresponding destination attribute from the other entity object involved in the association.

    For example, Figure 4-4 shows the selected OrderId attribute from the OrderEO entity object as the source entity attribute. Because the OrderItemEO rows contain an order ID that relates them to a specific OrderEO row, you would select this OrderId foreign key attribute in the OrderItemEO entity object as the destination attribute.

    Figure 4-4 Create Association Wizard, Attribute Pairs That Relate Two Entity Objects Defined

    Image shows step 2 of the Create Association wizard
  5. Click Add to add the matching attribute pair to the table of source and destination attribute pairs below.

    By default, the Bound checkbox is selected for both the source and destination attribute. This checkbox allows you to specify whether or not the value will be bound into the association SQL statement that is created internally when navigating from source entity to target entity or from target entity to source entity (depending on which side you select).

    Typically, you would deselect the checkbox for an attribute in the relationship that is a transient entity attribute whose value is a constant and therefore should not participate in the association SQL statement to retrieve the entity.

  6. If the association requires multiple attribute pairs to define it, you can repeat the preceding steps to add additional source/target attribute pairs.

  7. Finally, ensure that the Cardinality dropdown correctly reflects the cardinality of the association. The default is a one-to-many relationship. Click Next.

    For example, since the relationship between a OrderEO row and its related OrderItemEO rows is one-to-many, you can leave the default setting.

  8. On the Association SQL page, you can preview the association SQL predicate that will be used at runtime to access the related destination entity objects for a given instance of the source entity object.

  9. On the Association Properties page, disable the Expose Accessor checkbox on either the Source or the Destination entity object when you want to create an association that represents a one-way relationship. The default, bidirectional navigation is more convenient for writing business validation logic, so in practice, you typically leave these default checkbox settings.

    For example, Figure 4-5 shows an association that represents a bidirectional relationship, permitting either entity object to access the related entity row(s) on the other side when needed. In this example, this means that if you are working with an instance of an OrderEO entity object, you can easily access the collection of its related OrderItemEO rows. With any instance of a OrderItemEO entity object, you can also easily access the Order to which it belongs.

    Figure 4-5 Association Properties Control Runtime Behavior

    Image shows step 4 of the Association Properties page
  10. When you are satisfied with the association definition, click Finish.

4.3.2 What Happens When You Create an Association

When you create an association, JDeveloper creates an appropriate XML component definition file and saves it in the directory that corresponds to the name of its package. For example, if you created an association named OrderItemsOrdersFkAssoc in the oracle.fodemo.storefront.entities.associations subpackage, then the association XML file would be created in the ./oracle/fodemo/storefront/entities/associations directory with the name OrderItemsOrdersFkAssoc.xml. At runtime, the entity object uses the association information to automate working with related sets of entities.

4.3.3 How to Change Entity Association Accessor Names

You should consider the default settings for the accessor names on the Association Properties page and decide whether changing the names to something more intuitive is appropriate. The default settings define the names of the accessor attributes you will use at runtime to programmatically access the entities on the other side of the relationship. By default, the accessor names will be the names of the entity object on the other side. Since the accessor names on an entity must be unique among entity object attributes and other accessors, if one entity is related to another entity in multiple ways, then the default accessor names are modified with a numeric suffix to make the name unique.

In an existing association, you can rename the accessor using the Association Properties dialog.

To rename the entity accessor in an association:

  1. In the Application Navigator, double-click the association.

  2. In the overview editor, click the Relationships navigation tab.

  3. On the Relationships page, expand the Accessors category and click the Edit icon.

    The Association Properties dialog displays the current settings for the association's accessors.

  4. Modify the name as necessary, and click OK to apply your changes and close the dialog.

4.3.4 How to Rename and Move Associations to a Different Package

Since associations are a component that you typically configure at the outset of your project and don't change frequently thereafter, you might want to move the associations to a different package so that your entity objects are easier to see. Both renaming components and moving them to a different package is straightforward using JDeveloper's refactoring functionality.

To move a set of business components to a different package:

  1. In the Application Navigator, select the components you want to move.

  2. Right-click one of the selected components, and choose Refactor > Move.

  3. In the Move Business Components dialog, enter the name of the package to move the component(s) to, or click Browse to navigate to and select the package.

  4. Click OK to apply your changes and close the dialog.

To rename a component:

  1. In the Application Navigator, right-click the component you want to rename, and choose Refactor > Rename.

  2. In the Rename dialog, enter the new name for the component and click OK.

When you refactor ADF Business Components, JDeveloper moves the XML and Java files related to the components, and updates any other components that might reference them.

Figure 4-6 shows what the Application Navigator would look like after renaming all of the associations and moving them to the oracle.fodemo.storefront.associations subpackage. While you can refactor the associations into any package name you choose, picking a subpackage keeps them logically related to the entities, and allows you to collapse the package of associations to better manage which files display in the Application Navigator.

Figure 4-6 Application Navigator After Association Refactoring

Image of Application Navigator after association refactoring

4.3.5 What You May Need to Know About Using a Custom View Object in an Association

You can associate a custom view object with the source end or destination end (or both) of an entity association.

When you traverse entity associations in your code, if the entities are not already in the cache, then the ADF Business Components framework performs a query to bring the entity (or entities) into the cache. By default, the query performed to bring an entity into the cache is the find-by-primary-key query that selects values for all persistent entity attributes from the underlying table. If the application performs a lot of programmatic entity association traversal, you could find that retrieving all of the attributes might be heavy-handed for your use cases.

Entity associations support the ability to associate a custom, entity-based view object with the source entity or destination entity in the association, or both. The primary entity usage of the entity-based view object you supply must match the entity type of the association end for which you use it.

Using a custom view object can be useful because the custom view object's query can include fewer columns and it can include an ORDER BY clause. This allows you to control how much data is retrieved when an entity is brought into the cache due to association traversal, as well as the order in which any collections of related entities will appear.

For more information about creating a custom view object, see Section 39.8.2, "How to Create an Entity-Based Programmatic View Object."

4.3.6 What You May Need to Know About Composition Associations

A composition association represents a relationship between entities, such as Person referenced by an Order or a OrderItem contained in a Order. When you create composition associations, it is useful to know about the kinds of relationships you can represent, and the various options.

Associations between entity objects can represent two styles of relationships depending on whether the source entity:

  • References the destination entity

  • Contains the destination entity as a logical, nested part

Figure 4-7 depicts an application business layer that represents both styles of relationships. For example, an OrderEO entry references a PersonEO. This relationship represents the first kind of association, reflecting that a PersonEO or an OrderEO entity object can exist independent from each other. In addition, the removal of an Order does not imply the cascade removal of the Person to which it was referring.

In contrast, the relationship between OrderEO and its collection of related OrderItemEO details is stronger than a simple reference. The OrderItemEO entries comprise a logical part of the overall OrderEO. In other words, a OrderEO is composed of OrderItemEO entries. It does not make sense for a OrderItemEO entity row to exist independently from an OrderEO, and when an OrderEO is removed — assuming the removal is allowed — all of its composed parts should be removed as well. This kind of logical containership represents the second kind of association, called a composition. The UML diagram in Figure 4-7 illustrates the stronger composition relationship using the solid diamond shape on the side of the association which composes the other side of the association.

Figure 4-7 OrderEO Composed of OrderItemEO Entries and References Both PersonEO and AddressEO

Image shows objects that are part of ServiceRequest

The Business Components from Tables Wizard creates composition associations by default for any foreign keys that have the ON DELETE CASCADE option. You can use the Create Association wizard or the overview editor for the association to indicate that an association is a composition association. Select the Composition Association checkbox on either the Association Properties page of the Create Association wizard or the Relationships page of the overview editor. An entity object offers additional runtime behavior in the presence of a composition. For the settings that control the behavior, see Section 4.10.13, "How to Configure Composition Behavior."

4.4 Creating an Entity Diagram for Your Business Layer

Since your layer of business domain objects represents a key reusable asset for your team, it is often convenient to visualize the business domain layer using a UML model. JDeveloper supports easily creating a diagram for your business domain layer that you and your colleagues can use for reference.

The UML diagram of business components is not just a static picture that reflects the point in time when you dropped the entity objects onto the diagram. Rather, it is a UML-based rendering of the current component definitions, that will always reflect the current state of affairs. What's more, the UML diagram is both a visualization aid and a visual navigation and editing tool. To open the overview editor for any entity object in a diagram, right-click the desired object and choose Properties from the context menu or double-click the desired object. You can also perform some entity object editing tasks directly on the diagram, like renaming entities and entity attributes, and adding or removing attributes.

4.4.1 How to Create an Entity Diagram

To create a diagram of your entity objects, you can use the Create Business Components Diagram dialog, which is available in the New Gallery.

To create an entity diagram that models existing entity objects:

  1. In the Application Navigator, right-click the project in which you want to create the entity diagram and choose New.

  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Business Components Diagram, and click OK.

  3. In the dialog, do the following to create the diagram:

    • Enter a name for the diagram, for example Business Domain Objects.

    • Enter the package name in which the diagram will be created. For example, you might create it in a subpackage like myproject.model.design.

  4. Click OK.

  5. To add existing entity objects to the diagram, select them in the Application Navigator and drop them onto the diagram surface.

After you have created the diagram you can use the Property Inspector to adjust visual properties of the diagram. For example you can:

  • Hide or show the package name

  • Change the font

  • Toggle the grid and page breaks on or off

  • Display association names that may otherwise be ambiguous

You can also create an image of the diagram in PNG, JPG, SVG, or compressed SVG format, by choosing Publish Diagram from the context menu on the diagram surface.

Figure 4-8 shows a sample diagram that models various entity objects from the business domain layer.

Figure 4-8 UML Diagram of Business Domain Layer

Image of UML Diagram of Business Domain Layer

4.4.2 What Happens When You Create an Entity Diagram

When you create a business components diagram, JDeveloper creates an XML file *.oxd_bc4j representing the diagram in a subdirectory of the project's model path that matches the package name in which the diagram resides.

By default, the Application Navigator unifies the display of the project contents paths so that ADF components and Java files in the source path appear in the same package tree as the UML model artifacts in the project model path. However, as shown in Figure 4-9, using the Navigator Display Options toolbar button on the Application Navigator, you can see the distinct project content path root directories when you prefer.

Figure 4-9 Toggling the Display of Separate Content Path Directories

Image of toggling folder sorting in Application Navigator

4.4.3 What You May Need to Know About the XML Component Descriptors

When you include a business component like an entity object to a UML diagram, JDeveloper adds extra metadata to a <Data> section of the component's XML component descriptor as shown in Example 4-2. This additional information is used at design time only.

Example 4-2 Additional UML Metadata Added to an Entity Object XML Descriptor

<Entity Name="OrderEO" ... >
   <Data>
      <Property Name ="COMPLETE_LIBRARY" Value ="FALSE" />
      <Property Name ="ID"
                Value ="ff16fca0-0109-1000-80f2-8d9081ce706f::::EntityObject" />
      <Property Name ="IS_ABSTRACT" Value ="FALSE" />
      <Property Name ="IS_ACTIVE" Value ="FALSE" />
      <Property Name ="IS_LEAF" Value ="FALSE" />
      <Property Name ="IS_ROOT" Value ="FALSE" />
      <Property Name ="VISIBILITY" Value ="PUBLIC" />
   </Data>
   :
</Entity>

4.4.4 What You May Need to Know About Changing the Names of Components

On an entity diagram, the names of entity objects, attributes, and associations can be changed for clarity. Changing names on a diagram does not affect the underlying data names. The name change persists for the diagram only. The new name may contain spaces and mixed case for readability. To change the actual entity object names, attribute names, or association names, open the entity object or association in the overview editor.

4.5 Defining Property Sets

A property set is a named collection of properties, where a property is defined as a name/value pair. Property sets are a convenience mechanism to group properties and then reference them from other ADF Business Components objects. Properties defined in a property set can be configured to be translatable, in which case the translations are stored in a message bundle file owned by the property set.

Property sets can be used for a variety of functions, such as control hints and error messages. A property set may contain control hints and other custom properties, and you can associate them with multiple attributes of different objects.

Note:

Take care when defining property sets that contain translatable content. Be sure not to "overload" common terms in different contexts. For example, the term "Name" might be applied to both an object and a person in one language, but then translated into two different terms in a target language. Even though a term in several contexts might be the same in the source language, a separate distinguishable term should be used for each context.

Property sets can be used with entity objects and their attributes, view objects and their attributes, and application modules.

4.5.1 How to Define a Property Set

To define a property set, you create a new property set using a dialog and then specify properties using the Property Inspector.

To define a property set:

  1. In the Application Navigator, right-click the project where you want to create the property set, and choose New.

  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Property Set, and click OK.

    Figure 4-10 Property Set in New Gallery

    Image of New Gallery showing Property Set selected.
  3. In the Create Property Set dialog, enter the name and location of the property set and click OK.

  4. From the View menu, choose Property Inspector.

  5. In the Property Inspector, define the properties for the property set.

4.5.2 How to Apply a Property Set

After you have created the property set, you can apply the property set to an entity object or attribute, and use the defined properties (or override them, if necessary).

To apply a property set to an entity object or view object:

  1. In the Application Navigator, double-click the desired object (entity object or view object).

  2. In the overview editor, click the General navigation tab, and then click the Edit icon next to the Property Set line.

  3. Select the appropriate property set, and click OK.

To apply a property set to an attribute:

  1. In the Application Navigator, double-click the desired object (entity object or view object).

  2. In the overview editor, click the Attributes navigation tab, and double-click the attribute you want to edit.

  3. In the Edit Attribute dialog, click the first node to view the general properties of the attribute.

    For view objects, it is the View Attribute node. For entity objects, it is the Entity Attribute node.

  4. In the Property Set dropdown list, select the appropriate property set, and click OK.

4.6 Defining Attribute Control Hints for Entity Objects

If you are familiar with previous versions of ADF business components, you may have used control hints. Control hints allow you to define label text, tooltip, and format mask hints for entity object attributes. The UI hints you define on your business domain layer are inherited by any entity-based view objects as well. You can also set additional control hints on view objects and application modules in a similar manner.

4.6.1 How to Add Attribute Control Hints

To add attribute control hints to an entity object, use the overview editor.

To add attribute control hints to an entity object:

  1. In the Application Navigator, double-click the desired entity object.

  2. In the overview editor, click the Attributes navigation tab, and double-click the attribute you want to edit.

  3. In the Edit Attribute dialog, click the Control Hints node to view the attribute's control hints.

  4. Specify control hints as necessary, and then click OK.

    For example, Figure 4-11 shows control hints defined for the attribute ExpireDate of the PaymentOptionEO entity object. The defined hints include the following:

    • Format Type to Simple Date

    • Format mask of yyyy-MM-dd

    Figure 4-11 Edit Attribute Dialog, Control Hints Node

    Image of Setting UI Control Hints in Attribute Editor

Note:

Java defines a standard set of format masks for numbers and dates that are different from those used by the Oracle database's SQL and PL/SQL languages. For reference, see the Javadoc for the java.text.DecimalFormat and java.text.SimpleDateFormat classes.

4.6.2 What Happens When You Add Attribute Control Hints

When you define attribute control hints for an entity object, JDeveloper creates a resource bundle file in which to store them. The hints that you define can be used by generated forms and tables in associated view clients. The type of file and its granularity are determined by Resource Bundle options in the Project Properties dialog. For more information, see Section 4.7, "Working with Resource Bundles."

4.6.3 How to Define Formatters and Masks

When you set the Format Type control hint (in the Edit Attribute dialog) for an attribute (for example, to Simple Date), you can also specify a format mask for the attribute to customize how the UI displays the value. If the mask you want to use is not listed in the Format dropdown list, you can simply type it into the field.

Not all formatters require format masks. Specifying a format mask is only needed if that formatter type requires it. For example, the date formatter requires a format mask, but the currency formatter does not. In fact the currency formatter does not support format mask at all.

The mask elements that you can use are defined by the associated Java format class. For information about the mask elements for the Simple Date format type, see the Javadoc for java.text.SimpleDateFormat. For information about the mask elements for the Number format type, see the Javadoc for java.text.DecimalFormat.

If you have a format mask that you will continue to use on multiple occasions, you can add it to the formatinfo.xml file, so that it is available from the Format dropdown list in the Edit Attribute dialog. The entries in this file define the format masks and formatter classes for a domain class. Example 4-3 shows the format definitions for the java.util.Date domain.

Note:

You can find the formatinfo.xmlfile in the BC4J subdirectory of the JDeveloper system directory (for example, C:\Documents and Settings\username\Application Data\JDeveloper\system##\o.BC4J\formatinfo.xml).

Example 4-3 Format Definitions for java.util.Date in formatinfo.xml

<?xml version="1.0"?><FORMATTERS>
. . . 
  <DOMAIN CLASS="java.util.Date">
     <FORMATTER name="Simple Date" class="oracle.jbo.format.DefaultDateFormatter">
        <FORMAT text="yyyy-MM-dd" />
        <FORMAT text="EEE, MMM d, ''yy"  />
        <FORMAT text="dd-MM-yy" />
        <FORMAT text="dd-MMM-yyyy" />
        <FORMAT text="dd/MMM/yyyy" />
     </FORMATTER>
  </DOMAIN>
. . . 
</FORMATTERS>

The definition of the format mask belongs to a formatter and a domain class, and includes the text specification of the mask as it appears in the Edit Attribute dialog. When you specify the Format Type (FORMATTER name) for an attribute of a given type (DOMAIN CLASS), the masks (FORMAT text) appear in the Format dropdown list.

To map a formatter to a domain for use with control hints, you can either amend one of the default formatters provided in the oracle.jbo.format package, or create a new formatter class by extending the oracle.jbo.format.Formatter class. The default formatters provided with JDeveloper aggregate the formatters provided in the java.text package.

It is not necessary to create new domain to map a formatter. You can use an existing domain when the business components project contains a domain of the same data type as the formatter.

To define a new format mask:

  1. Open the formatinfo.xml file in a text editor.

  2. Find the domain class and formatter name for which you want to add a format mask.

  3. Insert a new FORMAT entry within the FORMATTER element.

After defining a format mask, you can select the new format mask from the Format dropdown list in the Edit Attribute dialog.

Note:

If you create a new domain for the format mask, the XML definition of the formatter must include a DOMAIN CLASS (which can be a new or existing one), the FORMATTER (which includes the name and class), and the list of FORMAT definitions the formatter class specifies.

4.7 Working with Resource Bundles

When you define translatable strings (such as validator error messages, or attribute control hints for an entity object or view object), by default JDeveloper creates a project-level resource bundle file in which to store them. For example, when you define control hints for an entity object in the StoreFront project, JDeveloper creates the message bundle file named StoreFrontBundle.xxx for the package. The hints that you define can be used by generated forms and tables in associated view clients.

The resource bundle option that JDeveloper uses is determined by an option on the Resource Bundle page of the Project Properties dialog. By default JDeveloper sets the option to Properties Bundle, which produces a .properties file. For more information on this and other resource bundle options, see Section 4.7.1, "How to Set Message Bundle Options."

You can inspect the message bundle file for the entity object by selecting the object in the Application Navigator and looking in the corresponding Sources node in the Structure window. The Structure window shows the implementation files for the component you select in the Application Navigator.

Example 4-4 shows a sample message bundle file where the control hint information appears. The first entry in each String array is a message key; the second entry is the locale-specific String value corresponding to that key.

Example 4-4 Project Message Bundle Stores Locale-Sensitive Control Hints

AddressUsageEO_OwnerTypeCode_Error_0=Invalid OwnerTypeCode.
AddressUsageEO_UsageTypeCode_Error_0=Invalid UsageTypeCode.
OwnerTypeCode_CONTROLTYPE=105
PaymentOptionEO_RoutingIdentifier_Error_0=Please enter a valid routing identifier.
PaymentOptionsEO_PaymentTypeCode_Error_0=Invalid PaymentTypeCode.
PaymentTypeCode_CONTROLTYPE=105
PaymentOption_AccountNumber=Please enter a valid Account Number
MinPrice_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter
CostPrice_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter
UnitPrice_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter
OrderEO_GiftMessage=Please supply a message shorter than 200 characters
OrderEO=Please supply a gift message
DiscountBaseEO_DiscountAmount=Discount must be between 0 and 40%
 
oracle.fodemo.storefront.entities.PaymentOptionEO.ExpireDate_FMT_FORMAT=mm/yy
#Date range validation for ValidFrom and ValidTo dates
PaymentOptionEO_invalidDateRange_Error_0=Date range is invalid. {0} must be greater than {1}.
PaymentOptionEO_DateRange_Error_0=Invalid date range.{0} should be greater than {1}.
 
oracle.fodemo.storefront.entities.PaymentOptionEO.ValidFromDate_LABEL=Valid From Date
oracle.fodemo.storefront.entities.PaymentOptionEO.ValidToDate_LABEL=Valid To Date
OrderItemsVO_ImageId_Rule_0=ImageId not found
oracle.fodemo.storefront.store.queries.AddressesVO.Address1_LABEL=Address
oracle.fodemo.storefront.store.queries.AddressesVO.PostalCode_LABEL=Post Code or ZIP
. . . 

4.7.1 How to Set Message Bundle Options

The resource bundle option JDeveloper uses to save control hints and other translatable strings is determined by an option on the Resource Bundle page of the Project Properties dialog. By default JDeveloper sets the option to Properties Bundle which produces a .properties file.

To set resource bundle options for your project

  1. In the Application Navigator, right-click the project and choose Project Properties.

  2. Click Resource Bundle.

  3. Select whether to use project or custom settings.

    If you select Use Custom Settings, the settings apply only to your work with the current project. They are preserved between sessions, but are not recorded with the project and cannot be shared with other users. If you select Use Project Settings, your choices are recorded with the project and can be shared with others who use the project.

  4. Specify your preference with the following options by selecting or deselecting the option:

    • Automatically Synchronize Bundle

    • Warn About Hard-coded Translatable Strings

    • Always Prompt for Description

    For more information on these options, click Help to see the online help.

  5. Select your choice of resource bundle granularity.

    • One Bundle Per Project (default)

    • One Bundle Per File

    • Multiple Shared Bundles (not available for ADF Business Components)

  6. Select the type of file to use.

    • List Resource Bundle

      The ListResourceBundle class manages resources in a name/value array. Each ListResourceBundle class is contained within a Java class file. You can store any locale-specific object in a ListResourceBundle class.

    • Properties Bundle (default)

      A text file containing translatable text in name/value pairs. Property files (like the one shown in Example 4-4) can contain values only for String objects. If you need to store other types of objects, you must use a ListResourceBundle instead.

    • Xliff Resource Bundle

      The XML Localization Interchange File Format (XLIFF) is an XML-based format for exchanging localization data.

  7. Click OK to apply your settings and close the dialog.

4.7.2 How to Use Multiple Resource Bundles

When you define translatable strings (for example, for attribute control hints), the Select Text Resource dialog allows you to enter a new string or select one that is already defined in the default resource bundle for the object. You can also use a different resource bundle if necessary. This is helpful when you use a common resource bundle that is shared between projects.

To use strings in a nondefault resource bundle:

  1. In the Select Text Resource dialog, select the bundle you want to use from the Resource Bundle dropdown list.

    If the desired resource bundle is not included in the Resource Bundle dropdown list, click the Browse icon to locate and select the resource bundle you want to use.

    The dialog displays the strings that are currently defined in the selected resource bundle.

  2. Select an existing string and click Select, or enter a new string and click Save and Select.

    If you entered a new string it is written to the selected resource bundle.

4.7.3 How to Internationalize the Date Format

Internationalizing the model layer of an application built using ADF Business Components entails producing translated versions of each component message bundle file. For example, the Italian version of the OrdersImplMsgBundle message bundle would be a class named OrdersImplMsgBundle_it and a more specific Swiss Italian version would have the name OrdersImplMsgBundle_it_ch. These classes typically extend the base message bundle class, and contain entries for the message keys that need to be localized, together with their localized translation.

Example 4-5 shows the Italian version of an entity object message bundle. Notice that in the Italian translation, the format masks for RequestDate and AssignedDate have been changed to dd/MM/yyyy HH:mm. This ensures that an Italian user will see a date value like May 3rd, 2006, as 03/05/2006 15:55, instead of 05/03/2006 15:55, which the format mask in the default message bundle would produce. Notice the overridden getContents() method. It returns an array of messages with the more specific translated strings merged together with those that are not overridden from the superclass bundle. At runtime, the appropriate message bundles are used automatically, based on the current user's locale settings.

Example 4-5 Localized Entity Object Component Message Bundle for Italian

package devguide.model.entities.common;
import oracle.jbo.common.JboResourceBundle;
public class ServiceRequestImplMsgBundle_it 
       extends ServiceRequestImplMsgBundle {
  static final Object[][] sMessageStrings = {
    { "AssignedDate_FMT_FORMAT", "dd/MM/yyyy HH:mm" }, 
    { "AssignedDate_LABEL", "Assegnato il" }, 
    { "AssignedTo_LABEL", "Assegnato a" }, 
    { "CreatedBy_LABEL", "Aperto da" }, 
    { "ProblemDescription_LABEL", "Problema" }, 
    { "RequestDate_FMT_FORMAT", "dd/MM/yyyy HH:mm" },
    { "RequestDate_LABEL", "Aperto il" }, 
    { "RequestDate_TOOLTIP", "La data in cui il ticket è stato aperto" },
    { "Status_LABEL", "Stato" }, 
    { "SvrId_LABEL", "Ticket" }
  };
  public Object[][] getContents() {    return super.getMergedArray(sMessageStrings, super.getContents());  }
}

4.8 Defining Business Logic Groups

Business logic groups allow you to encapsulate a set of related control hints, default values, and validation logic. A business logic group is maintained separate from the base entity in its own file, and can be enabled dynamically based on context values of the current row.

This is useful, for example, for an HR application that defines many locale-specific validations (like national identifier or tax law checks) that are maintained by a dedicated team for each locale. The business logic group eases maintenance by storing these validations in separate files, and optimizes performance by loading them only when they are needed.

Each business logic group contains a set of business logic units. Each unit identifies the set of business logic that is loaded for the entity, based on the value of the attribute associated with the business logic group.

For example, you can define a business logic group for an Employee entity object, specifying the EmpRegion attribute as the discriminator. Then define a business logic unit for each region, one that specifies a range validator for the employee's salary. When the application loads a row from the Employee entity, the appropriate validator for the EmpSalary attribute is loaded (based on the value of the EmpRegion attribute).

In another example, from the StoreFront module of the Fusion Order Demo application, the PersonEO entity object has a business logic group called PersonTypeCodeGroup that uses PersonTypeCode as the discriminator attribute. Because this attribute has three valid values (CUST, STAFF, and SUPP), there are three corresponding business logic units.

In this scenario, each business logic unit contains new or modified business logic that pertains only to that person type:

  • The CUST business logic unit contains logic that pertains to customers. For example, it contains a validator that checks for a phone number because all customers must have a phone number.

  • The STAFF business logic unit contains logic that pertains to staff members. For example, it contains a validator that constrains the credit limit.

  • The SUPP business logic unit contains logic that pertains to suppliers. For example, it contains a validator that makes sure the ContactByAffiliatesFlag attribute is set to N, because suppliers cannot be contacted by affiliates.

4.8.1 How to Create a Business Logic Group

You create the business logic group for an entity object from the overview editor.

To create a business logic group:

  1. In the Application Navigator, double-click the entity for which you want to create a business logic group.

  2. In the overview editor, click the General navigation tab.

  3. On the General page, expand the Business Logic Groups section, and click the Add icon.

  4. In the creation dialog, select the appropriate group discriminator attribute and specify a name for the group.

    Tip:

    To enhance the readability of your code, you can name the group to reflect the discriminator. For example, if the group discriminator attribute is PersonTypeCode, you can name the business logic group PersonTypeCodeGroup.
  5. Click OK.

The new business logic group is added to the table in the overview editor. After you have created the group, you can add business logic units to it.

4.8.2 How to Create a Business Logic Unit

You can create a business logic unit from the New Gallery, or directly from the context menu of the entity that contains the business logic group.

To create a business logic unit:

  1. In the Application Navigator, right-click the entity that contains the business logic group and choose New Entity Business Logic Unit from the context menu.

  2. In the Create Business Logic Unit dialog, specify the name of the base entity and select the appropriate business logic group.

  3. Enter a name for the business logic unit.

    The name of each business logic unit must reflect a valid value of the group discriminator attribute with which this business logic unit will be associated. For example, if the group discriminator attribute is PersonTypeCode, the name of the business logic unit associated with the PersonTypeCode value of STAFF must be STAFF.

  4. Specify the package for the business logic unit.

    Note:

    The package for the business logic unit does not need to be the same as the package for the base entity or the business logic group. This allows you to develop and deliver business logic units separately from the core application.
  5. Click OK.

JDeveloper creates the business logic unit and opens it in the overview editor. The name displayed for the business logic unit in the Application Navigator contains the name of the entity object and business logic group in the format EntityName_BusLogicGroupName_BusLogicUnitName. For example, when you create a business logic unit with the name CUST in the PersonTypeCodeGroup business logic group of the PersonEO entity object, the displayed name of the business logic unit is PersonEO_PersonTypeCodeGroup_CUST.

After you have created the unit, you can redefine the business logic for it.

4.8.3 How to Add Logic to a Business Logic Unit

After you have created a business logic unit, you can open it in the overview editor and add business logic (such as adding an entity-level validator) just as you would in the base entity.

To add an entity validator to a business logic unit:

  1. In the Application Navigator, double-click the business logic unit.

  2. In the overview editor, click the Business Rules navigation tab.

  3. On the Business Rules page, select the Entity Validators folder and click the Add icon.

  4. Define your validation rule, and click OK.

For example, the PersonEO entity object in the StoreFront module of the Fusion Order Demo application has a business logic unit called PersonEO_PersonTypeCodeGroup_CUST. This business logic unit has an entity validator that checks for the presence of a phone number to ensure that all persons who are customers have a phone number.

4.8.4 How to Override Attributes in a Business Logic Unit

When you view the Attributes page for the business logic unit (in the overview editor), you can see that the Extends column in the attributes table shows that the attributes are "extended" in the business logic unit. Extended attributes are editable only in the base entity, not in the business logic unit. To implement changes in the business logic unit rather than the base entity, you must define attributes as overridden in the business logic unit before you edit them.

To override attributes in a business logic unit:

  1. In the Application Navigator, double-click the business logic unit.

  2. In the overview editor, click the Attributes navigation tab.

  3. On the Attributes page, select the desired attribute and click the Override button.

After you make an attribute overridden, you can edit the attribute as you normally would by double-clicking the attribute to open it in the Edit Attribute dialog. You will notice that in an overridden attribute, you are limited to making modifications to only control hints, validators, and default values.

4.8.5 What Happens When You Create a Business Logic Group

When you create a business logic group, JDeveloper adds a reference to the group in the base entity's XML file. Example 4-6 shows the code added to the base entity's XML file for the business logic group.

Example 4-6 XML Code in the Base Entity for a Business Logic Group

<BusLogicGroup
    Name="PersonTypeCodeGroup"
    DiscrAttrName="PersonTypeCode"/>

When you create a business logic unit, JDeveloper generates an XML file similar to that of an entity object. Example 4-7 shows XML code for a business logic unit.

Note:

The package for the business logic unit does not need to be the same as the package for the base entity or the business logic group. This allows you to develop and deliver business logic units separately from the core application.

Example 4-7 XML Code for a Business Logic Unit

<Entity
  xmlns="http://xmlns.oracle.com/bc4j"
  Name="PersonEO_PersonTypeCodeGroup_CUST"
  Version="11.1.1.54.6"
  Extends="oracle.fodemo.storefront.entities.PersonEO"
  DBObjectType="table"
  DBObjectName="PERSONS"
  BindingStyle="OracleName"
  UseGlueCode="false"
  BusLogicGroupName="PersonTypeCodeGroup"
  BusLogicUnitName="CUST"
  xmlns:validation="http://xmlns.oracle.com/adfm/validation">
  <DesignTime>
    <Attr Name="_codeGenFlag2" Value="Access"/>
    <AttrArray Name="_publishEvents"/>
  </DesignTime>
  <validation:ExpressionValidationBean
    Name="PersonEO_PersonTypeCodeGroup_CUST_Rule_0"
    OperandType="EXPR"
    Inverse="false">
    <validation:MsgIds>
      <validation:Item
        Value="CUST_PHONE_REQUIRED"/>
    </validation:MsgIds>
    <validation:TransientExpression>
      <![CDATA[if (PhoneNumber == null && MobilePhoneNumber == null)
        return false;
        else return true;]]>
    </validation:TransientExpression>
  </validation:ExpressionValidationBean>
  <ResourceBundle>
    <PropertiesBundle
      PropertiesFile="oracle.fodemo.storefront.entities.common.PersonEO_PersonTypeCodeGroup_CUSTMsgBundle"/>
  </ResourceBundle>
</Entity>

4.8.6 What Happens at Runtime: Invoking a Business Logic Group

When a row is loaded in the application at runtime, the entity object decides which business logic units to apply to it.

The base entity maintains a list of business logic groups. Each group references the value of an attribute on the entity, and this value determines which business logic unit to load for that group. This evaluation is performed for each row that is loaded.

If the logic for determining which business logic unit to load is more complex than just a simple attribute value, you can create a transient attribute on the entity object, and use a Groovy expression to determine the value of the transient attribute.

4.9 Configuring Runtime Behavior Declaratively

Entity objects offer numerous declarative features to simplify implementing typical enterprise business applications. Depending on the task, sometimes the declarative facilities alone may satisfy your needs. The declarative runtime features that describe the basic persistence features of an entity object are covered in this section, while declarative validation and business rules are covered in Chapter 7, "Defining Validation and Business Rules Declaratively."

Note:

It is possible to go beyond the declarative behavior to implement more complex business logic or validation rules for your business domain layer when needed. In Chapter 8, "Implementing Validation and Business Rules Programmatically," you'll see some of the most typical ways that you extend entity objects with custom code.

Also, it is important to note as you develop your application that the business logic you implement, either programmatically or declaratively, should not assume that the attributes of an entity object or view row will be set in a particular order. This will cause problems if the end user enters values for the attributes in an order other than the assumed one.

4.9.1 How to Configure Declarative Runtime Behavior

To configure the declarative runtime behavior of an entity object, use the overview editor.

To configure the declarative runtime behavior of an entity object:

  1. In the Application Navigator, double-click an entity object.

  2. In the overview editor, click the General navigation tab to view the name and package of the entity object, and configure aspects of the object at the entity level, such as its associated schema, alternative keys, custom properties, and security.

  3. Click the Attributes navigation tab to create or delete attributes that represent the data relevant to an entity object, and configure aspects of the attribute, such as validation rules, custom properties, and security.

    Select an attribute and click the Edit icon to access the properties of the attribute. For information on how to set these properties, see Section 4.10, "Setting Attribute Properties."

    Tip:

    If your entity has a long list of attribute names, there's a quick way to find the one you're looking for. In the Structure window with the Attributes node expanded, you can begin to type the letters of the attribute name and JDeveloper performs an incremental search to take you to its name in the tree.
  4. Click the Business Rules navigation tab to define declarative validators for the entity object and its attributes. For more information, see Chapter 7, "Defining Validation and Business Rules Declaratively."

  5. Click the Java navigation tab to select the classes you generate for custom Java implementation. You can use the Java classes for such things as defining programmatic business rules, as in Chapter 8, "Implementing Validation and Business Rules Programmatically."

  6. Click the Business Events navigation tab to define events that your entity object can use to notify others of interesting changes in its state, optionally including some or all of the entity object's attributes in the delivered event. For more information about business events, see Section 4.11, "Creating Business Events."

  7. Click the View Accessors navigation tab to create and manage view accessors. For more information, see Section 10.4.1, "How to Create a View Accessor for an Entity Object or View Object."

4.9.2 What Happens When You Configure Declarative Runtime Behavior

The declarative settings that describe and control an entity object's runtime behavior are stored in its XML component definition file. When you use the overview editor to modify settings of your entity, JDeveloper updates the component's XML definition file and optional custom Java files.

4.10 Setting Attribute Properties

The declarative framework helps you set attribute properties easily. In all cases, you set these properties in the Edit Attribute dialog, which you can access from the Attributes page of the overview editor.

4.10.1 How to Set Database and Java Data Types for an Entity Object Attribute

The Persistent property controls whether the attribute value corresponds to a column in the underlying table, or whether it is just a transient value. If the attribute is persistent, the Database Column area lets you change the name of the underlying column that corresponds to the attribute and indicate its column type with precision and scale information (e.g. VARCHAR2(40) or NUMBER(4,2)). Based on this information, at runtime the entity object enforces the maximum length and precision/scale of the attribute value, and throws an exception if a value does not meet the requirements.

Both the Business Components from Tables wizard and the Create Entity Object wizard infer the Java type of each entity object attribute from the SQL type of the database column type of the column to which it is related.

Note:

The project's Type Map setting also plays a role in determining the Java data type. You specify the Type Map setting when you initialize your business components project, before any business components are created. For more information, see Section 3.3.1, "Choosing a Connection, SQL Flavor, and Type Map."

The Attribute Type field (in the Edit Attribute dialog) allows you to change the Java type of the entity attribute to any type you might need. The Database Column Type field reflects the SQL type of the underlying database column to which the attribute is mapped. The value of the Database Column Name field controls the column to which the attribute is mapped.

Your entity object can handle tables with various column types, as listed in Table 4-1. With the exception of the java.lang.String class, the default Java attribute types are all in the oracle.jbo.domain and oracle.ord.im packages and support efficiently working with Oracle database data of the corresponding type. The dropdown list for the Attribute Type field includes a number of other common Java types that are also supported.

Table 4-1 Default Entity Object Attribute Type Mappings

Oracle Column Type Entity Column Type Entity Java Type

NVARCHAR2(n), VARCHAR2(n), NCHAR VARYING(n), VARCHAR(n)

VARCHAR2

java.lang.String

NUMBER

NUMBER

oracle.jbo.domain.Number

DATE

DATE

oracle.jbo.domain.Date

TIMESTAMP(n), TIMESTAMP(n) WITH TIME ZONE, TIMESTAMP(n) WITH LOCAL TIME ZONE

TIMESTAMP

java.sql.Timestamp

LONG

LONG

java.lang.String

RAW(n)

RAW

oracle.jbo.domain.Raw

LONG RAW

LONG RAW

oracle.jbo.domain.Raw

ROWID

ROWID

oracle.jbo.domain.RowID

NCHAR, CHAR

CHAR

oracle.jbo.domain.Char

CLOB

CLOB

oracle.jbo.domain.ClobDomain

NCLOB

NCLOB

oracle.jbo.domain.NClobDomain

BLOB

BLOB

oracle.jbo.domain.BlobDomain

BFILE

BFILE

oracle.jbo.domain.BFileDomain

ORDSYS.ORDIMAGE

ORDSYS.ORDIMAGE

oracle.ord.im.OrdImageDomain

ORDSYS.ORDVIDEO

ORDSYS.ORDVIDEO

oracle.ord.im.OrdVideoDomain

ORDSYS.ORDAUDIO

ORDSYS.ORDAUDIO

oracle.ord.im.OrdAudioDomain

ORDSYS.ORDDOC

ORDSYS.ORDDOC

oracle.ord.im.OrdDocDomain


Note:

In addition to the types mentioned here, you can use any Java object type as an entity object attribute's type, provided it implements the java.io.Serializable interface.

4.10.2 How to Indicate Data Type Length, Precision, and Scale

When working with types that support defining a maximum length like VARCHAR2(n), the Database Column Type field (in the Edit Attribute dialog) includes the maximum attribute length as part of the value. For example, an attribute based on a VARCHAR2(10) column in the database will initially reflect the maximum length of 10 characters by showing VARCHAR2(10) as the database column type. If for some reason you want to restrict the maximum length of the String-valued attribute to fewer characters than the underlying column will allow, just change the maximum length of the Database Column Type value.

For example, if the EMAIL column in the PERSONS table is VARCHAR2(50), then by default the Email attribute in the Persons entity object defaults to the same. But if you know that the actual email addresses are always 8 characters or fewer, you can update the database column type for the Email attribute to be VARCHAR2(8) to enforce a maximum length of 8 characters at the entity object level.

The same holds for attributes related to database column types that support defining a precision and scale like NUMBER(p[,s]). For example, to restrict an attribute based on a NUMBER(7,2) column in the database to instead have a precision of 5 and a scale of 1, just update the value of the Database Column Type field to be NUMBER(5,1).

4.10.3 How to Control the Updatability of an Attribute

The Updatable property controls when the value of a given attribute can be updated. You can select the following values:

  • Always, the attribute is always updatable

  • Never, the attribute is read-only

  • While New, the attribute can be set during the transaction that creates the entity row for the first time, but after being successfully committed to the database the attribute is read-only

Note:

In addition to the static declaration of updatability, you can also add custom code in the isAttributeUpdateable() method of the entity to determine the updatability of an attribute at runtime.

4.10.4 How to Make an Attribute Mandatory

Select the Mandatory checkbox if the field is required. The mandatory property is enforced during entity-level validation at runtime (and not when the attribute validators are run).

4.10.5 How to Define the Primary Key for the Entity

The Primary Key property indicates whether the attribute is part of the key that uniquely identifies the entity. Typically, you use a single attribute for the primary key, but multiattribute primary keys are fully supported.

At runtime, when you access the related Key object for any entity row using the getKey() method, this Key object contains the value of the primary key attribute for the entity object. If your entity object has multiple primary key attributes, the Key object contains each of their values. It is important to understand that these values appear in the same relative sequential order as the corresponding primary key attributes in the entity object definition.

For example, if the OrderItemEO entity object has multiple primary key attributes OrderId and LineItemId. On the Entity Attribute page of the overview editor, OrderId is first, and LineItemId is second. An array of values encapsulated by the Key object for an entity row of type OrderItemEO will have these two attribute values in exactly this order.

It is crucial to be aware of the order in which multiple primary key attributes appear on the Entity Attributes page. If you try to use findByPrimaryKey() to find an entity with a multiattribute primary key, and the Key object you construct has these multiple primary key attributes in the wrong order, the entity row will not be found as expected.

4.10.6 How to Define a Static Default Value

The Value field (in the Edit Attribute dialog) allows you to specify a static default value for the attribute when the Value Type is set to Literal. For example, you can set the default value of the ServiceRequest entity object's Status attribute to Open, or set the default value of the User entity object's UserRole attribute to user.

Note:

When more than one attribute is defaulted for an entity object, the attributes are defaulted in the order in which they appear in the entity object's XML file.

4.10.7 How to Define a Default Value Using a Groovy Expression

You can use a Groovy expression to define a default value for an attribute. This approach is useful if you want to be able to change default values at runtime, but if the default value is always the same, the value is easier to see and maintain using the Default field (in the Edit Attribute dialog). For general information about using Groovy, see Section 3.6, "Overview of Groovy Support."

To define a default value using a Groovy expression:

  1. In the Application Navigator, double-click the entity to open the overview editor.

  2. In the overview editor, click the Attributes navigation tab.

  3. On the Attributes page, select the desired attribute and click the Edit icon.

  4. In the Edit Attribute dialog, select Expression for the value type, and click Edit (next to the Value field).

  5. Enter a Groovy expression in the field provided, and click OK.

  6. Click OK.

4.10.8 What Happens When You Create a Default Value Using a Groovy expression

When you define a default value using a Groovy expression, a <TransientExpression> tag is added to the entity object's XML file within the appropriate attribute. Figure 4-11 shows sample XML code for an Groovy expression that gets the current date for a default value.

Example 4-8 Default Date Value

<TransientExpression>
    <![CDATA[
        newValue <= adf.currentDate
    ]]>
</TransientExpression> 

4.10.9 How to Synchronize with Trigger-Assigned Values

If you know that the underlying column value will be updated by a database trigger during insert or update operations, you can enable the respective Insert or Update checkboxes in the Refresh After area (in the Edit Attribute dialog) to ensure the framework automatically retrieves the modified value and keeps the entity object and database row in sync. The entity object will use the Oracle SQL RETURNING INTO feature, while performing the INSERT or UPDATE to return the modified column back to your application in a single database roundtrip.

Note:

If you create an entity object for a synonym that resolves to a remote table over a DBLINK, use of this feature will give an error at runtime like:
JBO-26041: Failed to post data to database during "Update"
## Detail 0 ##
ORA-22816: unsupported feature with RETURNING clause

Section 38.6, "Basing an Entity Object on a Join View or Remote DBLink" describes a technique to circumvent this database limitation.

4.10.10 How to Get Trigger-Assigned Primary Key Values from a Database Sequence

One common case for refreshing an attribute after insert occurs when a primary key attribute value is assigned by a BEFORE INSERT FOR EACH ROW trigger. Often the trigger assigns the primary key from a database sequence using PL/SQL logic. Example 4-9 shows an example of this.

Example 4-9 PL/SQL Code Assigning a Primary Key from a Database Sequence

CREATE OR REPLACE TRIGGER ASSIGN_SVR_ID
BEFORE INSERT ON SERVICE_REQUESTS FOR EACH ROW
BEGIN
 IF :NEW.SVR_ID IS NULL OR :NEW.SVR_ID < 0 THEN
   SELECT SERVICE_REQUESTS_SEQ.NEXTVAL
     INTO :NEW.SVR_ID
     FROM DUAL;
   END IF;
END;

In the Edit Attribute dialog, you can set the value of the Type field to the built-in data type named DBSequence and the primary key will be assigned automatically by the database sequence. Setting this data type automatically selects the refresh after Insert checkbox.

Note:

The sequence name shown on the Sequence tab is used only at design time when you use the Create Database Tables feature described in Section 4.2.6, "How to Create Database Tables from Entity Objects." The sequence indicated here will be created along with the table on which the entity object is based.

When you create a new entity row whose primary key is a DBSequence, a unique negative number is assigned as its temporary value. This value acts as the primary key for the duration of the transaction in which it is created. If you are creating a set of interrelated entities in the same transaction, you can assign this temporary value as a foreign key value on other new, related entity rows. At transaction commit time, the entity object issues its INSERT operation using the RETURNING INTO clause to retrieve the actual database trigger-assigned primary key value. In a composition relationship, any related new entities that previously used the temporary negative value as a foreign key will get that value updated to reflect the actual new primary key of the master.

You will typically also set the Updatable property of a DBSequence-valued primary key to Never. The entity object assigns the temporary ID, and then refreshes it with the actual ID value after the INSERT operation. The end user never needs to update this value.

For information on how to implement this functionality for an association that is not a composition, see Section 38.8.3.3, "Understanding Associations Based on DBSequence-Valued Primary Keys."

Note:

For a metadata-driven alternative to the DBSequence approach, see Section 4.12.5, "Assigning the Primary Key Value Using an Oracle Sequence."

4.10.11 How to Protect Against Losing Simultaneously Updated Data

At runtime, the framework provides automatic "lost update" detection for entity objects to ensure that a user cannot unknowingly modify data that another user has updated and committed in the meantime. Typically, this check is performed by comparing the original values of each persistent entity attribute against the corresponding current column values in the database at the time the underlying row is locked. Before updating a row, the entity object verifies that the row to be updated is still consistent with the current state of the database. If the row and database state are inconsistent, then the entity object raises the RowInconsistentException.

You can make the lost update detection more efficient by identifying any attributes of your entity whose values you know will be updated whenever the entity is modified. Typical candidates include a version number column or an updated date column in the row. The change-indicator attribute's value might be assigned by a database trigger you've written and refreshed in the entity object using the Refresh After Insert and Refresh After Update options (in the Edit Attribute dialog). Alternatively, you can indicate that the entity object should manage updating the change-indicator attribute's value using the history attribute feature described in Section 4.10.12, "How to Track Created and Modified Dates Using the History Column." To detect whether the row has been modified since the user queried it in the most efficient way, select the Change Indicator option to compare only the change-indicator attribute values.

4.10.12 How to Track Created and Modified Dates Using the History Column

If you need to keep track of historical information in your entity object, such as when an entity was created or modified and by whom, or the number of times the entity has been modified, you specify an attribute with the History Column option selected (in the Edit Attribute dialog).

If an attribute's data type is Number, String, or Date, and if it is not part of the primary key, then you can enable this property to have your entity automatically maintain the attribute's value for historical auditing. How the framework handles the attribute depends which type of history attribute you indicate:

  • Created On: This attribute is populated with the time stamp of when the row was created. The time stamp is obtained from the database.

  • Created By: The attribute is populated with the name of the user who created the row. The user name is obtained using the getUserPrincipalName() method on the Session object.

  • Modified On: This attribute is populated with the time stamp whenever the row is updated/created.

  • Modified By: This attribute is populated with the name of the user who creates or updates the row.

  • Version Number: This attribute is populated with a long value that is incremented whenever a row is created or updated.

4.10.13 How to Configure Composition Behavior

An entity object exhibits composition behavior when it creates (or composes) other entities, such as an OrderEO entity creating a OrderItemEO entity. This additional runtime behavior determines its role as a logical container of other nested entity object parts.

Note:

Composition also affects the order in which entities are validated. For more information, see Section 7.2.3, "Understanding the Impact of Composition on Validation Order."

The features that are always enabled for composing entity objects are described in the following sections:

The additional features, and the properties that affect their behavior, are described in the following sections:

4.10.13.1 Orphan-Row Protection for New Composed Entities

When a composed entity object is created, it performs an existence check on the value of its foreign key attribute to ensure that it identifies an existing entity as its owning parent entity. At create time, if no foreign key is found or else a value that does not identify an existing entity object is found, the entity object throws an InvalidOwnerException instead of allowing an orphaned child row to be created without a well-identified parent entity.

Note:

The existence check finds new pending entities in the current transaction, as well as existing ones in the database if necessary.

4.10.13.2 Ordering of Changes Saved to the Database

Composition behavior ensures that the sequence of data manipulation language (DML) operations performed in a transaction involving both composing and composed entity objects is performed in the correct order. For example, an INSERT statement for a new composing parent entity object will be performed before the DML operations related to any composed children.

4.10.13.3 Cascade Update of Composed Details from Refresh-On-Insert Primary Keys

When a new entity row having a primary key configured to refresh on insert is saved, then after its trigger-assigned primary value is retrieved, any composed entities will have their foreign key attribute values updated to reflect the new primary key value.

There are a number of additional composition related features that you can control through settings on the Association Properties page of the Create Association wizard or the overview editor. Figure 4-12 shows the Relationships page for the OrderItemsOrdersFkAssoc association between two entity objects: OrderItemEO and OrderEO.

4.10.13.4 Cascade Delete Support

You can either enable or prevent the deletion of a composing parent while composed children entities exist. When the Implement Cascade Delete option (see Figure 4-12) is deselected, the removal of the composing entity object is prevented if it contains any composed children.

Figure 4-12 Composition Settings on Relationship Page of Overview Editor for Associations

Composition settings in overview editor for association

When selected, this option allows the composing entity object to be removed unconditionally together with any composed children entities. If the related Optimize for Database Cascade Delete option is deselected, then the composed entity objects perform their normal DELETE statement at transaction commit time to make the changes permanent. If the option is selected, then the composed entities do not perform the DELETE statement on the assumption that the database ON DELETE CASCADE constraint will handle the deletion of the corresponding rows.

4.10.13.5 Cascade Update of Foreign Key Attributes When Primary Key Changes

Select the Cascade Update Key Attributes option (see Figure 4-12) to enable the automatic update of the foreign key attribute values in composed entities when the primary key value of the composing entity is changed.

4.10.13.6 Locking of Composite Parent Entities

Select the Lock Top-Level Container option (see Figure 4-12) to control whether adding, removing, or modifying a composed detail entity row should attempt to lock the composing entity before allowing the changes to be saved.

4.10.13.7 Updating of Composing Parent History Attributes

Select the Update Top-Level History Columns option (see Figure 4-12) to control whether adding, removing, or modifying a composed detail entity object should update the Modified By and Modified On history attributes of the composing parent entity.

4.10.14 How to Set the Discriminator Attribute for Entity Object Inheritance Hierarchies

Sometimes a single database table stores information about several different kinds of logically related objects. For example, a payroll application might work with hourly, salaried, and contract employees all stored in a single EMPLOYEES table with an EMPLOYEE_TYPE column. In this case, the value of the EMPLOYEE_TYPE column contains values like H, S, or C to indicate respectively whether a given row represents an hourly, salaried, or contract employee. And while it is possible that many attributes and behavior are the same for all employees, certain properties and business logic may also depend on the type of employee.

In situations where common information exists across related objects, it may be convenient to represent these different types of entity objects using an inheritance hierarchy. For example, attributes and methods common to all employees can be part of a base Employee entity object, while subtype entity objects like HourlyEmployee, SalariedEmployee, and ContractEmployee extend the base Employee object and add additional properties and behavior. The Discriminator attribute setting is used to indicate which attribute's value distinguishes the type of row. Section 38.7, "Using Inheritance in Your Business Domain Layer," explains how to set up and use inheritance.

4.10.15 How to Define Alternate Key Values

Database primary keys are often generated from a sequence and may not be data you want to expose to the user for a variety of reasons. For this reason, it's often helpful to have alternate key values that are unique. For example, you might want to enforce that every customer have a unique email address. Because a customer may change their email address, you won't want to use that value as a primary key, but you still want the user to have a unique field they can use for login or other purposes.

Alternate keys are useful for direct row lookups via the findByKey class of methods. Alternate keys are frequently used for efficient uniqueness checks in the middle tier. For information on how to find out if a value is unique, see Section 7.4.1, "How to Ensure That Key Values Are Unique."

To define an alternate key, you use the Create Entity Constraint wizard.

To define alternate key values:

  1. In the Application Navigator, right-click an entity object and choose New Entity Constraint.

  2. Follow the steps in the Create Entity Constraint wizard to name your constraint and select the attribute or attributes that participate in the key.

  3. On the Properties page, select Alternate Key and choose the appropriate Key Properties options.

    For more information about the Key Properties options, press the F1 key or click Help.

4.10.16 What Happens When You Define Alternate Key Values

When you define alternate key values, a hashmap is created for fast access to entities that are already in memory.

4.10.17 What You May Need to Know About Alternate Key Values

The Unique key constraint is used only for forward generation of UNIQUE constraints in the database, not for alternate key values.

4.11 Creating Business Events

Business events raised from the model layer are useful for launching business processes and triggering external systems synchronization by way of the Oracle Mediator.

Oracle Mediator supports declarative subscriptions which map business events to actions. In other words, you can define and publish a business event (such as a new customer being created) in one component, and then subscribe to that event in another component so that a business process is notified when it occurs. You can then, in the subscribing component, proceed with an action you assign to that event (such as sending a welcome new customer email).

You declaratively define business events at the entity level. You may also specify conditions under which those events should be raised. Business events that meet the specified criteria are raised upon successful commit of the changed data. A business event is raised to the Mediator on a successful create, update, or delete of an entity object.

To implement a business event, you first create an event definition, then map that event definition to an event point, then publish that definition. After the business event is published, you can subscribe to the event from another component.

4.11.1 Introducing Event Definitions

An event definition describes an event that will be published and raised with an event system Mediator. An event definition is stored in an entity object's XML file with the elements shown in Table 4-2.

Table 4-2 Event Definition Elements for Entity Objects

Element Description

Event Name

Name of the event, for example, OrderUpdated

Payload

A list of attributes sent to the subscriber. Attributes marked as optional appear on payload only if changed.


4.11.2 Introducing Event Points

An event point is a place from which an event can be raised. On a successful commit, one of the event points shown in Table 4-3 can be raised to the Mediator for each entity in a transaction.

Table 4-3 Example Event Points Raised to the Mediator

DML Type Event Name Event Description

CREATE

EntityCreated

A new Entity has been created.

UPDATE

EntityUpdated

An existing Entity has been updated.

DELETE

EntityDeleted

An existing Entity has been deleted.


Note that no events are raised by default; all events are custom. When you create the event, you can specify the name and DML operation appropriately.

For each event point, you must specify which event definitions should be raised on a particular event point. In other words, you must declaratively map each event definition to an event point.

4.11.3 What You May Need to Know About Event Points

Transactional event delivery, where event delivery is part of the transaction, is not supported by the framework.

Synchronous events, where the publisher waits for further processing until the subscriber has confirmed event reception, is not supported by the framework.

4.11.4 How to Create a Business Event

To create a business event, use the Business Events page of the overview editor.

To create a business event:

  1. In the Application Navigator, double-click an entity object.

  2. In the overview editor, click the Business Events navigation tab.

  3. On the Business Events page, expand the Event Definitions section and click the New icon.

  4. In the Create Business Event Definition dialog, provide a name that describes this event, such as EmployeeContactInfoChanged.

  5. In the payload table, click New and Delete to select the appropriate attributes for this event.

    Alternatively, you can double-click the cell and pick the attributes you want.

    Note:

    Only attributes of supported types are displayed in the Entity Attribute column. While ClobDomain attributes are supported, very large clob data can impact performance.
  6. In the Value Sent field, choose whether the value should Always be sent, or Only if changed.

    The Only if changed option provides the best performance because the attribute will be considered optional for the payload. If you leave the default Always, the payload will require the attribute whether or not the value has changed. For more details about payload efficiency, see Section 4.11.6, "What You May Need to Know About Payload Size."

  7. Use the arrow buttons to rearrange the order of attributes.

    The order that the attributes appear in defines their order in the generated XSD. Since you'll be using the XSD to build your Fabric mediator and BPEL process, you might want the most frequently accessed attributes at the top.

  8. Click OK.

Repeat the procedure for each business event that you want to define. To publish an event, see Section 4.11.7, "How to Publish a Business Event."

4.11.5 What Happens When You Create a Business Event

When you create a business event, the entity object's XML file is updated with the event definition. Example 4-10 shows an example of the XML code for a business event. JDeveloper also generates an associated XSD file for the event schema that allows specification of required attributes and optional attributes. Required attributes correspond to Value Sent - Always in the Create Business Event Definition dialog, whereas optional attributes are those for which you changed Value Sent to Only if changed.

Example 4-10 XML Code for a Business Event

<EventDef
  Name="CustBusEvent1">
  <Payload>
    <PayloadItem
      AttrName="Order.OrderId"/>
    <PayloadItem
      AttrName="LineItemId"/>
    <PayloadItem
      AttrName="ProductBase.ProductId"
      SendOnlyIfChanged="true"/>
  </Payload>
</EventDef>

Example 4-11 shows an example of the XSD event schema for a business event.

Example 4-11 XSD Event Schema for a Business Event

<?xml version = '1.0' encoding = 'UTF-8'?>
<xs:schema targetNamespace="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO"
 xmlns="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO"
 elementFormDefault="qualified" attributeFormDefault="unqualified"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="CustBusEvent1Info">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="Order.OrderId" type="DecimalValuePair" minOccurs="1"/>
          <xs:element name="LineItemId" type="DecimalValuePair" minOccurs="1"/>
            <xs:element name="ProductBase.ProductId" type="DecimalValuePair" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>
   </xs:element>
   <xs:complexType name="ValuePair" abstract="true"/>
   <xs:complexType name="DecimalValuePair">
      <xs:complexContent>
         <xs:extension base="ValuePair">
            <xs:sequence>
               <xs:element name="newValue" minOccurs="0">
                  <xs:complexType>
                     <xs:complexContent>
                        <xs:extension base="xs:anyType">
                           <xs:attribute name="value" type="xs:decimal"/>
                        </xs:extension>
                     </xs:complexContent>
                  </xs:complexType>
               </xs:element>
               <xs:element name="oldValue" minOccurs="0">
                  <xs:complexType>
                     <xs:complexContent>
                        <xs:extension base="xs:anyType">
                           <xs:attribute name="value" type="xs:decimal"/>
                        </xs:extension>
                     </xs:complexContent>
                  </xs:complexType>
               </xs:element>
            </xs:sequence>
         </xs:extension>
      </xs:complexContent>
   </xs:complexType>
</xs:schema>

Example 4-12 shows an example of the EDL event definition for the entity object.

Example 4-12 EDL Event Definition for the Entity Object

<definitions 
      targetNamespace="/oracle/fodemo/storefront/entities/events/edl/OrderItemEO"
      xmlns:ns0="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO" 
      xmlns="http://schemas.oracle.com/events/edl">
   <schema-import 
        namespace="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO" 
        location="OrderItemEO.xsd"/>
   <event-definition name="CustBusEvent1">
      <content element="ns0:CustBusEvent1Info"/>
   </event-definition>
</definitions>

4.11.6 What You May Need to Know About Payload Size

The attributes of the associated entity object constitute the payload of a business event. The payload attributes for a business event are defined by the creator of the event. It isn't automatically optimized. When the event is defined, an attribute can be marked as sent Always or Only if changed. For events fired during creation, only new values are sent. For events fired during an update or delete, the new and old values are sent and only the attributes that should be based on the Value Sent setting. For best performance, you should include only the primary key attribute for delete events.

To support composition scenarios (such as a purchase order with line items), a child entity can raise events defined on the parent entity, and events defined on the child entity can include attributes from the parent entity. When a child entity raises an event on a parent entity, only a single event is raised for a particular top-level entity per transaction, regardless of how many times the child entity raises it.

When defining business events, remember that while ClobDomain attributes are supported, very large clob data can have performance implications.

4.11.7 How to Publish a Business Event

To publish a business event, use the Business Events page of the entity objects overview editor.

To publish a business event:

  1. In the Application Navigator, double-click an entity object.

  2. In the overview editor, click the Business Events navigation tab.

  3. On the Business Events page, expand the Event Publication section and click the Edit event publications icon.

  4. In the Edit Event Publications dialog, click New to create a new event.

  5. Double-click the new cell in Event column, and select the appropriate event.

  6. Double-click the corresponding cell in Event Point column, and select the appropriate event point action.

  7. You can optionally define conditions for raising the event using the Raise Conditions table.

  8. Click OK.

4.11.8 How to Subscribe to Business Events

After you have created a business event, you can subscribe and respond to the event.

Before you begin:

  • Open (or create) the SCA project that will subscribe to the business event.

To subscribe to a business event:

  1. Using the file system, copy the XSD and event definition files for the business event into your SCA project's source path.

  2. In the Application Navigator, right-click the project, and choose New.

  3. In the New Gallery, expand SOA Tier, select Service Components and then Mediator, and click OK.

  4. In the Create Mediator dialog, select the Subscribe to Events template, as shown in Figure 4-13.

    Figure 4-13 Create Mediator Dialog, Subscribe to Events

    Create Mediator dialog, Subscribe to Events
  5. Click the Add icon to add an event.

  6. In the Event Chooser dialog, click the Browse icon to navigate to and select the event's definition file, and then click OK.

  7. In the Create Mediator dialog, you can optionally change the Consistency option and specify a Filter for the event.

  8. Click OK to generate the mediator.

    The resulting mediator (.mplan file) is displayed in the overview editor.

  9. You can now click the Add icon in the Routing Rules section to add a rule for how to respond to the event.

4.12 Working Programmatically with Entity Objects and Associations

You may not always need or want UI-based or programmatic clients to work directly with entity objects. Sometimes, you may just want to use an external client program to access an application module and work directly with the view objects in its data model. Chapter 5, "Defining SQL Queries Using View Objects" describes how to easily combine the flexible SQL-querying of view objects with the business logic enforcement and automatic database interaction of entity objects to build powerful applications. The combination enables a fully updatable application module data model, designed to meet the needs of the current end-user tasks at hand, that shares the centralized business logic in your reusable domain business object layer.

However, it is important first to understand how view objects and entity objects can be used on their own before learning to harness their combined power. By learning about these objects in greater detail, you will have a better understanding of when you should use them alone and when to combine them in your own applications.

Since clients don't work directly with entity objects, any code you write that works programmatically with entity objects will typically be custom code in a custom application module class or in the custom class of another entity object.

4.12.1 How to Find an Entity Object by Primary Key

To access an entity row, you use a related object called the entity definition. At runtime, each entity object has a corresponding entity definition object that describes the structure of the entity and manages the instances of the entity object it describes. After creating an application module and enabling a custom Java class for it, imagine you wanted to write a method to return a specific order. It might look like the retrieveOrderById() method shown in Example 4-13.

To find an entity object by primary key:

  1. Find the entity definition.

    You obtain the entity definition object for the OrderEO entity by passing its fully qualified name to the static getDefinitionObject() method imported from the EntityDefImpl class. The EntityDefImpl class in the oracle.jbo.server package implements the entity definition for each entity object.

  2. Construct a key.

    You build a Key object containing the primary key attribute that you want to look up. In this case, you're creating a key containing the single orderId value passed into the method as an argument.

  3. Find the entity object using the key.

    You use the entity definition's findByPrimaryKey() method to find the entity object by key, passing in the current transaction object, which you can obtain from the application module using its getDBTransaction() method. The concrete class that represents an entity object row is the oracle.jbo.server.EntityImpl class.

  4. Return the object or some of its data to the caller.

Example 4-13 show example code for a retrieveOrderById() method developed using this basic procedure.

Example 4-13 Retrieving an OrderEO Entity Object by Key

/* Helper method to return an Order by Id  */
private OrderEOImpl retrieveOrderById(long orderId) {
  EntityDefImpl orderDef = OrderEOImpl.getDefinitionObject();
  Key orderKey = OrderEOImpl.createPrimaryKey(new DBSequence(orderId));
  return (OrderEOImpl)orderDef.findByPrimaryKey(getDBTransaction(),orderKey);
}

Note:

The oracle.jbo.Key object constructor can also take an Object array to support creating multiattribute keys, in addition to the more typical single-attribute value keys.

4.12.2 How to Access an Associated Entity Using the Accessor Attribute

You can create a method to access an associated entity based on an accessor attribute that requires no SQL code. For example, the method findOrderCustomer() might find an order, then access the associated PersonEO entity object representing the customer assigned to the order. For an explanation of how associations enable easy access from one entity object to another, see Section 4.3, "Creating and Configuring Associations."

To avoid a conflict with an existing method in the application module that finds the same associated entity using the same accessor attribute, you can refactor this functionality into a helper method that you can then reuse anywhere in the application module it is required. For example, the retrieveOrderById() method (shown in Example 4-13) refactors the functionality that finds an order.

To access an associated entity object using the accessor attribute:

  1. Find the associated entity by the accessor attribute.

    The findOrderCustomer() method uses the retrieveOrderById() helper method to retrieve the OrderEO entity object by ID.

  2. Access the associated entity using the accessor attribute.

    Using the attribute getter method, you can pass in the name of an association accessor and get back the entity object on the other side of the relationship. (Note that Section 4.3.3, "How to Change Entity Association Accessor Names," explains that renaming the association accessor allows it to have a more intuitive name.)

  3. Return some of its data to the caller.

    The findOrderCustomer() method uses the getter methods on the returned PersonEO entity to return the assigned customer's name by concatenating their first and last names.

Notice that you did not need to write any SQL to access the related PersonEO entity. The relationship information captured in the ADF association between the OrderEO and PersonEO entity objects is enough to allow the common task of data navigation to be automated.

Example 4-14 shows the code for findOrderCustomer() that uses the helper method.

Example 4-14 Accessing an Associated Entity Using the Accessor Attribute

/*  Access an associated Customer entity from the Order entity   */  
public String findOrderCustomer(long orderId) {
      //1. Find the OrderEO object
  OrderEOImpl order = retrieveOrderById(orderId);
  if (order != null) {
      //2. Access the PersonEO object using the association accessor attribute
    PersonEOImpl cust  = (PersonEOImpl)order.getPerson();
    if (cust != null) {
      //3. Return attribute values from the associated entity object 
      return cust.getFirstName() + " " + cust.getLastName();
    }
    else {
      return "Unassigned";
    }
  }
  else {
    return null;
  }
}

4.12.3 How to Update or Remove an Existing Entity Row

Once you've got an entity row in hand, it's simple to update it or remove it. You could add a method like the updateOrderStatus() shown in Example 4-15 to handle the job.

To update an entity row:

  1. Find the Order by ID.

    Using the retrieveOrderById() helper method, the updateOrderStatus() method retrieves the OrderEO entity object by Id.

  2. Set one or more attributes to new values.

    Using the EntityImpl class' setAttribute() method, the updateOrderStatus() method updates the value of the Status attribute to the new value passed in.

  3. Commit the transaction.

    Using the application module's getDBTransaction() method, the updateOrderStatus() method accesses the current transaction object and calls its commit() method to commit the transaction.

Example 4-15 Updating an Existing Entity Row

/* Update the status of an existing order */  
public void updateOrderStatus(long orderId, String newStatus) {
      //1. Find the order
  OrderEOImpl order = retrieveOrderById(orderId);
  if (order != null) {
      //2. Set its Status attribute to a new value
    order.setOrderStatusCode(newStatus);
      //3. Commit the transaction
    try {
      getDBTransaction().commit();
    }
    catch (JboException ex) {
      getDBTransaction().rollback();
      throw ex;
    }
  }
}

The example for removing an entity row would be the same, except that after finding the existing entity, you would use the following line instead to remove the entity before committing the transaction:

// Remove the entity instead!
order.remove();

4.12.4 How to Create a New Entity Row

In addition to using the entity definition to find existing entity rows, you can also use it to create new ones. In the case of product entities, you could write a createProduct() method like the one shown in Example 4-16 to accept the name and description of a new product, and return the new product ID assigned to it. This example assumes that the ProductId attribute of the ProductBaseEO entity object has been updated to have the DBSequence type (see Section 4.10.10, "How to Get Trigger-Assigned Primary Key Values from a Database Sequence"). This setting ensures that the attribute value is refreshed to reflect the value of the trigger from the corresponding database table, assigned to it from the table's sequence in the application schema.

To create an entity row:

  1. Find the entity definition.

    Using the getDefinitionObject() method, the createProduct() method finds the entity definition for the Product entity.

  2. Create a new instance.

    Using the createInstance2() method on the entity definition, the createProduct() method creates a new instance of the entity object.

    Note:

    The method name has a 2 at the end. The regular createInstance() method has protected access and is designed to be customized as described Section E.2.4, "EntityImpl Class" of Appendix E, "Most Commonly Used ADF Business Components Methods." The second argument of type AttributeList is used to supply attribute values that must be supplied at create time; it is not used to initialize the values of all attributes found in the list. For example, when creating a new instance of a composed child entity row using this API, you must supply the value of a composing parent entity's foreign key attribute in the AttributeList object passed as the second argument. Failure to do so results in an InvalidOwnerException.
  3. Set attribute values.

    Using the attribute setter methods on the entity object, the createProduct() method assigns values for the Name, Status, and other attributes in the new entity row.

  4. Commit the transaction.

    Calling commit() on the current transaction object, the createProduct() method commits the transaction.

  5. Return the trigger-assigned product ID to the caller.

    Using the attribute getter method to retrieve the value of the ProductId attribute as a DBSequence, and then calling getSequenceNumber().longValue(), the createProduct() method returns the sequence number as a long value to the caller.

Example 4-16 Creating a New Entity Row

/* Create a new Product and Return its new id */
public long createProduct(String name, String status, String shipCode) {
      //1. Find the entity definition for the Product entity
  EntityDefImpl productDef = ProductBaseEOImpl.getDefinitionObject();
      //2. Create a new instance of a Product entity
  ProductBaseEOImpl newProduct = (ProductBaseEOImpl)productDef.createInstance2(getDBTransaction(),null);
      //3. Set attribute values
  newProduct.setProductName(name);
  newProduct.setProductStatus(status);
  newProduct.setShippingClassCode(shipCode);
  newProduct.setSupplierId(new Number(100));
  newProduct.setListPrice(new Number(499));
  newProduct.setMinPrice(new Number(479));
  newProduct.setCreatedBy("Test Client");
  newProduct.setLastUpdatedBy("Test Client");
  newProduct.setCategoryId(new Number(5));
      //4. Commit the transaction
  try {
    getDBTransaction().commit();
  }
  catch (JboException ex) {
    getDBTransaction().rollback();
    throw ex;
  }
      //5. Access the database-trigger-assigned ProductId value and return it
  DBSequence newIdAssigned = newProduct.getProductId();
  return newIdAssigned.getSequenceNumber().longValue();
}

4.12.5 Assigning the Primary Key Value Using an Oracle Sequence

As an alternative to using a trigger-assigned value (as described in Section 4.10.10, "How to Get Trigger-Assigned Primary Key Values from a Database Sequence"), you can assign the value to a primary key when creating a new row using an Oracle sequence. This metadata-driven approach allows you to centralize the code to retrieve the primary key into a single Java file that can be reused by multiple entity objects.

Example 4-17 shows a simple CustomEntityImpl framework extension class on which the entity objects are based. Its overridden create() method tests for the presence of a custom attribute-level metadata property named SequenceName and if detected, populates the attribute's default value from the next number in that sequence.

Example 4-17 CustomEntityImpl Framework Extension Class

package sample;
 
import oracle.jbo.AttributeDef;
import oracle.jbo.AttributeList;
import oracle.jbo.server.EntityImpl;
import oracle.jbo.server.SequenceImpl;
 
public class CustomEntityImpl extends EntityImpl {
    protected void create(AttributeList attributeList) {
        super.create(attributeList);
        for (AttributeDef def : getEntityDef().getAttributeDefs()) {
            String sequenceName = (String)def.getProperty("SequenceName");
            if (sequenceName != null) {
               SequenceImpl s = new SequenceImpl(sequenceName,getDBTransaction());
               setAttribute(def.getIndex(),s.getSequenceNumber());
            }
        }
    }
}

To assign the primary key value using an Oracle sequence:

  1. Create the CustomEntityImpl.java file in your project, and insert the code shown in Example 4-17.

  2. In the Application Navigator, double-click the entity you want to edit.

  3. In the overview editor, click the Attributes navigation tab, and double-click the attribute you want to edit.

  4. In the Edit Attribute dialog, set the attribute Type to Number, and then click the Custom Properties node.

  5. Enter SequenceName for the name.

  6. Enter the name of the database sequence for the value, click Add, and then click OK to create the custom property.

    For example, a Dept entity could define the custom property SequenceName on its Deptno attribute with the value DEPT_TABLE_SEQ.

4.13 Generating Custom Java Classes for an Entity Object

As described in this chapter, all of the database interaction and a large amount of declarative runtime functionality of an entity object can be achieved without using custom Java code. When you need to go beyond the declarative features to implement custom business logic for your entities, you'll need to enable custom Java generation for the entities that require custom code. Appendix E, "Most Commonly Used ADF Business Components Methods," provides a quick reference to the most common code that you will typically write, use, and override in your custom entity object and entity definition classes.

4.13.1 How to Generate Custom Classes

To enable the generation of custom Java classes for an entity object, use the Java page of the overview editor.

To generate a custom Java class for an entity object:

  1. In the Application Navigator, double-click the entity.

  2. In the overview editor, click the Java navigation tab, and then click the Edit icon.

  3. In the Select Java Options dialog, select the types of Java classes you want to generate.

    • Entity Object Class — the most frequently customized, it represents each row in the underlying database table.

    • Entity Collection Class — rarely customized.

    • Entity Definition Class — less frequently customized, it represents the related class that manages entity rows and defines their structure.

  4. Click OK.

4.13.2 What Happens When You Generate Custom Classes

When you select one or more custom Java classes to generate, JDeveloper creates the Java file(s) you've indicated. For example, assuming an entity object named fodemo.storefront.entities.OrderEO, the default names for its custom Java files will be OrderEOImpl.java for the entity object class and OrderEODefImpl.java for the entity definition class. Both files are created in the same ./fodemo/storefront/entities directory as the component's XML component definition file.

The Java generation options for the entity object continue to be reflected on subsequent visits to the Java page of the overview editor. Just as with the XML definition file, JDeveloper keeps the generated code in your custom Java classes up to date with any changes you make in the editor. If later you decide you didn't require a custom Java file for any reason, disabling the relevant options on the Java page causes the custom Java files to be removed.

4.13.3 What Happens When You Generate Entity Attribute Accessors

When you enable the generation of a custom entity object class, if you also enable the Accessors option, then JDeveloper generates getter and setter methods for each attribute in the entity object. For example, an OrderEO entity object that has the corresponding custom OrderEOImpl.java class might have methods (like those shown in Example 4-18) generated in it.

Example 4-18 Getter and Setter Methods from OrderEOImpl.java

public DBSequence getOrderId() { ... }
public void setOrderId(DBSequence value) { ... }
 
public Date getOrderDate() { ... }
public void setOrderDate(Date value) { ... }
 
public String getOrderStatusCode() { ... }
public void setOrderStatusCode(String value) { ... }
 
public Number getCustomerId() { ... }
public void setCustomerId(Number value) { ... }
 
public String getShipToName() { ... }
public void setShipToName(String value) { ... }

These methods allow you to work with the row data with compile-time checking of the correct data type usage. That is, instead of writing a line like this to get the value of the CustomerId attribute:

Number customerId = (Number)order.getAttribute("CustomerId");

you can write the code like:

Number customerId = order.getCustomerId();

You can see that with the latter approach, the Java compiler would catch a typographical error had you accidentally typed CustomerCode instead of CustomerId:

// spelling name wrong gives compile error
Number customerId = order.getCustomerCode();

Without the generated entity object accessor methods, an incorrect line of code like the following cannot be caught by the compiler:

// Both attribute name and type cast are wrong, but compiler cannot catch it
String customerId = (String)order.getAttribute("CustomerCode");

It contains both an incorrectly spelled attribute name, as well as an incorrectly typed cast of the getAttribute() return value. When you use the generic APIs on the Row interface, which the base EntityImpl class implements, errors of this kind raise exceptions at runtime instead of being caught at compile time.

4.13.4 How to Navigate to Custom Java Files

As shown in Figure 4-14, when you've enabled generation of custom Java classes, they also appear as child nodes under the Application Sources node for the entity object. As with all ADF components, when you select an entity object in the Application Navigator, the Structure window provides a structural view of the entity. When you need to see or work with the source code for a custom Java file, there are two ways to open the file in the source editor:

  • You can right-click the Java file, and choose Open from the context menu, as shown in Figure 4-14.

  • You can right-click an item in a node in the Structure window, and choose Go To Source from the context menu.

Figure 4-14 Seeing and Navigating to Custom Java Classes for an Entity Object

Image shows context menu in Application Navigator

4.13.5 What You May Need to Know About Custom Java Classes

See the following sections for additional information about custom Java classes.

4.13.5.1 About the Framework Base Classes for an Entity Object

When you use an XML-only entity object, at runtime its functionality is provided by the default ADF Business Components implementation classes. Each custom Java class that is generated will automatically extend the appropriate ADF Business Components base class so that your code inherits the default behavior and you can easily add to or customize it. An entity object class will extend EntityImpl, while the entity definition class will extend EntityDefImpl (both in the oracle.jbo.server package).

4.13.5.2 You Can Safely Add Code to the Custom Component File

Some developers are hesitant to add their own code to generated Java source files. Each custom Java source code file that JDeveloper creates and maintains for you includes the following comment at the top of the file to clarify that it is safe for you to add your own custom code to this file.

// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------

JDeveloper does not blindly regenerate the file when you click OK or Apply in an edit dialog. Instead, it performs a smart update to the methods that it needs to maintain, leaving your own custom code intact.

4.13.5.3 Configuring Default Java Generation Preferences

You can generate custom Java classes for your view objects when you need to customize their runtime behavior or when you simply prefer to have strongly typed access to bind variables or view row attributes.

To configure the default settings for ADF Business Components custom Java generation, you can choose Preferences from the Tools menu and open the Business Components page to set your preferences to be used for business components created in the future. Developers getting started with ADF Business Components should set their preference to generate no custom Java classes by default. As you run into a specific need for custom Java code, you can enable just the bit of custom Java you need for that one component. Over time, you'll discover which set of defaults works best for you.

4.13.5.4 Attribute Indexes and InvokeAccessor Generated Code

The entity object is designed to function based on XML only or as an XML component definition combined with a custom Java class. To support this design choice, attribute values are not stored in private member fields of an entity's class (a file that is not present in the XML-only situation). Instead, in addition to a name, attributes are also assigned a numerical index in the entity's XML component definition based on the zero-based, sequential order of the <Attribute> and association-related <AccessorAttribute> tags in that file. At runtime, attribute values in an entity row are stored in a sparse array structure managed by the base EntityImpl class, indexed by the attribute's numerical position in the entity's attribute list.

For the most part, this private implementation detail is unimportant, since as a developer using entity objects, you are shielded from having to understand this. However, when you enable a custom Java class for your entity object, this implementation detail relates to some of the generated code that JDeveloper maintains in your entity object class. It is sensible to understand what that code is used for. For example, in the custom Java class for a OrderEO entity object, each attribute or accessor attribute has a corresponding generated integer enum. JDeveloper ensures that the values of these enums correctly reflect the ordering of the attributes in the XML component definition.

You'll also notice that the automatically maintained, strongly typed getter and setter methods in the entity object class use these attribute enums, as shown in Example 4-19.

Example 4-19 Getter and Setter Methods Using Attribute Constants in the Custom Entity Java Class

// In oracle.fodemo.storefront.entities.OrderEOImpl class
public Date getOrderDate() {
    return (Date)getAttributeInternal(ORDERDATE); // <-- Attribute enum
}
public void setOrderDate(Date value) {
    setAttributeInternal(ORDERDATE, value); // <-- Attribute enum
}

Another aspect of the automatically maintained code related to entity attribute enums are the getAttrInvokeAccessor() and setAttrInvokeAccessor() methods. These methods optimize the performance of attribute access by numerical index, which is how generic code in the EntityImpl base class typically accesses attribute values when performing generic processing. An example of the getAttrInvokeAccessor() method is shown in Example 4-20. The companion setAttrInvokeAccessor() method looks similar.

Example 4-20 getAttrInvokeAccessor() Method in the Custom Entity Java Class

// In oracle.fodemo.storefront.entities.OrderEOImpl class
/** getAttrInvokeAccessor: generated method. Do not modify. */
protected Object getAttrInvokeAccessor(int index, AttributeDefImpl attrDef) 
        throws Exception {
 if ((index >= AttributesEnum.firstIndex()) && (index < AttributesEnum.count())) {
     return AttributesEnum.staticValues()[index - AttributesEnum.firstIndex()].get(this);
 }
  return super.getAttrInvokeAccessor(index, attrDef);
}

The rules of thumb to remember about this generated attribute-index related code are the following.

The Do's
  • Add custom code if needed inside the strongly typed attribute getter and setter methods.

  • Use the overview editor to change the order or type of entity object attributes.

    JDeveloper changes the Java signature of getter and setter methods, as well as the related XML component definition for you.

The Don'ts
  • Don't modify the getAttrInvokeAccessor() and setAttrInvokeAccessor() methods.

  • Don't change the values of the attribute index numbers manually.

Note:

If you need to manually edit the generated attribute enums because of source control merge conflicts or other reasons, you must ensure that the zero-based ordering reflects the sequential ordering of the <Attribute> and <AccessorAttribute> tags in the corresponding entity object XML component definition.

4.13.6 Programmatic Example for Comparison Using Custom Entity Classes

To better evaluate the difference of using custom generated entity classes versus working with the generic EntityImpl class, Example 4-21 shows a version of methods in a custom entity class (StoreFrontServiceImpl.java) from a custom application module class (StoreFrontService2Impl.java). Some important differences to notice are:

  • Attribute access is performed using strongly typed attribute accessors.

  • Association accessor attributes return the strongly typed entity class on the other side of the association.

  • Using the getDefinitionObject() method in your custom entity class allows you to avoid working with fully qualified entity definition names as strings.

  • The createPrimaryKey() method in your custom entity class simplifies creating the Key object for an entity.

Example 4-21 Programmatic Entity Examples Using Strongly Typed Custom Entity Object Classes

package devguide.examples.appmodules;

import oracle.fodemo.storefront.entities.OrderEOImpl;

import oracle.fodemo.storefront.entities.PersonEOImpl;
import oracle.fodemo.storefront.entities.ProductBaseEOImpl;

import oracle.jbo.ApplicationModule;
import oracle.jbo.JboException;
import oracle.jbo.Key;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.DBSequence;
import oracle.jbo.domain.Number;
import oracle.jbo.server.ApplicationModuleImpl;
import oracle.jbo.server.EntityDefImpl;

// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
/**
 * This custom application module class illustrates the same
 * example methods as StoreFrontServiceImpl.java, except that here
 * we're using the strongly typed custom Entity Java classes
 * OrderEOImpl, PersonsEOImpl, and ProductsBaseEOImpl instead of working
 * with all the entity objects using the base EntityImpl class.
 */

public class StoreFrontService2Impl extends ApplicationModuleImpl {
    /**This is the default constructor (do not remove).
     */
     public StoreFrontService2Impl() {
     }
     /*
      * Helper method to return an Order by Id
      */
     private OrderEOImpl retrieveOrderById(long orderId) {
       EntityDefImpl orderDef = OrderEOImpl.getDefinitionObject();
       Key orderKey = OrderEOImpl.createPrimaryKey(new DBSequence(orderId));
       return (OrderEOImpl)orderDef.findByPrimaryKey(getDBTransaction(),orderKey);
     }
 
     /*
      * Find an Order by Id
      */
     public String findOrderTotal(long orderId) {
       OrderEOImpl order = retrieveOrderById(orderId);
       if (order != null) {
         return order.getOrderTotal().toString();
       }
       return null;
     }
     
     /*
      * Create a new Product and Return its new id
      */
     public long createProduct(String name, String status, String shipCode) {
       EntityDefImpl productDef = ProductBaseEOImpl.getDefinitionObject();
       ProductBaseEOImpl newProduct = (ProductBaseEOImpl)productDef.createInstance2(getDBTransaction(),null);
         newProduct.setProductName(name);
         newProduct.setProductStatus(status);
         newProduct.setShippingClassCode(shipCode);
         newProduct.setSupplierId(new Number(100));
         newProduct.setListPrice(new Number(499));
         newProduct.setMinPrice(new Number(479));
         newProduct.setCreatedBy("Test Client");
         newProduct.setLastUpdatedBy("Test Client");
         newProduct.setCategoryId(new Number(5));
       try {
         getDBTransaction().commit();
       }
       catch (JboException ex) {
         getDBTransaction().rollback();
         throw ex;
       }
       DBSequence newIdAssigned = newProduct.getProductId();
       return newIdAssigned.getSequenceNumber().longValue();
     }
     /*
      * Update the status of an existing order
      */  
     public void updateRequestStatus(long orderId, String newStatus) {
       OrderEOImpl order = retrieveOrderById(orderId);
       if (order != null) {
         order.setOrderStatusCode(newStatus);
         try {
           getDBTransaction().commit();
         }
         catch (JboException ex) {
           getDBTransaction().rollback();
           throw ex;
         }
       }
     }
     
     /*
      * Access an associated Customer entity from the Order entity
      */  
     public String findOrderCustomer(long orderId) {
       OrderEOImpl svcReq = retrieveOrderById(orderId);
       if (svcReq != null) {
         PersonEOImpl cust  = (PersonEOImpl)svcReq.getPerson();
         if (cust != null) {
           return cust.getFirstName() + " " + cust.getLastName();
         }
         else {
           return "Unassigned";
         }
       }
       else {
         return null;
       }
     }  
    
      /*
       * Testing method
       */
       public static void main(String[] args) {
         String        amDef = "devguide.model.StoreFrontService";
         String        config = "StoreFrontServiceLocal";
         ApplicationModule am = Configuration.createRootApplicationModule(amDef,config);
         /* 
          * NOTE: This cast to use the StoreFrontServiceImpl class is OK since 
          * this code is inside a business tier *Impl.java file and not in a
          * client class that is accessing the business tier from "outside".
          */
         StoreFrontServiceImpl service = (StoreFrontServiceImpl)am;
         String total = service.findOrderTotal(1011);
         System.out.println("Status of Order # 1011 = " + total);
         String customerName = service.findOrderCustomer(1011);
         System.out.println("Customer for Order # 1011 = " + customerName);
         try {
             service.updateOrderStatus(1011,"CANCEL");
         }
         catch (JboException ex) {
           System.out.println("ERROR: "+ex.getMessage());
         }
         long id = 0;
         try {
             id = service.createProduct(null, "NEW", "CLASS1");
         }
         catch (JboException ex) {
           System.out.println("ERROR: "+ex.getMessage());
         }
         id = service.createProduct("Canon PowerShot G9", "NEW", "CLASS1");
         System.out.println("New product created successfully with id = "+id);
         Configuration.releaseRootApplicationModule(am,true);
       }
     }

4.14 Adding Transient and Calculated Attributes to an Entity Object

In addition to having attributes that map to columns in an underlying table, your entity objects can include transient attributes that display values calculated (for example, using Java or Groovy) or that are value holders. For example, a transient attribute you create, such as FullName, could be calculated based on the concatenated values of FirstName and LastName attributes.

Once you create the transient attribute, you can perform a calculation in the entity object Java class, or use a Groovy expression in the attribute definition to specify a default value.

If you want to be able to change the value at runtime, you can use a Groovy expression. If the calculated value is not likely to change (for example, if it's a sum of the line items), you can perform the calculation directly in the entity object Java class.

4.14.1 How to Add a Transient Attribute

Use the Attributes page of the overview editor to create a transient attribute.

To add a transient attribute to an entity object:

  1. In the Application Navigator, double-click the entity.

  2. In the overview editor, click the Attributes navigation tab, and then click the New icon.

  3. Enter a name for the attribute.

  4. Set the Java attribute type.

  5. Disable the Persistent option.

  6. If the value will be calculated, set Updatable to Never.

  7. Click OK.

4.14.2 What Happens When You Add a Transient Attribute

When you add a transient attribute, JDeveloper updates the XML component definition for the entity object to reflect the new attribute.

The <Attribute> tag of a transient attribute has no TableName and a ColumnName of $none$, as shown in Example 4-22.

Example 4-22 XML Code for a Transient Attribute

<Attribute
   Name="FullName"
   IsUpdateable="false"
   IsQueriable="false"
   IsPersistent="false"
   ColumnName="$none$"
   Type="java.lang.String"
   ColumnType="$none$"
   SQLType="VARCHAR" >
</Attribute>

In contrast, a persistent entity attribute has both a TableName and a ColumnName, as shown in Example 4-23.

Example 4-23 XML Code for a Persistent

<Attribute
   Name="FirstName"
   IsNotNull="true"
   Precision="30"
   ColumnName="FIRST_NAME"
   Type="java.lang.String"
   ColumnType="VARCHAR2"
   SQLType="VARCHAR"
   TableName="USERS" >
</Attribute>

4.14.3 How to Base a Transient Attribute On a Groovy Expression

When creating a transient attribute, you can use a Groovy expression to provide the default value.

To create a transient attribute based on a Groovy expression:

  1. Create a new attribute, as described in the first four steps of Section 4.14.1, "How to Add a Transient Attribute."

  2. In the Application Navigator, double-click the entity.

  3. In the overview editor, click the Attributes navigation tab, and then click the New icon.

  4. In the New Entity Attribute dialog box, enter a name for the attribute.

  5. Set the Java attribute type.

  6. Click the Edit button next to the Value field.

    Expressions that you define are evaluated using the Groovy scripting language, as described in Section 3.6, "Overview of Groovy Support." Groovy lets you insert expressions and variables into strings. The expression is saved as part of the entity object definition.

  7. In the Edit Expression dialog, enter an expression in the field provided, as shown in Figure 4-15.

    Attributes that you reference can include any attribute that the entity object defines. Do not reference attributes in the expression that are not defined by the entity object.

    Figure 4-15 Edit Expression Dialog

    Entering an expression for a transient attribute
  8. Select the appropriate recalculate setting.

    If you select Always (default), the expression is evaluated each time any attribute in the row changes. If you select Never, the expression is evaluated only when the row is created.

  9. You can optionally provide a condition for when to recalculate the expression.

    For example, the following expression in the Based on the following expression field causes the attribute to be recalculated when either the Quantity attribute or the UnitPrice attribute are changed:

    return (adf.object.isAttributeChanged("Quantity") || adf.object.isAttributeChanged("UnitPrice"));
    
  10. You can also list attributes on which this attribute is dependent.

    In Figure 4-15, the Quantity and UnitPrice attributes are selected, which causes the attribute to be recalculated when either attribute is changed.

  11. Click OK to save the expression.

  12. Then click OK to create the attribute.

Note:

If either the value expression or the optional recalculate expression that you define references an attribute from the base entity object, you must define this as a dependency on the Dependencies page of the Edit Attribute dialog. In the Dependency page, locate the attributes in the Available list and shuttle each to the Selected list.

4.14.4 What Happens When You Base a Transient Attribute on Groovy Expression

When you base a transient attribute on a Groovy expression, a <TransientExpression> tag is added to the entity object's XML file within the appropriate attribute, as shown in Example 4-24.

Example 4-24 Calculating a Transient Attribute Using a Groovy Expression

<TransientExpression>
   <![CDATA[
      ((Quantity == null) ? 0 : Quantity) * ((UnitPrice == null) ? 0 : UnitPrice)
   ]]>
</TransientExpression>

4.14.5 How to Add Java Code in the Entity Class to Perform Calculation

A transient attribute is a placeholder for a data value. If you change the Updatable property of the transient attribute to While New or Always, then the end user can enter a value for the attribute. If you want the transient attribute to display a calculated value, then you'll typically leave the Updatable property set to Never and write custom Java code that calculates the value.

After adding a transient attribute to the entity object, to make it a calculated attribute you need to:

  • Enable a custom entity object class on the Java page of the overview editor, choosing to generate accessor methods

  • Write Java code inside the accessor method for the transient attribute to return the calculated value

  • Specify each dependent attribute for the transient attribute on the Dependencies page of the Edit Attribute dialog

For example, after generating the view row class, the Java code to return the transient attribute's calculated value would reside in the getter method for the attribute (such as FullName), as shown in Example 4-25.

Example 4-25 Getter Method for a Transient Attribute

// Getter method for FullName calculated attribute in UserImpl.java 
public String getFullName() {
  // Commented out original line since we'll always calculate the value
  // return (String)getAttributeInternal(FULLNAME);
      return getFirstName()+" "+getLastName();
}

To ensure that the transient attribute is reevaluated whenever the attributes to be concatenated (such as LastName and FirstName) might be changed by the end user, specify the dependent attributes for the transient attribute. On the Dependencies page of the Edit Attribute dialog, locate the attributes in the Available list and shuttle each to the Selected list.