4 Creating a Business Domain Layer Using Entity Objects

This chapter describes how to use ADF entity objects to create a reusable business layer of Java objects that describe the business domain in an Oracle ADF application.

This chapter includes the following sections:

4.1 About Entity Objects

You use ADF entity objects to represent rows of a data source. ADF entity objects allow you to modify row attributes, encapsulate domain business logic and implement business policies and rules.

An entity object is the ADF business component that represents a row in the specified data source (generally a single database table, view, or synonym) 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.

4.1.1 Entity Object Use Cases and Examples

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 (as illustrated in Figure 4-1) 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 the 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.

Figure 4-1 Entity Object Encapsulates Business Logic for a Table

Description of Figure 4-1 follows
Description of "Figure 4-1 Entity Object Encapsulates Business Logic for a Table"

4.1.2 Additional Functionality for Entity Objects

You may find it helpful to understand other Oracle ADF features before you start working with entity objects. Following are links to other functionality that may be of interest.

4.2 Creating Entity Objects and Associations

Create ADF entity objects and associations from existing tables. You can also create ADF entity objects from scratch and generate tables.

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.

Before you begin:

It may be helpful to have an understanding of the options you have for creating entity objects. For more information, see Creating Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

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

  1. In the Applications window, right-click the project in which you want to create the entity objects and choose New > From Gallery.
  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 click the Create a new database connection icon to create a connection. Click OK.
  4. On the Entity Objects page, do the following to create the entity objects:
    • Enter the name of the package 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:

    Because 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-2 shows what the wizard page looks like after selecting the S_COUNTRIES table in the Summit ADF schema, setting a package name of summit.model.entities, and renaming the entity object in the singular.

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

    Description of Figure 4-2 follows
    Description of "Figure 4-2 Create Business Components from Tables Wizard, Entity Objects Page "
  5. When you are satisfied with the selected table objects and their corresponding entity object names, click Finish.

The Applications window 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-3, the associations have been moved to a subpackage (assoc) and do not appear in the entities package in the Applications window. For more information, see How to Rename and Move Associations to a Different Package.

Figure 4-3 New Entity Objects in Applications window

Description of Figure 4-3 follows
Description of "Figure 4-3 New Entity Objects in Applications window"

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.

Note:

Associations are not generated when you use the Create Entity Object wizard. However, the Business Components from Tables wizard does generate associations. If you use the Create Entity Object wizard to create entity objects, you will need to create the corresponding associations manually.

Before you begin:

It may be helpful to have an understanding of the options you have for creating entity objects. For more information, see Creating Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a single entity object:

  1. In the Applications window, right-click the project in which you want to create the entity object and choose New > From Gallery.
  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 a name for 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 Object 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 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 document 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 Foreign Key Associations Generated When Entity Objects Are Derived from Tables

In addition to the entity objects, the Business Components from Tables wizard also generates named association components that capture information about the relationships between entity objects. For example, the database diagram in Figure 4-4 shows that JDeveloper derives default association names like SItemOrdIdFkAssoc 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 document file and saves it in the directory that corresponds to the name of its package.

Note:

Associations are generated when you use the Business Components from Tables wizard. However, the Create Entity Object wizard does not generate associations. If you use the Create Entity Object wizard to create entity objects, you will need to create the corresponding associations manually.

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

Figure 4-4 S_ITEM and S_ORD Tables Related by Foreign Key

Description of Figure 4-4 follows
Description of "Figure 4-4 S_ITEM and S_ORD Tables Related by Foreign Key"

4.2.3.2 Entity Object Key Generated 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. Because every entity object must have at least one attribute marked as a primary key, the wizard marks all columns as part of 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 setting from other attributes. 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 all columns 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 RowID Generated 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 Applications window 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 Applications window 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 performs its operations directly against the database and will drop existing tables. It does not generate a script to run later. 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.

Before you begin:

It may be helpful to have an understanding of the options you have for creating entity objects. For more information, see Creating Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To synchronize an entity with changes to its database table:

  1. In the Applications window, right-click the entity object you want to synchronize 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. Click OK.

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.

Before you begin:

It may be helpful to have an understanding of the options you have for creating entity objects. For more information, see Creating Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To remove an entity attribute:

  1. In the Applications window, double-click the entity object that contains the attribute you want to remove.
  2. In the overview editor, click the Attributes navigation tab.
  3. On the Attributes page, right-click the attribute and choose Delete.

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

  4. If usages were found, click Show Usages.

    The dialog shows all usages of the attribute, which you can click to display the usage in the source editor.

  5. Click Preview to display usages of the attribute in the Log window, and then work through the list 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.

Before you begin:

It may be helpful to have an understanding of the options you have for creating entity objects. For more information, see Creating Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To locate all usages of an entity attribute:

  1. In the Applications window, double-click the entity object.
  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.

Note:

Do not use effective dating on a parent entity object in a master-detail relationship. Because child objects can raise events on parent objects, and effective dated entity objects cause row splits during update and delete operations, it is possible for the child object to raise an event on the wrong parent object if both the parent and child are updated during the same transaction.

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.

Before you begin:

It may be helpful to have an understanding of the options you have for creating entity objects. For more information, see Creating Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create an effective dated entity object

  1. In the Applications window, double-click the entity object on which you want enable effective dating.
  2. In the Properties window, expand the Type category.

    If necessary, choose Properties from the Window menu to display the Properties window.

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

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

    To display the property 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 Properties window for the appropriate attributes. To do so, select the appropriate attribute in the overview editor and set the Start Date or End Date property to true in the Properties window.

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.

The following example shows some sample XML entries that are generated when you create an effective dated entity. For more information about working with effective dated objects, see Limiting View Object Rows Using Effective Date Ranges.

// 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.

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

Create entity associations manually if foreign key constraints don’t exist so that JDeveloper is able to infer associations between ADF entity objects.

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.

Before you begin:

It may be helpful to have an understanding of why you create associations. For more information, see Creating and Configuring Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create an association:

  1. In the Applications window, right-click the project in which you want to create the association and choose New > From Gallery.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Association, and click OK.
  3. In the Create Association wizard, on the Name page, do the following:
    • 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-5 shows the selected Id attribute from the OrdEO entity object as the source entity attribute. Because the ItemEO rows contain an order ID that relates them to a specific OrdEO row, you would select this OrdId foreign key attribute in the ItemEO entity object as the destination attribute.

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

    Description of Figure 4-5 follows
    Description of "Figure 4-5 Create Association Wizard, Attribute Pairs That Relate Two Entity Objects Defined"
  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 an OrdEO row and its related ItemEO 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-6 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 OrdEO 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-6 Association Properties Control Runtime Behavior

    Description of Figure 4-6 follows
    Description of "Figure 4-6 Association Properties Control Runtime Behavior"
  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 document file and saves it in the directory that corresponds to the name of its package. For example, if you created an association named SItemOrderIdFkAssoc in the oracle.summit.model.entities.assoc subpackage, then the association XML file would be created in the ./oracle/summit/model/entities/assoc directory with the name SItemOrderIdFkAssoc.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.

Before you begin:

It may be helpful to have an understanding of why you create associations. For more information, see Creating and Configuring Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To rename the entity accessor in an association:

  1. In the Applications window, double-click the association that contains the entity accessor you want to rename.
  2. In the overview editor, click the Relationships navigation tab.
  3. On the Relationships page, expand the Accessors section and click the Edit icon.

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

  4. In the Association Properties dialog, modify the name as necessary, and click OK.

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. However, it is not advisable to rename or move ADF Business Components objects manually, as missed references can break your application. For more information about JDeveloper's refactoring functionality, see Refactoring a Fusion Web Application .

Before you begin:

It may be helpful to have an understanding of why you create associations. For more information, see Creating and Configuring Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

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

  1. In the Applications window, 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.

If you just want to rename a component, right-click the component you want to rename, and choose Refactor > Rename. 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-7 shows what the Applications window would look like after renaming all of the associations and moving them to the oracle.summit.model.entities.assoc 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 Applications window.

Figure 4-7 Applications Window After Association Refactoring

Description of Figure 4-7 follows
Description of "Figure 4-7 Applications Window 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 How to Create an Entity-Based Programmatic View Object.

4.3.6 What You May Need to Know About Composition Associations

An association represents a relationship between entities, such as a Customer referenced by an Order or an Item contained in an Order. When you create 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-8 depicts an application business layer that represents both styles of relationships. For example, an OrdEO entry references a CustomerEO. This relationship represents the first kind of association, reflecting that a CustomerEO or an OrdEO entity object can exist independent from each other. In addition, the removal of an Order does not imply the cascade removal of the Customer to which it was referring.

In contrast, the relationship between an OrdEO and its collection of related ItemEO details is stronger than a simple reference. The ItemEO entries comprise a logical part of the overall OrdEO. In other words, an OrdEO is composed of ItemEO entries. It does not make sense for an ItemEO entity row to exist independently from an OrdEO, and when an OrdEO 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-8 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-8 OrdEO Composed of ItemEO Entries and References Both CustomerEO and PaymentTypeEO

Description of Figure 4-8 follows
Description of "Figure 4-8 OrdEO Composed of ItemEO Entries and References Both CustomerEO and PaymentTypeEO"

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.

Note:

A composition association cannot be based on a transient attribute.

An entity object offers additional runtime behavior in the presence of a composition. For the settings that control the behavior, see How to Configure Composition Behavior.

4.4 Creating a Diagram of Entity Objects for Your Business Layer

You can use JDeveloper to create a UML diagram of business services based on ADF Business Components. The UML diagram serves as a visualization, visual navigation, and editing tool.

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 Open. You can also perform some entity object editing tasks directly on the diagram, like renaming entity objects and attributes, and adding or removing attributes. However, changes to the entity objects in a business components diagram have no impact on the underlying database objects.

4.4.1 How to Show Entity Objects in a Business Components 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.

Before you begin:

It may be helpful to have an understanding of how entity diagrams are used in the application. For more information, see Creating a Diagram of Entity Objects for Your Business Layer.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a business components diagram that models existing entity objects:

  1. In the Applications window, right-click the project in which you want to create the entity diagram and choose New > From Gallery.
  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 Applications window and drop them onto the diagram surface.

After you have created the diagram you can use the Properties window 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-9 shows a sample diagram that models various entity objects from the business domain layer.

Figure 4-9 UML Diagram of Business Domain Layer

Description of Figure 4-9 follows
Description of "Figure 4-9 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 *.adfbc_diagram 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 Applications window 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-10, using the Applications Window Options button in the Applications window, you can see the distinct project content path root directories when you prefer.

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

This image is described in the surrounding text

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

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

<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

Define ADF property sets to use them as control hints and error messages.

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. Property sets can be used with entity objects and their attributes, view objects and their attributes, and application modules.

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.

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. Because property sets are schema driven, in addition to adding names, you can add descriptions as well.

When defining property sets that contain translatable content, be sure not to overload common terms in different contexts. 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. For example, the term "Name" used as a value for a field's label might be applied to both an object and a person in one language, but then translated into two different terms in a target language. In this case you would provide two name/value pairs in which the value in the source language is "Name" for both. Then, when translated into a target language with one term for an object name and a different term for a person name, each term can be translated appropriately. You can use the property's description to provide the distinct context for each term.

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 Properties window.

Before you begin:

It may be helpful to have an understanding of how property sets can be used. For more information, see Defining Property Sets.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To define a property set:

  1. In the Applications window, right-click the project in which you want to create the property set, and choose New > From Gallery.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Property Set, and click OK.

    Figure 4-11 Property Set in New Gallery

    Description of Figure 4-11 follows
    Description of "Figure 4-11 Property Set in New Gallery"
  3. In the Create Property Set dialog, enter the name and location of the property set and click OK.
  4. From the main menu, choose Window > Properties.
  5. In the Properties window, 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).

Before you begin:

It may be helpful to have an understanding of how property sets can be used. For more information, see Defining Property Sets.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

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

  1. In the Applications window, double-click the object (entity object or view object) to which you want to apply a property set.

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

  3. In the Select Property Set dialog, select the appropriate property set and click OK.

To apply a property set to an attribute:

  1. In the Applications window, double-click the object (entity object or view object) that contains the attribute to which you want to apply a property set.
  2. In the overview editor, click the Attributes navigation tab, select the attribute you want to edit, and then click the Details tab.
  3. On the Details page, select the appropriate property set from the Property Set dropdown list.

4.6 Defining Attribute Control Hints for Entity Objects

Control hints are inherited by ADF entity-based view objects. Use them to set label text, tooltip and format mask hints for attributes of entity objects.

Control hints allow you to define label text, tooltip, and format mask hints for entity object attributes. If desired, you can use a Groovy expression to calculate the value of the UI hint. The UI hints you define on your business domain layer are inherited by 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.

Before you begin:

It may be helpful to have an understanding of how control hints are used in an entity object. For more information, see Defining Attribute Control Hints for Entity Objects.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To add attribute control hints to an entity object:

  1. In the Applications window, double-click the entity object that contains the attribute to which you want to add control hints.
  2. In the overview editor, click the Attributes navigation tab and select the attribute you want to edit.
  3. In the Attributes page, click the UI Hints tab and specify control hints as necessary.

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

    • Format Type of Simple Date

    • Format mask of mm/yy

    Figure 4-12 Overview Editor, Attributes Page, UI Hints Tab

    Description of Figure 4-12 follows
    Description of "Figure 4-12 Overview Editor, Attributes Page, UI Hints Tab "
  4. If you want to define the UI hint using a Groovy expression, click the arrow beside the text box and choose Show Expression. The application displays the New Expression Method hyperlink. Click this hyperlink to display the Edit Expression Editor. Enter the Groovy expression in this editor and click OK.

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 Working with Resource Bundles.

4.6.3 About Formatters and Masks

When you set the Format Type control hint (on the UI Hints tab) 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.

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.

For information on how to define a format mask to use from the UI Hints tab in JDeveloper, see How to Define Format Masks.

4.6.4 How to Define Format Masks

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 on the UI Hints tab. The entries in this file define the format masks and formatter classes for a domain class. The following example shows the format definitions for the java.util.Date domain.

<?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>

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).

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 on the UI Hints tab. 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.

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.

Before you begin:

It may be helpful to have an understanding of how control hints are used in an entity object. For more information, see Defining Attribute Control Hints for Entity Objects.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To make a new format mask available in JDeveloper:

  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.

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

After defining a format mask, you can select the new format mask from the Format dropdown list on the UI Hints tab.

4.7 Working with Resource Bundles

JDeveloper stores translatable strings that you define on ADF Business Components in project-level resource bundle files.

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 Model project, JDeveloper creates the message bundle file named ModelBundle.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 How to Set Message Bundle Options.

You can inspect the message bundle file for the entity object by selecting the object in the Applications window 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 Applications window.

The following example 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.

oracle.summit.model.views.ItemVO.QuantityShipped_LABEL=Shipped
oracle.summit.model.views.ItemVO.ItemTotal_LABEL=Item Total
oracle.summit.model.views.ItemVO.ItemTotal_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter
oracle.summit.model.views.ItemVO.Price_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter
oracle.summit.model.views.OrdVO.DateOrdered_FMT_FORMATTER=oracle.jbo.format.DefaultDateFormatter
oracle.summit.model.views.OrdVO.DateOrdered_FMT_FORMAT=dd-MM-yyyy
oracle.summit.model.views.OrdVO.DateShipped_FMT_FORMATTER=oracle.jbo.format.DefaultDateFormatter
oracle.summit.model.views.OrdVO.DateShipped_FMT_FORMAT=dd-MM-yyyy
oracle.summit.model.entities.OrdEO_Rule_0=You cannot have a shipping date that is before the order date
oracle.summit.model.entities.OrdEO.CustomerId_Rule_0=This is an invalid customer id
errorId=This customer must pay cash
oracle.summit.model.entities.ItemEO.ProductId_Rule_0=Invalid Product Id
oracle.summit.model.views.OrdVO.Total_LABEL=Order Total
oracle.summit.model.views.OrdVO.Total_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter
oracle.summit.model.views.OrdVO.OrderFilled_LABEL=Order Filled
. . . 

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.

Before you begin:

It may be helpful to have an understanding of how resource bundles are used. For more information, see Working with Resource Bundles.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To set resource bundle options for your project:

  1. In the Applications window, right-click the project for which you want to specify resource bundle options, and choose Project Properties.
  2. In the Project Properties dialog, click Resource Bundle.
  3. On the Resource Bundle page, specify 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 described in How to Set Message Bundle Options) 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.

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.

Before you begin:

It may be helpful to have an understanding of how resource bundles are used. For more information, see Working with Resource Bundles.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

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.

The following example 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.

package oracle.summit.model;
import oracle.jbo.common.JboResourceBundle;
public class ModelImplMsgBundle_it 
       extends ModelImplMsgBundle {
  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

Use Business Logic Groups to store related control hints, default values and validation logic of ADF entity objects in a separate file for optimized performance and easy maintenance.

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, you might have a Orders entity object has a business logic group called FilledOrderGroup that uses OrderFilled as the discriminator attribute. Because this attribute has two valid values (N and Y), there are two 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 Orders_FilledOrderGroup_N business logic unit contains logic that pertains to orders that have not yet been filled. For example, the DateShipped attribute is configured to be hidden.

  • The Orders_FilledOrderGroup_Y business logic unit contains logic that pertains to orders that have been filled. For example, the DateShipped attribute is configured to be displayed.

4.8.1 How to Create a Business Logic Group

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

Before you begin:

It may be helpful to have an understanding of how business logic groups are used. For more information, see Defining Business Logic Groups.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a business logic group:

  1. In the Applications window, 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 OrderFilled, you can name the business logic group FilledOrderGroup.

  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.

Before you begin:

It may be helpful to have an understanding of how business logic groups are used. For more information, see Defining Business Logic Groups.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a business logic unit:

  1. In the Applications window, right-click the entity object that contains the business logic group and choose New Entity Business Logic Unit.
  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 OrderFilled, the name of the business logic unit associated with the OrderFilled value of Y must be Y.

  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 Applications window 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 Y in the FilledOrderGroup business logic group of the Orders entity object, the displayed name of the business logic unit is Orders_FilledOrderGroup_Y.

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.

Before you begin:

It may be helpful to have an understanding of how business logic groups are used. For more information, see Defining Business Logic Groups.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To add an entity validator to a business logic unit:

  1. In the Applications window, double-click the business logic unit to which you want to add an entity validator.
  2. In the overview editor, click the Business Rules navigation tab.
  3. On the Business Rules page, select the Entity Validators node and click the Add icon.
  4. In the Add Validation Rule dialog, define your validation rule, and click OK.

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.

Before you begin:

It may be helpful to have an understanding of how business logic groups are used. For more information, see Defining Business Logic Groups.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To override attributes in a business logic unit:

  1. In the Applications window, double-click the business logic unit that contains the attributes you want to override.
  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 in the tabs below the table. 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, as shown below.

<BusLogicGroup
    Name="FilledOrderGroup"
    DiscrAttrName="OrderFilled"/>

When you create a business logic unit, JDeveloper generates an XML file similar to that of an entity object. The following example 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.

<Entity
  xmlns="http://xmlns.oracle.com/bc4j"
  Name="Orders_FilledOrderGroup_N"
  Version="12.1.2.66.11"
  Extends="oracle.summit.model.buslogic.Orders"
  InheritPersonalization="merge"
  DBObjectType="table"
  DBObjectName="S_ORD"
  BindingStyle="OracleName"
  BusLogicGroupName="FilledOrderGroup"
  BusLogicUnitName="N">
  <Attribute
    Name="Total"
    Precision="11"
    Scale="2"
    ColumnName="TOTAL"
    SQLType="NUMERIC"
    Type="java.math.BigDecimal"
    ColumnType="NUMBER"
    TableName="S_ORD">
    <DesignTime>
      <Attr Name="_OverrideAttr" Value="true"/>
    </DesignTime>
    <Properties>
      <SchemaBasedProperties>
        <LABEL
      ResId="oracle.summit.model.buslogic.Orders_FilledOrderGroup_N.Total_LABEL"/>
      </SchemaBasedProperties>
    </Properties>
  </Attribute>
  <Attribute
    Name="DateShipped"
    ColumnName="DATE_SHIPPED"
    SQLType="DATE"
    Type="java.sql.Date"
    ColumnType="DATE"
    TableName="S_ORD">
    <DesignTime>
      <Attr Name="_OverrideAttr" Value="true"/>
    </DesignTime>
    <Properties>
      <SchemaBasedProperties>
        <DISPLAYHINT
          Value="Hide"/>
      </SchemaBasedProperties>
    </Properties>
  </Attribute>
  <ResourceBundle>
    <PropertiesBundle
      PropertiesFile="oracle.summit.model.ModelBundle"/>
  </ResourceBundle>
</Entity>

4.8.6 What Happens at Runtime: How Business Logic Groups Are Invoked

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

Use ADF entity objects to implement business logic and validation rules for the business layer using its declarative runtime features.

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 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 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.

Before you begin:

It may be helpful to have an understanding of declarative configuration of runtime behavior. For more information, see Configuring Runtime Behavior Declaratively.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To configure the declarative runtime behavior of an entity object:

  1. In the Applications window, 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.
    • The Alternate Keys section allows you to select entity object attributes mapped to the database that can serve as an alternative primary key. For information on alternative keys, see How to Define Alternate Key Values.

    • The Tuning section allows you to set options to make database operations more efficient when you create, modify, or delete multiple entities of the same type in a single transaction. For more information, see How to Use Update Batching.

    • The Custom Properties section allows you to define custom metadata that you can access at runtime on the entity.

    • The Security section allows you to define role-based updatability permissions for the entity. For more information, see Enabling ADF Security in a Fusion Web Application.

    • The Business Logic Groups section allows you to add and edit business logic groups. For more information, see Defining Business Logic Groups.

  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 Setting Attribute Properties.

    Note:

    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. The script box allows you to view and edit Groovy script for your business rules, and set breakpoints for debugging. For more information, see 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 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 Creating Business Events.
  7. Click the View Accessors navigation tab to create and manage view accessors. For more information, see 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 document 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.9.3 How to Use Update Batching

You can use update batching to reduce the number of DML statements issued with multiple entity modifications.

By default, the ADF Business Components framework performs a single DML statement (DELETE, INSERT, UPDATE) for each modified entity of a given entity definition type. For example, say you have an Employee entity object type for which multiple instances are modified during typical use of the application. If two instances were created, three existing instances modified, and four existing instances deleted, then at transaction commit time the framework issues nine DML statements (4 DELETEs, 2 INSERTs, and 3 UPDATEs) to save these changes.

If you will frequently be updating more than one entity of a given type in a transaction, consider using the update batching feature for that entity definition type. In the example, update batching (with a threshold of 1) causes the framework to issue just three DML statements: one bulk DELETE statement processing four deletes, one bulk INSERT statement processing two inserts, and one bulk UPDATE statement processing three updates.

The threshold value indicates the number of modified entity rows for a given operation that will be processed individually. When the number of modified entity rows exceeds the threshold value, batch processing will be used for that operation. In this example, if the threshold is 3, DELETE will perform batch update (because there are 4 DELETEs), but INSERTs and UPDATEs will not use batch (because there are only 2 INSERTs and 3 UPDATEs).

Note:

When update batching is used in entity objects with a composition relationship, all composed child objects must define a threshold value greater than 1 if the parent object defines a threshold value greater than 1.

The batch update feature is disabled if any of following conditions are present:

  • The entity object has attributes of BLOB or CLOB type. Batch DML with streaming data types is not supported.

  • The entity object has attributes that are set to Refresh After Insert or Refresh After Update. There is no method to bulk-return all of the trigger-assigned values in a single round trip, so retrieving and updating attributes doesn't work with batch DML.

  • The entity object was created from a table that did not have a primary key. If an entity object is reverse-engineered from a table that does not have a primary key, a ROWID-valued attribute is created and assigned as the primary key instead. The ROWID value is managed as a Retrieve-on-Insert value, so it will not work with batch DML.

Before you begin:

It may be helpful to have an understanding of the declarative configuration of runtime behavior. For more information, see Configuring Runtime Behavior Declaratively.

You may also find it useful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To enable update batching for an entity

  1. In the Applications window, double-click the entity for which you want to enable update batching.
  2. In the overview editor, click the General navigation tab.
  3. On the General page, expand the Tuning section, select the Use Update Batching checkbox, and specify the appropriate threshold.

This establishes a batch processing threshold beyond which Oracle ADF will process the modifications in a bulk DML operation.

4.10 Setting Attribute Properties

Use the declarative framework to set attribute properties on the Attributes page of the overview editor of ADF Business Components.

The declarative framework helps you set attribute properties easily. In all cases, you set these properties on 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 (for example, VARCHAR2(40) or NUMBER(4,2)). Based on this information, at runtime the entity object enforces the maximum length or precision and 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 How to Initialize the Data Model Project With a Database Connection.

The Type field (on the Details tab) allows you to change the Java type of the entity attribute to any type you might need. The Column Type field reflects the SQL type of the underlying database column to which the attribute is mapped. The value of the 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. The default Java attribute types are in the oracle.jbo.domain and various java packages, and support efficiently working with Oracle database data of the corresponding type. The dropdown list for the 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 java.math.BigDecimal java.math.BigInteger java.lang.Integer java.lang.Long

DATE

DATE

java.sql.Timestamp

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

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 oracle.jbo.domain.DomainInterface 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 Column Type field (on the Details tab) 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 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 Column Type field to be NUMBER(5,1).

Note:

Use this feature judiciously. If other applications can access this database column, then you cannot be sure that the other applications will respect the more restrictive constraints.

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 ItemEO entity object has multiple primary key attributes OrdId and ItemId. On the Attribute page of the overview editor, OrdId is first, and ItemId is second. An array of values encapsulated by the Key object for an entity row of type ItemEO 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.

In addition, to populate the primary key in new rows, you might want to use a trigger to assign the value from the database. For more information, see How to Get Trigger-Assigned Primary Key Values from a Database Sequence

4.10.6 How to Define a Static Default Value

The value field on the Details tab 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 an Expression

You can use a Groovy expression or SQL statement to define a default value for an attribute. This approach is useful if you want to be able to dynamically define default values at runtime, but if the default value is always the same, the value is easier to see and maintain using a value field with the Literal type (on the Details tab). For general information about using Groovy, see Using Groovy Scripting Language with Business Components.

Before you begin:

It may be helpful to have an understanding of how you set attribute properties. For more information, see Setting Attribute Properties.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To define a default value using an expression:

  1. In the Applications window, double-click the entity object that contains the attribute for which you want to define a default value.
  2. In the overview editor, click the Attributes navigation tab.
  3. On the Attributes page, select the attribute you want to edit, and then click the Details tab.
  4. In the Details page, select the value type, and click the Edit icon for the Value field.
    • To use a Groovy expression, select Expression.

    • To use a SELECT statement, select SQL.

  5. To enter:
    • SQL statement, enter the statement in the field provided and click OK. Note that you can enter an SQL statement only for transient attributes.

    • Groovy expression, click the icon beside to open the Edit Expression Editor dialog. In the Edit Expression Editor dialog, enter an expression in the field provided. Attributes that you reference can include any attribute that the business component defines. Specify the attributes on which this attribute is dependent by moving these attributes from Available to Selected. Click OK to save the expression

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 this business object's XML file within the appropriate attribute. The following example shows XML code for a Groovy expression that has been added for the ID attribute of the CountryVO view object. The XML definition informs you that the .bcs file where this Groovy expression script resides in is pointed to by the operations.xml file, which in this example is CodeSourceName=”CountryVOOperations.xml”. The operations.xml file has an URI which points the user to the .bcs file.

<View Attribute
        Name="Id"
        IsNotNull="true"
        PrecisionRule="true"
        EntityAttrName="Id"
        EntityUsage="CountryEO"
        AliasName="ID">
        <TransientExpression
                Name="ExpressionScript"
                trustMode="untrusted"
                CodeSourceName="CountryVORow"/>

</ViewAttribute> 

Note:

If Groovy file generation is enabled for this Model project or application-wide, then the Groovy expression displays as a hyperlink in Overview Editor page of this business component under the Default Value section. Click this hyperlink to open the .bcs file associated with this business component. Refer the topic What You May Need to Know About Groovy Project Settings to read more about project settings. Refer this topic What Happens When You Enter Expressions to read about how the application manages Groovy expressions when you enter them.

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 Refresh on Insert or Refresh on Update checkboxes on the Details tab to ensure that 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

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, as shown in the following example.

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;

Note:

You can create a trigger in a live database or, as an alternative, create it in an offline database and subsequently import the trigger to the live database. For more information about working with offline database objects, see the "Creating, Editing, and Dropping Database Objects" section in Developing Applications with Oracle JDeveloper.

On the Details tab (on the Attributes page of the overview editor), 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 on 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 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 Associations Based on DBSequence-Valued Primary Keys.

Note:

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

4.10.11 What You May Need to Know About Changing the Value of Primary Keys

The updating of primary key values through the user interface is not supported by ADF Business Components. You can, however, update a primary key value using DBSequence, as described in How to Get Trigger-Assigned Primary Key Values from a Database Sequence or programmatically, as described in Assigning the Primary Key Value Using an Oracle Sequence. In some cases, this kind of user interface behavior can be implemented using a surrogate key rather than allowing the user to change the primary key.

4.10.12 How to Protect Against Losing Simultaneously Updated Data

At runtime, the framework provides "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, because you selected the Refresh on Insert or Refresh on Update option (on the Details tab). Alternatively, you can indicate that the entity object should manage updating the change-indicator attribute's value using the history attribute feature described in How to Track Created and Modified Dates Using the History Column.

To detect, in the most efficient way, whether an entity row has been modified since the user queried it, select the Change Indicator option to compare only the change-indicator attribute values. Entity objects without a change-indicator attribute may perform less efficiently because values in all attributes will be used in data consistency checks. Entity objects without a change-indicator attribute are also subject to a loss of data consistency when the a user session loses affinity with its application module (when application module pooling is used). For more information, see What You May Need to Know About State Management and Data Consistency. For more information about application module pooling, see Tuning Application Module Pools.

4.10.13 How to Protect Against Truncated Attribute Values at Runtime

In rare situations, at runtime during application module passivation, string values may not be preserved correctly. For example, whitespace may get truncated. You may define a custom property on the entity object attribute to encapsulate your string value in CDATA section.

To define the XML_CDATA custom property and preserve whitespace formatting of an entity object attribute's default value:
  1. In the overview editor for the entity object, select the Attribute page and then select the attribute and click the Custom Properties tab.
  2. In the Custom Properties tab, add the property with the name XML_CDATA and set the value to true.
JDeveloper adds the custom property to the entity object XML definition, as the following example for an attribute CategoryCode shows.
<Attribute
   Name="CategoryCode"
   ...
   <Properties>
     <CustomProperties>
       <Property
         Name="XML_CDATA"
         ResId="model.Departments.LocationId.XML_CDATA_VALUE"/>
     </CustomProperties>
   </Properties>
</Attribute>

4.10.14 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 Track Change History option selected (on the Details tab).

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.15 How to Configure Composition Behavior

An entity object exhibits composition behavior when it creates (or composes) other entities, such as an OrdEO entity creating an ItemEO entity. This additional runtime behavior determines its role as a logical container of other nested entity object parts. Because of this relationship, a composition association cannot be based on a transient attribute.

Note:

Composition also affects the order in which entities are validated. For more information, see 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.15.1 Orphan-Row Protection for New Composed Entity Objects

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.15.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.15.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-13 shows the Relationships page for the SItemOrdIdFkAssoc association between two entity objects: ItemEO and OrdEO.

4.10.15.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-13) is deselected, the removal of the composing entity object is prevented if it contains any composed children.

Figure 4-13 Behavior Settings on Relationship Page of Overview Editor for Associations

This image is described in the surrounding text

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.15.5 Cascade Update of Foreign Key Attributes When Primary Key Changes

Select the Cascade Update Key Attributes option (see Figure 4-13) 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.15.6 Locking of Composite Parent Entity Objects

Select the Lock Top-Level Container option (see Figure 4-13) 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.15.7 Using Batch Update with Composition Entity Objects

When update batching is used in entity objects with a composition relationship, all composed child objects must define a threshold value greater than 1 if the parent object defines a threshold value greater than 1.

4.10.15.8 Updating of Composing Parent History Attributes

Select the Update Top-Level History Columns option (see Figure 4-13) 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.16 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. Using Inheritance in Your Business Domain Layer, explains how to set up and use inheritance.

4.10.17 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 How to Ensure That Key Values Are Unique.

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

Before you begin:

It may be helpful to have an understanding of how you set attribute properties. For more information, see Setting Attribute Properties.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To define alternate key values:

  1. In the Applications window, right-click the entity object for which you want to define an alternate key 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.18 What Happens When You Define Alternate Key Values

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

4.10.19 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 Adding Transient and Calculated Attributes to an Entity Object

Add transient attributes in ADF entity objects based on one-to-many columns. Use an entity object’s Java class or use a Groovy expression to define a calculated attribute of a transient attribute.

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 way the value is calculated at runtime, you can use a Groovy expression. If the way the value is calculated 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.11.1 How to Add a Transient Attribute

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

Before you begin:

It may be helpful to have an understanding of the use of transient and calculated attributes. For more information, see Adding Transient and Calculated Attributes to an Entity Object.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To add a transient attribute to an entity object:

  1. In the Applications window, double-click the entity object to which you want to add a transient attribute.
  2. In the overview editor, click the Attributes navigation tab, and then click the New icon and choose New Attribute.
  3. In the New Entity Attribute dialog, enter a name for the attribute, select the Java attribute type from the Type dropdown list, and click OK.
  4. On the Attributes page of the overview editor, click the Details tab and select the Transient radio button.
  5. If the value will be calculated, select Never from the Updatable dropdown list.

4.11.2 What Happens When You Add a Transient Attribute

When you add a transient attribute, JDeveloper updates the XML document for the entity object to reflect the new attribute. The <Attribute> tag of a transient attribute has no TableName or ColumnName, as shown in the following example.

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

In contrast, a persistent entity attribute has both a TableName and a ColumnName, as shown in the following example.

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

4.11.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.

Before you begin:

It may be helpful to have an understanding of transient and calculated attributes. For more information, see Adding Transient and Calculated Attributes to an Entity Object.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a transient attribute based on a Groovy expression:

  1. In the Applications window, double-click the entity object to which you want to add a transient attribute.
  2. In the overview editor, click the Attributes navigation tab, and then click the New icon and choose New Attribute.
  3. In the New Entity Attribute dialog, enter a name for the attribute, select the Java attribute type from the Type dropdown list, and click OK.
  4. On the Attributes page of the overview editor, click the Details tab and select the Transient option.
  5. If the value will be calculated, select Never from the Updatable dropdown list.
  6. Select Expression checkbox under Default Value and click the icon beside to open the Edit Expression Editor dialog.

    Expressions that you define are evaluated using the Groovy scripting language, as described in Using Groovy Scripting Language with Business Components. Use the Groovy programming language to insert expressions and variables into strings. The expression is saved as part of the entity object definition.

  7. In the Edit Expression Editor dialog, enter an expression in the field provided.

    Attributes that you reference can include any attribute that the entity object defines. For attributes not defined by the entity object, use the appropriate context object to reference these attributes in the expression.

    Figure 4-14 Edit Expression Editor

    Description of Figure 4-14 follows
    Description of "Figure 4-14 Edit Expression Editor"
  8. Specify the attributes on which this attribute is dependent. Move these attributes from Available to Selected.
  9. Click OK to save the expression.
  10. If required, apply a recalculation expression that if evaluates to be true, recalculates the default expression. In the Refresh Expression Value field, click the icon beside to open the Edit Expression Editor dialog. Use the same procedure that you used to define a default expression to define the recalculation expression as well.

    For example, the following expression defined using the Edit Expression Editor dialog for the Refresh Expression Value field causes the attribute to be recalculated when either the QuantityShipped attribute or the Price attributes are changed:

    return (adf.object.isAttributeChanged("QuantityShipped") || adf.object.isAttributeChanged("Price"));
    

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. If you didn't provide dependencies in the Edit Expression Editor dialog when you defined the expression, you can do so on the Dependencies tab (on the Attributes page). On the Dependencies tab, locate the attributes in the Available list and shuttle each to the Selected list.

4.11.4 What Happens When You Base a Transient Attribute on a 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 the following example.

  <Attribute
    Name="QuantityTotal"
    ColumnName="QUANTITY_TOTAL"
    ...
    <TransientExpression
      Name="ExpressionScript"
      trustMode="untrusted"
      CodeSourceName="ItemEORow"/>
    <Dependencies>
      <Item
        Value="Price"/>
      <Item
        Value="QuantityShipped"/>
    </Dependencies>
  </Attribute>

4.11.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 tab of the Attributes page

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 the following example.

// 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. Even though you perform the computation in the code, the dependencies are needed to fire the appropriate events that cause the UI to refresh when any of the dependent attribute values change. To specify dependent attributes, on the Dependencies tab of the Attributes page, locate the attributes in the Available list and shuttle each to the Selected list.

4.12 Creating Business Events

You can define and publish business events from the ADF Model layer and synchronize them with external systems by way of Oracle Mediator.

Business events raised from the model layer are useful for launching business processes and triggering external systems synchronization.

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

Note:

Oracle Mediator provides a lightweight framework to mediate between various components within a composite application. Oracle Mediator converts data to facilitate communication between different interfaces exposed by different components that are wired to build a SOA composite application. For example, Oracle Mediator can accept data contained in a text file from an application or service, transform it into a format appropriate for updating a database that serves as a customer repository, and then route and deliver the data to that database.

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 perform the following tasks:

  1. Create an event definition, as described in How to Create a Business Event.

  2. Map the event definition to an event point and publish the event definition, as described in How to Define a Publication Point for a Business Event.

After the business event is published, you can subscribe to the event from another component, as described in How to Subscribe to Business Events.

4.12.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.12.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.12.3 What You May Need to Know About Event Points

Event delivery occurs outside the current database transaction, and is asynchronous. Therefore, the following are not supported:

  • 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.12.4 How to Create a Business Event

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

Before you begin:

It may be helpful to have an understanding of how business events work. For more information, see Creating Business Events.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a business event:

  1. In the Applications window, double-click the entity object for which you want to define a business event.
  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 What You May Need to Know About Payload.

  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 How to Define a Publication Point for a Business Event.

4.12.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, as shown in the following example.

<EventDef
    Name="CustBusEvent1">
    <Payload>
      <PayloadItem
        AttrName="Id"/>
      <PayloadItem
        AttrName="PaymentOptionId"/>
      <PayloadItem
        AttrName="PaymentTypeEO.Id"
        SendOnlyIfChanged="true"/>
    </Payload>
  </EventDef>

JDeveloper also generates an associated XSD file for the event schema and an event definition (EDL) file for the entity object. The EDL file provides an event definition, as shown in the following example.

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

The XSD file 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.

The following example shows an XSD event schema for a business event.

<?xml version = '1.0' encoding = 'UTF-8'?>
<xs:schema targetNamespace="http://oracle/summit/model/entities/events/schema/OrdEO" xmlns="http://oracle/summit/model/entities/events/schema/OrdEO" 
                                           elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="CustBusEvent1Info">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="Id" type="DecimalValuePair" minOccurs="1"/>
            <xs:element name="PaymentOptionId" type="IntValuePair" minOccurs="1"/>
         </xs:sequence>
      </xs:complexType>
   </xs:element>
   <xs:complexType name="ValuePair" abstract="true"/>
   <xs:complexType name="IntValuePair">
      <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:int"/>
                        </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:int"/>
                        </xs:extension>
                     </xs:complexContent>
                  </xs:complexType>
               </xs:element>
            </xs:sequence>
         </xs:extension>
      </xs:complexContent>
   </xs:complexType>
   <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>

4.12.6 What You May Need to Know About Payload

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 for only the attributes that should be sent, 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.

In the case of entity subtypes (for example, a Staff entity object is a subtype of the Persons entity), ADF Business Components does not support the overriding of business events. Because the subscriber to a business event listens to the event using the event name, overriding of events could cause the event subscriber to receive payload data unintended for that subscriber. Therefore, this capability is not supported.

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

4.12.7 How to Define a Publication Point for a Business Event

To define a publication point for a business event, use the Business Events page of the entity object overview editor.

Before you begin:

It may be helpful to have an understanding of how business events are used in the application. For more information, see Creating Business Events.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You need to have already created the event definition, as described in How to Create a Business Event, before you can publish it.

To define a publication point for a business event:

  1. In the Applications window, double-click the entity object that contains the business event you want to publish.
  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. Click the new cell in Event column, and select the appropriate event.
  6. 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.12.8 How to Subscribe to Business Events

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

Note:

To subscribe to business events or create and deploy SOA composite applications and projects in JDeveloper, you must install the Oracle SOA Suite extension. For instructions on installing this extension for JDeveloper, see Enabling Oracle JDeveloper Extensions in Installing Oracle JDeveloper. For more information about the capabilities of Oracle SOA Suite, see Introduction to Building Applications with Oracle SOA Suite in Developing SOA Applications with Oracle SOA Suite.

Before you begin:

It may be helpful to have an understanding of business events. For more information, see Creating Business Events.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You will also need to complete the following task:

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 JDeveloper, open the SCA project that will subscribe to the business event.
  3. In the Applications window, right-click the SCA project that will subscribe to the business event, and choose New > From Gallery.
  4. In the New Gallery, expand SOA Tier, select Service Components and then Mediator, and click OK.
  5. In the Create Mediator dialog, select the Subscribe to Events template, as shown in Figure 4-15.

    Figure 4-15 Create Mediator Dialog, Subscribe to Events

    Description of Figure 4-15 follows
    Description of "Figure 4-15 Create Mediator Dialog, Subscribe to Events"
  6. Click the Add icon to add an event.
  7. In the Event Chooser dialog, click the Browse icon to navigate to and select the event's definition file, and then click OK.
  8. In the Create Mediator dialog, you can optionally change the Consistency option and specify a Filter for the event.
  9. Click OK to generate the mediator.

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

  10. In the overview editor, click the Add icon in the Routing Rules section to add a rule for how to respond to the event.

4.13 Generating Custom Java Classes for an Entity Object

Enable custom Java generation for an ADF entity object to implement custom business logic that cannot be achieved using its declarative runtime functionality.

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. 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.

Best Practice:

Oracle recommends that developers getting started with ADF Business Components consider overriding the base framework classes to enable all generated custom Java classes to pick up customized behavior. This practice is preferred over creating custom classes for individual entity objects and view objects. You use the ADF Business Components > Base Classes page of the Project Properties dialog to specify the framework classes to override when you generate custom component. See Extending Business Components Functionality.

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.

Before you begin:

It may be helpful to have an understanding of custom Java classes. For more information, see Generating Custom Java Classes for an Entity Object.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To generate a custom Java class for an entity object:

  1. In the Applications window, double-click the entity object for which you want to generate a custom Java class.
  2. In the overview editor, click the Java navigation tab, and then click the Edit Java options 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 summit.model.entities.OrdEO, the default names for its custom Java files will be OrdEOImpl.java for the entity object class and OrdEODefImpl.java for the entity definition class. Both files are created in the same ./summit/model/entities directory as the component's XML document 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.

Caution:

Because the resulting hard-coded method names impose limitations on optimizations or customizations for entity and view rows, you should generate an accessor method only if there is a business need for it (such as, having to expose the view row as a Java interface to a client).

For example, an OrdEO entity object that has the corresponding custom OrdEOImpl.java class might have methods like those shown in the following example.

public DBSequence getId() { ... }
public void setId(DBSequence value) { ... }
 
public Date getDateOrdered() { ... }
public void setDateOrdered(Date value) { ... }
 
public Integer getPaymentTypeId() { ... }
public void setPaymentTypeId(Integer value) { ... }
 
public Number getCustomerId() { ... }
public void setCustomerId(Number value) { ... }
 
public String getOrderFilled() { ... }
public void setOrderFilled(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-16, 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 Applications window, 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 multiple ways to open the file in the source editor:

  • You can right-click the Java file, and choose Open, as shown in Figure 4-16.

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

  • You can click the link on the Java page of the overview editor.

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

This image is described in the surrounding text

4.13.5 What You May Need to Know About Custom Java Classes

The custom Java classes generated by JDeveloper extend the base classes for your entity object, and allow you the flexibility to implement custom code while maintaining the integrity of the generated code. The following sections provide additional information about custom Java classes.

4.13.5.1 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 extends 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 and the entity collection class will extend EntityCache. These base classes are in the oracle.jbo.server package.

4.13.5.2 Safely Adding 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 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 Tools >Preferences from the main 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 (the default setting). 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 document 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). By default, reflection is used at runtime to assign attribute enumerations and invoke attribute accessors.

When reflection is turned off for the project, attributes are assigned a name and a numerical index in the entity's XML document 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.

Note:

To disable reflection, deselect Use reflection to invoke attribute accessors on the Options page (under ADF Business Components) in the Project Properties dialog.

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 an Orders 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 document.

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 the following example.

// In oracle.summit.model.appmodule.OrdersImpl class
    /**
     * Gets the attribute value for DateOrdered, using the alias name DateOrdered.
     * @return the value of DateOrdered
     */
  public Date getDateOrdered() {
    return (Date)getAttributeInternal(DATEORDERED);
  }
    /**
     * Sets <code>value</code> as the attribute value for DateOrdered.
     * @param value to set the DateOrdered
     */
  public void setDateOrdered(Date value) {
    setAttributeInternal(DATEORDERED, value);
  }

Another aspect of the maintained code related to entity attribute enums is 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 the following example. The companion setAttrInvokeAccessor() method looks similar.

// In oracle.summit.model.appmodule.OrdersImpl class
  /**
   * getAttrInvokeAccessor: generated method. Do not modify.
   * @param index the index identifying the attribute
   * @param attrDef the attribute
   * @return the attribute value
   * @throws Exception
   */
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 document 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 document.

4.13.6 Programmatic Example for Comparison Using Custom Entity Class References

To better evaluate the difference between using strongly typed entity references to custom entity classes and working with the generic entity references, consider the following example. The following example shows a version of strongly typed entity references in a custom application module class (AppModuleImpl.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.

package oracle.summit.model.appmodule.service;

import oracle.jbo.ApplicationModule;
import oracle.jbo.JboException;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.RowSetIterator;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.DBSequence;
import oracle.jbo.domain.Number;
import oracle.jbo.server.EntityDefImpl;
import oracle.jbo.server.EntityImpl;
import oracle.jbo.server.ViewLinkImpl;

import oracle.summit.base.SummitApplicationModuleImpl;
import oracle.summit.base.SummitViewObjectImpl;
import oracle.summit.model.appmodule.CustomerImpl;
import oracle.summit.model.appmodule.OrdersImpl;
import oracle.summit.model.appmodule.service.common.AppModule;

// ---------------------------------------------------------------------
// ---    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.
// ---------------------------------------------------------------------
public class AppModuleImpl extends SummitApplicationModuleImpl 
       implements AppModule {
  /* This is the default constructor (do not remove). */
  public AppModuleImpl() {
  }

  private OrdersImpl retrieveOrderById(long orderId) {
    EntityDefImpl orderDef = OrdersImpl.getDefinitionObject();
    Key orderKey = OrdersImpl.createPrimaryKey(new DBSequence(orderId));
    return (OrdersImpl)orderDef.findByPrimaryKey(getDBTransaction(),orderKey);
  }

  public String findOrderAndCustomer(long orderId) {
    OrdersImpl order = retrieveOrderById(orderId);
    if (order != null) {
      CustomerImpl cust = order.getCustomer();
      if (cust != null) {
        return "Customer: " + cust.getName() + ", Location: " + cust.getCity();
        }
      else {
        return "Unassigned";
      }
    }
    else {
      return null;
    }
  }  

    /* Find an Order by Id */
  public String findOrderTotal(long orderId) {
    OrdersImpl order = retrieveOrderById(orderId);
    if (order != null) {
      return order.getTotal().toString();
    }
    return null;
  }

    /* Update the status of an existing order */
  public void updateOrderStatus(long orderId, String newStatus) {
    OrdersImpl order = retrieveOrderById(orderId);
    if (order != null) {
      order.setOrderFilled(newStatus);
      try {
        getDBTransaction().commit();
      }
      catch (JboException ex) {
        getDBTransaction().rollback();
        throw ex;
      }
    }
  }

    /* Create a new Customer and Return the new id */
  public long createCustomer(String name, String city, Integer countryId) {
    EntityDefImpl customerDef = CustomerImpl.getDefinitionObject();
    CustomerImpl newCustomer =                (CustomerImpl)customerDef.createInstance2(getDBTransaction(),null);
    newCustomer.setName(name);
    newCustomer.setCity(city);
    newCustomer.setCountryId(countryId);
    try {
      getDBTransaction().commit();
    }
    catch (JboException ex) {
      getDBTransaction().rollback();
      throw ex;
    }
    DBSequence newIdAssigned = newCustomer.getId();
    return newIdAssigned.getSequenceNumber().longValue();
  }

    /* Custom method in an application module implementation class */
  public void doSomeCustomProcessing() {
    ViewObject vo = getCustomerView1();
    // create secondary row set iterator with system-assigned name
    RowSetIterator iter = vo.createRowSetIterator(null);
    while (iter.hasNext()) {
      Row r = iter.next();
      // Do something with the current row.
      Integer custId = (Integer)r.getAttribute("Id");
      String name  = (String)r.getAttribute("Name");
      System.out.println(custId + " " + name);
    }
    // close secondary row set iterator
    iter.closeRowSetIterator();
  }

    /* Container's getter for CustomerView1.
     * @return CustomerView1 */
  public SummitViewObjectImpl getCustomerView1() {
    return (SummitViewObjectImpl) findViewObject("CustomerView1");
  }

    /* Container's getter for ItemView1. 
     * @return ItemView1 */
  public SummitViewObjectImpl getItemView1() {
    return (SummitViewObjectImpl) findViewObject("ItemView1");
  }

    /* Container's getter for OrderView1. 
     * @return OrderView1 */
  public SummitViewObjectImpl getOrderView1() {
    return (SummitViewObjectImpl) findViewObject("OrderView1");
  }

    /* Container's getter for ItemView2.
     * @return ItemView2 */
  public SummitViewObjectImpl getItemView2() {
    return (SummitViewObjectImpl) findViewObject("ItemView2");
  }

    /* Container's getter for OrderView2.
     * @return OrderView2 */
  public SummitViewObjectImpl getOrderView2() {
    return (SummitViewObjectImpl) findViewObject("OrderView2");
  }

    /*Container's getter for SItemOrdIdFkLink1.
     * @return SItemOrdIdFkLink1 */
  public ViewLinkImpl getSItemOrdIdFkLink1() {
    return (ViewLinkImpl) findViewLink("SItemOrdIdFkLink1");
  }

    /* Container's getter for SOrdCustomerIdFkLink1.
     * @return SOrdCustomerIdFkLink1 */
  public ViewLinkImpl getSOrdCustomerIdFkLink1() {
    return (ViewLinkImpl) findViewLink("SOrdCustomerIdFkLink1");
  }
}

4.14 Working Programmatically with Entity Objects and Associations

JDeveloper allows you to access an ADF application module and work directly in the ADF data model using an external client program.

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. 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.14.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 the sample code below.

Before you begin:

It may be helpful to have an understanding of when to use a programmatic approach for working with entity objects and associations. For more information, see Working Programmatically with Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To find an entity object by primary key:

  1. Find the entity definition.

    You obtain the entity definition for an entity object (for example, Orders) 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. For example, for the Orders entity object you create 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.

The following example code shows a retrieveOrderById() method developed using this basic procedure.

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

Note:

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

4.14.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 findOrderAndCustomer() might find an order, then access the associated Customer 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 Creating and Configuring Associations.

To prevent 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, you might create a retrieveOrderById() method to refactor the functionality that finds an order, as the shown in the sample code below.

Before you begin:

It may be helpful to have an understanding of when to use a programmatic approach for working with entity objects and associations. For more information, see Working Programmatically with Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To access an associated entity object using the accessor attribute:

  1. Find the associated entity by the accessor attribute.

    For example, the findOrderAndCustomer() method uses the retrieveOrderById() helper method to retrieve the Orders 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 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.

    For example, the findOrderAndCustomer() method uses the getter methods on the returned Customer entity to return the assigned customer's name and location.

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

The following example shows the code for findOrderCustomer() that uses the helper method.

/*  Access an associated Customer entity from the Order entity   */  
public String findOrderAndCustomer(long orderId) {
  OrdersImpl order = retrieveOrderById(orderId);
  if (order != null) {
    CustomerImpl cust = order.getCustomer();
    if (cust != null) {
      return "Customer: " + cust.getName() + ", Location: " + cust.getCity();
    }
    else {
      return "Unassigned";
    }
  }
  else {
    return null;
  }
}

4.14.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. For example, you could add a method like updateOrderStatus() shown in the sample code below to perform the task.

Before you begin:

It may be helpful to have an understanding of when to use a programmatic approach for working with entity objects and associations. For more information, see Working Programmatically with Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To update an entity row:

  1. Find the order by ID.

    Using the retrieveOrderById() helper method, the updateOrderStatus() method retrieves the Orders 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 OrderFilled attribute to the new value passed in.

  3. Commit the transaction.

    You will then need to use the application module's getDBTransaction() method to access the current transaction object and calls its commit() method to commit the transaction.

    /* Update the status of an existing order */  
    public void updateOrderStatus(long orderId, String newStatus) {
      OrdersImpl order = retrieveOrderById(orderId);
      if (order != null) {
        order.setOrderFilled(newStatus);
      }
    }
    

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

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

4.14.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 customer entities, you could write a createCustomer() method like the one shown in the following example to accept the name and description of a new customer, and return the new customer ID assigned to it. This example assumes that the Id attribute of the Customer entity object has been updated to have the DBSequence type (see 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.

/* Create a new Customer and Return the new id */
public long createCustomer(String name, String city, Integer countryId) {
  EntityDefImpl customerDef = CustomerImpl.getDefinitionObject();
  CustomerImpl newCustomer = (CustomerImpl)customerDef.createInstance2(getDBTransaction(),null);
    newCustomer.setName(name);
    newCustomer.setCity(city);
    newCustomer.setCountryId(countryId);
  try {
    getDBTransaction().commit();
  }
  catch (JboException ex) {
    getDBTransaction().rollback();
    throw ex;
  }
  DBSequence newIdAssigned = newCustomer.getId();
  return newIdAssigned.getSequenceNumber().longValue();
}

Before you begin:

It may be helpful to have an understanding of when to use a programmatic approach for working with entity objects and associations. For more information, see Working Programmatically with Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create an entity row:

  1. Find the entity definition.

    Using the getDefinitionObject() method, the createCustomer() method finds the entity definition for the Customer entity.

  2. Create a new instance.

    Using the createInstance2() method on the entity definition, the createCustomer() 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 EntityImpl Class of 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 createCustomer() method assigns values for the Name, City, and other attributes in the new entity row.

  4. Commit the transaction.

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

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

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

4.14.5 Assigning the Primary Key Value Using an Oracle Sequence

As an alternative to using a trigger-assigned value (as described in 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.

The following example 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.

Before you begin:

It may be helpful to have an understanding of when to use a programmatic approach for working with entity objects and associations. For more information, see Working Programmatically with Entity Objects and Associations.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

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 the sample below.
  2. In the Applications window, double-click the entity you want to edit.
  3. In the overview editor, click the Attributes navigation tab, and select the attribute you want to edit.
  4. Click the Details tab and select Number from the Type dropdown list.
  5. Click the Custom Properties tab, and click the Add icon.
  6. Create a custom property with SequenceName for the name, and the name of the database sequence for the value.

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

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());
            }
        }
    }
}

4.14.6 How to Update a Deleted Flag Instead of Deleting Rows

For auditing purposes, once a row is added to a table, sometimes your requirements may demand that rows never be physically deleted from the table. Instead, when the end user deletes the row in the user interface, the value of a DELETED column should be updated from "N" to "Y" to mark it as deleted. You can use two method overrides to alter an entity object's default behavior to achieve this effect.

To accomplish this, you need to perform the following tasks:

4.14.6.1 Updating a Deleted Flag When a Row Is Removed

To update a deleted flag when a row is removed, enable a custom Java class for your entity object and override the remove() method to set the deleted flag before calling the super.remove() method. The following example shows what this would look like in the custom Java class of an entity object. It is important to set the attribute before calling super.remove() since an attempt to set the attribute of a deleted row will encounter the DeadEntityAccessException.

This example presumes that you've altered the table to have an additional DELETED column, and synchronized the entity object with the database to add the corresponding Deleted attribute.

The row will still be removed from the row set, but it will have the value of its Deleted flag modified to "Y" in the entity cache. The second part of implementing this behavior involves forcing the entity to perform an UPDATE instead of an DELETE when it is asked to perform its DML operation. You need to implement both parts for a complete solution.

// In your custom Java entity class
public void remove() {
  setDeleted("Y");
  super.remove();
}

4.14.6.2 Forcing an Update DML Operation Instead of a Delete

To force an entity object to be updated instead of deleted, override the doDML() method and write code that conditionally changes the operation flag. When the operation flag equals DML_DELETE, your code will change it to DML_UPDATE instead. The following example shows what this would look like in the custom Java class of an entity object.

This example presumes that you've altered the table to have an additional DELETED column, and synchronized the entity object with the database to add the corresponding Deleted attribute.

With this overridden doDML() method in place to complement the overridden remove() method described in Updating a Deleted Flag When a Row Is Removed, any attempt to remove an entity through any view object with a corresponding entity usage will update the DELETED column instead of physically deleting the row. Of course, in order to prevent "deleted" products from appearing in your view object query results, you will need to appropriately modify their WHERE clauses to include only products WHERE DELETED = 'N'.

// In your custom Java entity class
protected void doDML(int operation, TransactionEvent e) {
   if (operation == DML_DELETE) {
     operation = DML_UPDATE;
   }
   super.doDML(operation, e);
  }

4.14.7 How to Control Entity Posting Order to Prevent Constraint Violations

Due to database constraints, when you perform DML operations to save changes to a number of related entity objects in the same transaction, the order in which the operations are performed can be significant. If you try to insert a new row containing foreign key references before inserting the row being referenced, the database can complain with a constraint violation. You must understand what the default order for the processing of entity objects is during commit time and how to programmatically influence that order when necessary.

Note:

The example in this section refers to the oracle.summit.model.controlpostorder package in the SummitADF_Examples application workspace.

4.14.7.1 Default Post Processing Order

By default, when you commit the transaction the entity objects in the pending changes list are processed in chronological order, in other words, the order in which the entities were added to the list. This means that, for example, if you create a new employee (EmpEO entity object) and then a new department (DeptEO entity object) related to that product, the new EmpEO will be inserted first and the new DeptEO second.

4.14.7.2 Compositions and Default Post Processing Order

When two entity objects are related by a composition, the strict chronological ordering is modified automatically to ensure that composed parent and child entity rows are saved in an order that prevents violating any constraints. This means, for example, that a new parent entity row is inserted before any new composed children entity rows.

4.14.7.3 Overriding postChanges() to Control Post Order

If your related entities are associated but not composed, then you need to write a bit of code to ensure that the related entities get saved in the appropriate order.

4.14.7.3.1 Observing the Post Ordering Problem First Hand

Consider the newEmployeeForNewDepartment() custom method from the AppModule application module in the following example. It accepts a set of parameters and:

  1. Creates a new employee.
  2. Creates a new department.
  3. Sets the department ID to which the employee pertains.
  4. Commits the transaction.
  5. Constructs a Result Java bean to hold new employee ID and department ID.
  6. Returns the result.

Note:

The code makes the assumption that both EmpEO.Id and DeptEO.Id have been set to have DBSequence data type to populate their primary keys based on a sequence.

// In AppModuleImpl.java
public Result newEmployeeForNewDepartment(String deptName, 
                                           Number regionId, 
                                           String lastName, 
                                           String firstName,
                                           Number salary)  {

   oracle.jbo.domain.Date today = new Date(Date.getCurrentDate());
   Number objectId = new Number(0);
   
   // 1. Create a new employee
   EmpEOImpl newEmp = createNewEmp();
   // 2. Create a new department
   DeptEOImpl newDept = createNewDept();
   newDept.setName(deptName);
   newDept.setRegionId(regionId);
   // 3. Set the department id to which the employee pertains
   newEmp.setDeptId(newDept.getId().getSequenceNumber());
   newEmp.setLastName(lastName);
   newEmp.setFirstName(firstName);
   newEmp.setUserid((firstName + "." + lastName).substring(8));
   newEmp.setSalary(salary);
   // 4. Commit the transaction
   getDBTransaction().commit();
   // 5. Construct a bean to hold new department id and employee id
   Result result = new Result();
   result.setEmpId(newEmp.getId().getSequenceNumber());
   result.setDeptId(newDept.getId().getSequenceNumber());
   // 6. Return the result
   return result;
}
private DeptEOImpl createNewDept(){
   EntityDefImpl deptDef = DeptEOImpl.getDefinitionObject();
   return (DeptEOImpl) deptDef.createInstance2(getDBTransaction(), null);
}
private EmpEOImpl createNewEmp(){
   EntityDefImpl empDef = EmpEOImpl.getDefinitionObject();
   return (EmpEOImpl) empDef.createInstance2(getDBTransaction(), null);
}

If you add this method to the application module's client interface and test it from a test client program, you get an error:

oracle.jbo.DMLConstraintException: JBO-26048: 
Constraint "S_EMP_DEPT_ID_FK" is violated during post operation 
"Insert" using SQL statement 
"BEGIN INSERT INTO S_EMP(ID,LAST_NAME,FIRST_NAME,USERID,DEPT_ID,SALARY) 
 VALUES (:1,:2,:3,:4,:5,:6) RETURNING ID INTO :7; END;".
: ORA-02291: integrity constraint (SUMMIT_ADF.S_EMP_DEPT_ID_FK) 
violated - parent key not found

When the S_EMP row is inserted, the database complains that the value of its DEPT_ID foreign key doesn't correspond to any row in the S_DEPT table. This occurred because:

  • The code created the employee before the department.

  • The EmpEO and DeptEO entity objects are associated but not composed.

  • The DML operations to save the new entity rows is done in chronological order, so the new EmpEO gets inserted before the new DeptEO.

4.14.7.3.2 Forcing the Department to Post Before the Employee

To remedy the problem of attempting to add an employee with a not-yet-valid department ID, you could reorder the lines of code in the example to create the DeptEO first, then the EmpEO. While this would address the immediate problem, it still leaves the chance that another application developer could create things in an incorrect order.

The better solution is to make the entity objects themselves handle the posting order so the posting will work correctly regardless of the order of creation. To do this, you need to override the postChanges() method in the entity that contains the foreign key attribute referencing the associated entity object and write code as shown in the following example. In this example, since it is the EmpEO that contains the foreign key to the DeptEO entity, you need to update the EmpEO to conditionally force a related, new DeptEO to post before the employee posts itself.

The code tests whether the entity being posted is in the STATUS_NEW or STATUS_MODIFIED state. If it is, it retrieves the related product using the getDeptEO() association accessor. If the related DeptEO also has a post-state of STATUS_NEW, then first it calls postChanges() on the related parent row before calling super.postChanges() to perform its own DML.

If you were to run this example, you would see that without changing the creation order in the newEmployeeForNewDepartment() method's code, entities now post in the correct order — first new DeptEO, then new EmpEO. Yet, there is still a problem. The constraint violation still appears, but now for a different reason.

If the primary key for the DeptEO entity object were user-assigned, then the code in the following example would be all that is required to address the constraint violation by correcting the post ordering. In this example, however, the DeptEO.Id is assigned from a database sequence, and not user-assigned. So when a new DeptEO entity row gets posted, its Id attribute is refreshed to reflect the database-assigned sequence value. The foreign key value in the EmpEO.DeptId attribute referencing the new supplier is "orphaned" by this refreshing of the supplier's ID value. When the product's row is saved, its S_DEPT_ID value still doesn't match a row in the S_DEPT table, and the constraint violation occurs again. The next two sections discuss the solution to address this "orphaning" problem.

// In EmpEOImpl.java
public void postChanges(TransactionEvent transactionEvent) {
   /* If current entity is new or modified */
   if (getPostState() == STATUS_NEW || 
      getPostState() == STATUS_MODIFIED) {
      /* Get the associated dept for the employee */
      DeptEOImpl dept = getDeptEO();
      /* If there is an associated dept */
      if (dept != null) {
         /* And if it's post-status is NEW */
         if (dept.getPostState() == STATUS_NEW) {
            /*
   * Post the department first, before posting this
   * entity by calling super below
   */
            dept.postChanges(transactionEvent);
         }
      }
   }
   super.postChanges(transactionEvent);
}

Note:

An alternative to the programmatic technique discussed here, which solves the problem at the Java EE application layer, is the use of deferrable constraints at the database layer. If you have control over your database schema, consider defining (or altering) your foreign key constraints to be DEFERRABLE INITIALLY DEFERRED. This database setting causes the database to defer checking the constraint until transaction commit time. When this is done, the application can perform DML operations in any order, provided that by COMMIT time all appropriate related rows have been saved and would alleviate the parent/child ordering. However, you would still need to write the code to cascade-update the foreign key values if the parent's primary key is assigned from a sequence, as described in Associations Based on DBSequence-Valued Primary Keys, and Refreshing References to DBSequence-Assigned Foreign Keys.

4.14.7.3.3 Associations Based on DBSequence-Valued Primary Keys

Recall from How to Get Trigger-Assigned Primary Key Values from a Database Sequence, that when an entity object's primary key attribute is of DBSequence type, during the transaction in which it is created, its numerical value is a unique, temporary negative number. If you create a number of associated entities in the same transaction, the relationships between them are based on this temporary negative key value. When the entity objects with DBSequence-value primary keys are posted, their primary key is refreshed to reflect the correct database-assigned sequence number, leaving the associated entities that are still holding onto the temporary negative foreign key value "orphaned."

For entity objects based on a composition, when the parent entity object's DBSequence-valued primary key is refreshed, the composed children entity rows automatically have their temporary negative foreign key value updated to reflect the owning parent's refreshed, database-assigned primary key. This means that for composed entities, the "orphaning" problem does not occur.

However, when entity objects are related by an association that is not a composition, you need to write a little code to insure that related entity rows referencing the temporary negative number get updated to have the refreshed, database-assigned primary key value. The next section outlines the code required.

4.14.7.3.4 Refreshing References to DBSequence-Assigned Foreign Keys

When an entity like DeptEO in this example has a DBSequence-valued primary key, and it is referenced as a foreign key by other entities that are associated with (but not composed by) it, you need to override the postChanges() method as shown in the sample code below to save a reference to the row set of entity rows that might be referencing this new DeptEO row.

If the status of the current DeptEO row is New, then the code assigns the RowSet-valued return of the getEmp() association accessor to the newEmployeesBeforePost member field before calling super.postChanges().

This saved RowSet object is then used by the overridden refreshFKInNewContainees() method shown in the following example. It gets called to allow a new entity row to cascade-update its refreshed primary key value to any other entity rows that were referencing it before the call to postChanges(). It iterates over the EmpEOImpl rows in the newEmployeesBeforePost row set (if non-null) and sets the new department ID value of each one to the new sequence-assigned supplier value of the newly posted DeptEO entity.

// In DeptEOImpl.java
protected void refreshFKInNewContainees() {
   if (newEmployeesBeforePost != null) {
      Number newDeptId = getId().getSequenceNumber();
      /* 
       * Process the rowset of employees that referenced
       * the new department prior to posting, and update their
       * Id attribute to reflect the refreshed Id value
       * that was assigned by a database sequence during posting.
       */
      while (newEmployeesBeforePost.hasNext()){
         EmpEOImpl emp =
            (EmpEOImpl)newEmployeesBeforePost.next();
         emp.setDeptId(newDeptId);
      }
      closeNewProductRowSet();
   }  
}

After implementing this change, the sample in Observing the Post Ordering Problem First Hand runs without encountering any database constraint violations.

// In DeptEOImpl.java
RowSet newEmployeesBeforePost = null;
public void postChanges(TransactionEvent transactionEvent) {
   /* Only bother to update references if Department is a NEW one */
   if (getPostState() == STATUS_NEW) {
   /*
    * Get a rowset of employees related
    * to this new department before calling super
    */
   newEmployeesBeforePost = (RowSet)getEmpEO();
   }
   super.postChanges(transactionEvent);
}

4.14.8 Advanced Entity Association Techniques

This section describes several advanced techniques for working with associations between entity objects.

4.14.8.1 Modifying Association SQL Clause to Implement Complex Associations

When you need to represent a more complex relationship between entities than one based only on the equality of matching attributes, you can modify the association's SQL clause to include more complex criteria. For example, sometimes the relationship between two entities depends on effective dates. A Product may be related to a Supplier, but if the name of the supplier changes over time, each row in the SUPPLIERS table might include additional EFFECTIVE_FROM and EFFECTIVE_UNTIL columns that track the range of dates in which that product row is (or was) in use. The relationship between a Product and the Supplier with which it is associated might then be described by a combination of the matching SupplierId attributes and a condition that the product's RequestDate lie between the supplier's EffectiveFrom and EffectiveUntil dates.

You can set up this more complex relationship in the overview editor for the association. First, add any additional necessary attribute pairs on the Relationship page, which in this example would include one (EffectiveFrom, RequestDate) pair and one (EffectiveUntil, RequestDate) pair. Then, on the Query page you can edit the Where field to change the WHERE clause to be:

(:Bind_SupplierId = Product.SUPPLIER_ID) AND
(Product.REQUEST_DATE BETWEEN :Bind_EffectiveFrom
                                 AND :Bind_EffectiveUntil)

For more information about creating associations, see Creating and Configuring Associations.

4.14.8.2 Exposing View Link Accessor Attributes at the Entity Level

When you create a view link between two entity-based view objects, on the View Link Properties page, you have the option to expose view link accessor attributes both at the view object level as well as at the entity object level. By default, a view link accessor is exposed only at the view object level of the destination view object. By selecting the appropriate In Entity Object: SourceEntityName or In Entity Object:DestinationEntityName checkbox, you can opt to have JDeveloper include a view link attribute in either or both of the source or destination entity objects. This can provide a handy way for an entity object to access a set of related view rows, especially when the query to produce the rows depends only on attributes of the current row.

4.14.9 What You May Need to Know About Custom Entity Object Methods

Custom methods that you implement in the entity object class must not be dependent on the return type of an application module. At runtime, in specific cases, methods that execute with such a dependency may throw a ClassCastException because the returned application module does not match the expected type. It is therefore recommended that custom methods that you implement should not have code to get a specific application module implementation or view object implementation as shown below.

((MyAM)getTransaction().getRootApplicationModule()).getMyVO

Specifically, the above code fails with a ClassCastException in the following scenarios:

  • When your code uses the entity object in the context of a different view object, other that the view object that you reference in the method. Because your application may map more than one view object definition to the same same entity object, the ADF Business Components runtime does not guarantee that methods with dependencies on the view object will execute consistently.

  • When you manually nest an application module under a root application module. In this case, the nested application modules share the same Transaction object and there is no guarantee that the expected application module type is returned with the above code.

  • When the ADF Business Components framework implementation changes with releases. For example, in previous releases, the framework created an internal root application module in order to control declarative transactions that the application defined using ADF task flows.

4.15 Working Programmatically with Custom Data Sources and the Framework Base Class ProgrammaticEntityImpl

ADF Business Components supports programmatic entity objects that you can create to interact with a custom data source.

The framework base class oracle.jbo.server.ProgrammaticEntityImpl lets you take control of performing create/delete/update/lock/rollback operations for each row in a custom data source when you generate an EntityObjectImpl class for the entity object in your ADF Model project. At different phases of the entity populate, commit, or rollback lifecycle, the framework will call these hook points and the framework base implementation class gives the application developer control to interact with the custom data source and perform these operations.

Note:

Prior to the ProgrammaticEntityImpl base class, applications extended from the base class oracle.jbo.server.EntityImpl and overrode a number of lifecycle methods for custom behavior. Using the programmatic entity base class means integration with a custom data source is accomplished by the application developer without needing to understand the entire entity lifecycle.

To work with programmatic entity objects in the ADF Model project, you select the Programmatic option in the Create Entity Object wizard. Then entity object Java classes that you generate in the wizard will by default extend from the framework base class oracle.jbo.server.ProgrammaticEntityImpl instead of extending from the classic style framework class oracle.jbo.server.EntityImpl.

Note:

In order to ensure this behavior when creating an entity object, you must de-select the feature in the ADF Business Components Preferences dialog that enables extending from the base class oracle.jbo.server.EntityImpl. The option in the ADF Business Components-View Objects page of the Tools-Preferences dialog is Classic Programmatic View (de-select to enable extending from ProgrammaticEntityImpl).

4.15.1 How to Create an Entity Object Class Extending ProgrammaticEntityImpl

To create a programmatic entity object that extends oracle.jbo.server.ProgrammaticEntityImpl, you use the Create Entity Object wizard and select the Programmatic data source option.

Before you begin:

You must disable classic style programmatic entity object generation. In the Tools-Preferences dialog, de-select the option Classic Programmatic View on the ADF Business Components-View Objects page. If you leave this option selected (default), JDeveloper will generate entity object Java classes that extend oracle.jbo.server.EntityImpl. For details about classic mode, see Using Classic Style Programmatic View Objects for Alternative Data Sources.

To create programmatic entity objects extending ProgrammaticEntityImpl:

  1. In the Applications window, 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.

  3. In the Create Entity Object wizard, in the Name page, provide a name and package for the entity object. For the data source, select Programmatic.

  4. In the Attributes page, click New one or more times to define the entity object attributes your programmatic entity object requires.

  5. In the Attribute Settings page, adjust any setting you may need to for the attributes you defined.

  6. In the Java page, select Generate Entity Object Class to enable a custom entity object class (ProgrammaticEntityImpl) to contain your code.

  7. Click Finish to create the entity object.

    In your entity object’s custom Java class, override the methods described in Key Framework Methods to Override for ProgrammaticEntityImpl Based Entity Objects to implement your custom data retrieval strategy.

4.15.2 Key Framework Methods to Override for ProgrammaticEntityImpl Based Entity Objects

The generated EntityObjectImpl.java that you generate for a programmatic entity object has the following methods that you must override.

  • postDataProvider(int operationType , ArrayList retAttrNames)

    This method will be called during doDML execution. By default, the framework throws an Exception from the base implementation. Possible values of operationType argument are EntityImpl.DML_INSERT ,EntityImpl.DML_UPDATE or EntityImpl.DML_DELETE. Based on the operation type, applications should insert/update/delete the row from custom data source (or secondary cache, if maintaining). The retAttrNames argument contains the list of attribute names whose value the application should return back after row has been updated/inserted.

  • getRowFromDataSource(HashMap[String , Object] origPrimaryKeyMap)

    Applications should override this API to return the latest row data from custom data source. During entity fault-in, commit or re-populate, the framework needs to know the current row data to handle a multiple user access use case. The origPrimaryKeyMap argument will contain the original primary key information, even if the application has modified any of the key attributes.

  • lockDataProvider(boolean lock)

    Applications should override this API to lock/unlock the corresponding row in custom data source. Similar to the database behavior, the framework will try to ensure the locking of custom row DataProvider to avoid multiple users committing simultaneously. In case the row is already locked, the application should throw oracle.jbo.AlreadyLockedException exception.

  • rollbackDataProvider()

    If custom DataSource is also maintaining intermediate caching, applications can override this API to perform a rollback. The framework will invoke this API if any Exception occurs during the commit/postChanges phase.

  • commitDataProvider(ProgrammaticEntityImpl.CommitActionType commitActionType)

    If custom DataSource is also maintaining intermediate caching, applications should merge the cache with original data by overriding this API. Possible values for commitActionType argument are:

    • CommitActionType.DELETE - Delete the row from cache and original custom DS

    • CommitActionType.MERGE - Merge the row from cache to original custom DS

    • CommitActionType.IGNORE - Ignore this cached row and perform NO action for original row

Other Useful Protected APIs

  • getPrimaryKeyMap(boolean orig)

    Based on orig argument, this API returns the values map of the Primary key. If orig is TRUE, it will return the primary key values as read from the data source, else the current primary key values.

  • getChangedAttributeMap()

    This API return the HashMap of all the changed attributes.

4.16 Creating Custom, Validated Data Types Using Domains

Encapsulate common validation logic in custom data types, called as ADF Domains, which simplifies application maintenance and puts validation in a single place.

When you find yourself repeating the same sanity-checking validations on the values of similar attributes across multiple entity objects, you can save yourself time and effort by creating your own data types that encapsulate this validation. For example, imagine that across your business domain layer there are numerous entity object attributes that store strings that represent email addresses. One technique you could use to ensure that end users always enter a valid email address everywhere one appears in your business domain layer is to:

  • Use a basic String data type for each of these attributes

  • Add an attribute-level method validator with Java code that ensures that the String value has the format of a valid email address for each attribute

However, these approaches can become tedious quickly in a large application. Fortunately, ADF Business Components offers an alternative that allows you to create your own EmailAddress data type that represents an email address. After centralizing all of the sanity-checking regarding email address values into this new custom data type, you can use the EmailAddress as the type of every attribute in your application that represents an email address. By doing this, you make the intention of the attribute values more clear to other developers and simplify application maintenance by putting the validation in a single place. ADF Business Components calls these developer-created data types domains.

Domains are Java classes that extend the basic data types like String, Number, and Date to add constructor-time validation to ensure the candidate value passes relevant sanity checks. They offer you a way to define custom data types with cross-cutting behavior such as basic data type validation, formatting, and custom metadata properties in a way that is inherited by any entity objects or view objects that use the domain as the Java type of any of their attributes.

Note:

The example in this section refers to the oracle.summit.model.domains package in the SummitADF_Examples application workspace.

4.16.1 How to Create a Domain

To create a domain, use the Create Domain wizard. This wizard is available from the New Gallery in the ADF Business Components category.

Before you begin:

It may be helpful to have an understanding of using domains. For more information, see Creating Custom, Validated Data Types Using Domains.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a domain:

  1. In the Applications window, right-click the project for which you want to create a domain and choose New > From Gallery.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Domain, and click OK.
  3. In the Create Domain wizard, on the Name page, specify a name for the domain and a package in which it will reside. To create a domain based on a simple Java type, leave Domain for an Oracle Object Type unselected.
  4. Click Next.
  5. On the Settings page, indicate the base type for the domain and the database column type to which it will map.

    For example, if you were creating a domain called ShortEmailAddress to hold eight-character short email addresses, you would set the base type to String and the Database Column Type to VARCHAR2(8). You can set other common attribute settings on this panel as well.

  6. Click Finish.

4.16.2 What Happens When You Create a Domain

When you create a domain, JDeveloper creates its XML document in the subdirectory of your project's source path that corresponds to the package name you chose. For example, if you created the ShortEmailAddress domain in the summit.model.domains package, JDeveloper would create the ShortEmailAddress.xml file in the ./summit/model/domains subdirectory. A domain always has a corresponding Java class, which JDeveloper creates in the common subpackage of the package where the domain resides. This means it would create the ShortEmailAddress.java class in the summit.model.domains.common package. The domain's Java class is generated with the appropriate code to behave in a way that is identical to one of the built-in data types.

4.16.3 What You May Need to Know About Domains

Domains can be created as a variety of different types, and have different characteristics than standard attributes. The sections that follow describe some of the things you may need to know about when working with domains.

4.16.3.1 Domains as Entity and View Object Attributes

After you've created a domain in a project, it appears among the list of available data types in the Attribute Type dropdown list in the entity object and view object wizards and dialogs. To use the domain as the type of a given attribute, just pick it from the list.

Note:

The entity-mapped attributes in an entity-based view object inherit their data type from their corresponding underlying entity object attribute. Therefore, if the entity attribute uses a domain type, the matching view object attribute will as well. For transient or SQL-derived view object attributes, you can directly set the type to use a domain since it is not inherited from any underlying entity.

4.16.3.2 DataCreationException in Custom validate() Method

Typically, the only coding task you need to do for a domain is to write custom code inside the generated validate() method. Your implementation of the validate() method should perform your sanity checks on the candidate value being constructed, and throw a DataCreationException in the oracle.jbo package if the validation fails.

In order to throw an exception message that is translatable, you can create a message bundle class similar to the one shown in the following example. Create it in the same package as your domain classes themselves. The message bundle returns an array of {MessageKeyString,TranslatableMessageString} pairs.

Note:

There are additional formats available for resource bundles, such as XLIFF. For more information, see Working with Resource Bundles.

package oracle.summit.model.domains;

import java.util.ListResourceBundle;

public class ErrorMessages extends ListResourceBundle {
  public static final String INVALID_SHORTEMAIL = "30002";
  public static final String INVALID_EVENNUMBER = "30003";
  private static final Object[][] sMessageStrings = new String[][] {
      { INVALID_SHORTEMAIL,
        "A valid short email address has no @-sign or dot."},
      { INVALID_EVENNUMBER,
        "Number must be even."}
    };

  /**
   * Return String Identifiers and corresponding Messages
   * in a two-dimensional array.
   */
  protected Object[][] getContents() {
    return sMessageStrings;
  }
}

4.16.3.3 String Domains and String Value Aggregation

Since String is a base JDK type, a domain based on a String aggregates a private mData String member field to hold the value that the domain represents. Then, the class implements the DomainInterface expected by the ADF runtime, as well as the Serializable interface, so the domain can be used in method arguments or return types of the custom client interfaces of Oracle ADF components.

The following example shows the validate() method for a simple ShortEmailAddress domain class. It tests to make sure that the mData value does not contains an at-sign or a dot, and if it does, then the method throws DataCreationException referencing an appropriate message bundle and message key for the translatable error message.

public class ShortEmailAddress implements DomainInterface, Serializable {
  private String mData;
  // . . .
  protected void validate() {
  /**Implements domain validation logic and throws a JboException on error. */
    int atpos = mData.indexOf('@');
    int dotpos = mData.lastIndexOf('.');
    if (atpos > -1 || dotpos > -1) {
      throw new DataCreationException(ErrorMessages.class,
      ErrorMessages.INVALID_SHORTEMAIL,null,null);
    }
  }
  // . . .
}

4.16.3.4 Simple Domains and Built-In Types

Other simple domains based on a built-in type in the oracle.jbo.domain package extend the base type, as shown in the following example. It illustrates the validate() method for a simple Number-based domain called EvenNumber that represents even numbers.

public class EvenNumber extends Number {
  // . . .
  /**
   * Validates that value is an even number, otherwise
   * throws a DataCreationException with a custom
   * error message.
   */
  protected void validate() {
    if (getValue() % 2 == 1) {
      throw new DataCreationException(ErrorMessages.class,
      ErrorMessages.INVALID_EVENNUMBER,null,null);
    }
  }
  // . . .
}

4.16.3.5 Simple Domains As Immutable Java Classes

When you create a simple domain based on one of the basic data types, it is an immutable class. That means that once you've constructed a new instance of it like this:

ShortEmailAddress email = new ShortEmailAddress("emailaddress1");

You cannot change its value. If you want to reference a different short email address, you just construct another one:

ShortEmailAddress email = new ShortEmailAddress("emailaddress2");

This is not a new concept because it's the same way that String, Number, and Date classes behave, among others.

4.16.3.6 Creating Domains for Oracle Object Types When Useful

Oracle Database supports the ability to create user-defined types in the database. For example, you could create a type called POINT_TYPE using the following DDL statement:

create type point_type as object (
   x_coord number,
   y_coord number
);

If you use user-defined types like POINT_TYPE, you can create domains based on them, or you can reverse-engineer tables containing columns of object type to have JDeveloper create the domain for you. You can use the Create Domain wizard to create Oracle object type domains manually.

To manually create an Oracle object type domain:

  1. In the Applications window, right-click the project for which you want to create a domain and choose New > From Gallery.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Domain, and click OK.
  3. In the Create Domain wizard, on the Name page, select the Domain for an Oracle Object Type checkbox, then select the object type for which you want to create a domain from the Available Types list.
  4. Click Next.
  5. On the Settings page, use the Attribute dropdown list to switch between the multiple domain properties to adjust the settings as appropriate.
  6. Click Finish.

To reverse-engineer an Oracle object type domain:

In addition to manually creating object type domains, when you use the Business Components from Tables wizard and select a table containing columns of an Oracle object type, JDeveloper creates domains for those object types as part of the reverse-engineering process. For example, imagine you created a table like this with a column of type POINT_TYPE:

create table interesting_points(
  id number primary key,
  coordinates point_type,
  description varchar2(20)
);

If you create an entity object for the INTERESTING_POINTS table in the Business Components from Tables wizard, then you get both an InterestingPoints entity object and a PointType domain. The latter is generated, based on the POINT_TYPE object type, because it was required as the data type of the Coordinates attribute of the InterestingPoints entity object.

Unlike simple domains, object type domains are mutable. JDeveloper generates getter and setter methods into the domain class for each of the elements in the object type's structure. After changing any domain properties, when you set that domain as the value of a view object or entity object attribute, it is treated as a single unit. Oracle ADF does not track which domain properties have changed, only that a domain-valued attribute value has changed.

Note:

Domains based on Oracle object types are useful for working programmatically with data whose underlying type is an oracle object type. They can also simplify passing and receiving structure information to stored procedures. However, support for working with object type domains in the ADF binding layer is not complete, so it is not straightforward to use object domain-valued attributes in declaratively databound user interfaces.

4.16.3.7 Domains Packaged in the Common JAR

When you create an ADF Library JAR for your business components, as described in How to Package a Component into an ADF Library JAR, the domain classes and message bundle files in the *.common subdirectories of your project's source path get packaged into the *CSCommon.jar. They are classes that are common to both the middle-tier application server and to an eventual remote client you might need to support.

4.16.3.8 Custom Domain Properties and Attributes in Entity and View Objects

You can define custom metadata properties on a domain. Any entity object or view object attribute based on that domain inherits those custom properties as if they had been defined on the attribute itself. If the entity object or view object attribute defines the same custom property, its setting takes precedence over the value inherited from the domain.

4.16.3.9 Inherited Restrictive Properties of Domains in Entity and View Objects

JDeveloper enforces the declarative settings you impose at the domain definition level: they cannot be made less restrictive for the entity object or view object for an attribute based on the domain type. For example, if you define a domain to have its Updatable property set to While New, then when you use your domain as the Java type of an entity object attribute, you can set Updatable to be Never (more restrictive) but you cannot set it to be Always. Similarly, if you define a domain to be Persistent, you cannot make it transient later. When sensible for your application, set declarative properties for a domain to be as lenient as possible, so you can later make them more restrictive as needed.

4.17 Creating New History Types

Use history types to track time-specific data. JDeveloper provides out-of-the-box ADF history types and also allows you to create custom history types.

History types are used to track data specific to a point in time. JDeveloper ships with a number of history types, but you can also create your own. For information on the standard history types and how to use them, see How to Track Created and Modified Dates Using the History Column.

4.17.1 How to Create New History Types

You are not limited to the history types provided: you can add or remove custom history types using the History Types page in the Preferences dialog, and then write custom Java code to implement the desired behavior. The code to handle custom history types should be written in your application-wide entity base class for reuse.

Figure 4-17 shows a custom type called last update login with type ID of 11. Assume that last_update_login is a foreign key in the FND_LOGINS table.

Figure 4-17 New History Types in the Overview Editor

Description of Figure 4-17 follows
Description of "Figure 4-17 New History Types in the Overview Editor"

Before you begin:

It may be helpful to have an understanding of history types. For more information, see Creating New History Types.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create a custom history type:

  1. From the main menu, choose Tools > Preferences.
  2. In the Preferences dialog, expand ADF Business Components and click History Types.
  3. In the Preferences dialog, on the History Types page, click New.
  4. In the New History Type dialog, enter a string value for the name (spaces are allowed) and a numerical ID.

    The Type Id must be an integer between 11 and 126. The numerical values 0-10 are reserved for internal use. The display string is displayed in the Track Change History dropdown list the next time you use the overview editor. Figure 4-18 shows the new history type in the Preferences dialog.

    Figure 4-18 Custom History Type in Preferences

    Description of Figure 4-18 follows
    Description of "Figure 4-18 Custom History Type in Preferences"
  5. Open the entity object's Java class file (or the extension class on which it is based) and add a definition similar to the following.
    private static final byte LASTUPDATELOGIN_HISTORY_TYPE = 11;
    
  6. Override the getHistoryContextForAttribute(AttributeDefImpl attr) method from the EntityImpl base class with code similar the following.
    @Override
    protected Object getHistoryContextForAttribute(AttributeDefImpl attr) {
        if (attr.getHistoryKind() == LASTUPDATELOGIN_HISTORY_TYPE) {
            // Custom History type logic goes here
        }
        else {
            return super.getHistoryContextForAttribute(attr);
        }
    }
    

4.17.2 How to Remove a History Type

Because they are typically used for auditing values over the life of an application, it is rare that you would want to remove a history type. However, in the event that you need to do so, perform the following tasks:

  1. Remove the history type from the JDeveloper history types list in the Preferences dialog.

  2. Remove any custom code you implemented to support the history type in the getHistoryContextForAttribute method in the entity object's Java class file (or the extension class on which it is based).

  3. Remove all usages of the history type in the entity attribute metadata. Any attribute that you have defined to use this history type must be edited.

Before you begin:

It may be helpful to have an understanding of history types. For more information, see Creating New History Types.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To remove a history type from the JDeveloper history types list:

  1. From the main menu, choose Tools > Preferences.
  2. In the Preferences dialog, expand ADF Business Components and click History Types.
  3. In the Preferences dialog, on the History Types page, select the history type that you want to remove and click Delete.

4.18 Basing an Entity Object on a PL/SQL Package API

The ADF framework allows you to use the PL/SQL API in combination with a database view. It allows client programs to read using the database view and write to the table using the PL/SQL API.

If you have a PL/SQL package that encapsulates insert, update, and delete access to an underlying table, you can override the default DML processing event for the entity object that represents that table to invoke the procedures in your PL/SQL API instead. Often, such PL/SQL packages are used in combination with a companion database view. Client programs read data from the underlying table using the database view, and "write" data back to the table using the procedures in the PL/SQL package.

For example, say you want to create a Product entity object based on such a combination of a view and a package.

Given the S_PRODUCT table in the Summit ADF schema, consider a database view named PRODUCT_V, created using the following DDL statement:

create or replace view product_v
as select id,name,image_id,short_desc from s_product;

In addition, consider the simple PRODUCTS_API package shown below that encapsulates insert, update, and delete access to the underlying S_PRODUCT table.

create or replace PACKAGE PRODUCTS_API as
  procedure insert_product(p_id number,
                           p_name varchar2,
                           p_short_desc varchar2,
                           p_longtext_id number,
                           p_image_id number,
                           p_suggested_whlsl_price number,
                           p_whlsl_units varchar2);
  procedure update_product(p_id number,
                           p_name varchar2,
                           p_short_desc varchar2,
                           p_longtext_id number,
                           p_image_id number,
                           p_suggested_whlsl_price number,
                           p_whlsl_units varchar2);
  procedure delete_product(p_id number);
end products_api;

To create an entity object based on this combination of database view and PL/SQL package, you would perform the following tasks:

  1. Create a view-based entity object, as described in How to Create an Entity Object Based on a View.

  2. Create a base class for the entity object, as described in How to Centralize Details for PL/SQL-Based Entities into a Base Class.

  3. Implement the appropriate stored procedure calls, as described in How to Implement the Stored Procedure Calls for DML Operations.

  4. Handle selecting and locking functionality, if necessary, as described in How to Add Select and Lock Handling.

Note:

The example in these sections refers to the oracle.summit.model.wrapplsql package in the SummitADF_Examples application workspace.

Although the example uses an table-based entity object rather than a view-based entity object, the configuration details are congruent.

4.18.1 How to Create an Entity Object Based on a View

To create an entity object based on a view, you use the Create Entity Object wizard.

Before you begin:

It may be helpful to have an understanding of how entity objects can use the PS/SQL API. For more information, see Basing an Entity Object on a PL/SQL Package API.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You will need to launch the Create Entity Object wizard as described in How to Create Single Entity Objects Using the Create Entity Wizard, and proceed through the wizard with the exceptions noted in the following procedure.

To create an entity object based on a view:

  1. On the Name page, give the entity a name.
  2. In the Select Schema Object dialog, in the Object Type section, select the Views checkbox.

    This enables the display of the available database views in the current schema in when you click Query.

  3. In the Available Objects list, select the desired database view.
  4. On the Attribute Settings page, use the Select Attribute dropdown list to choose the attribute that will act as the primary key, and then select the Primary Key checkbox for that attribute.

    Note:

    When defining the entity based on a view, JDeveloper cannot automatically determine the primary key attribute since database views do not have related constraints in the database data dictionary.

4.18.2 What Happens When You Create an Entity Object Based on a View

By default, an entity object based on a view performs all of the following directly against the underlying database view:

  • SELECT statement (for findByPrimaryKey())

  • SELECT FOR UPDATE statement (for lock()), and

  • INSERT, UPDATE, DELETE statements (for doDML())

To use stored procedure calls, you will need to override the doDML() operations (as described in How to Centralize Details for PL/SQL-Based Entities into a Base Class), and possibly override the lock() and findByPrimaryKey() handling (as described in How to Implement the Stored Procedure Calls for DML Operations).

4.18.3 How to Centralize Details for PL/SQL-Based Entities into a Base Class

If you plan to have more than one entity object based on a PL/SQL API, it's a smart idea to abstract the generic details into a base framework extension class. In doing this, you'll be using several of the concepts described in Extending Business Components Functionality. Start by creating a PLSQLEntityImpl class that extends the base EntityImpl class that each one of your PL/SQL-based entities can use as their base class.

Note:

If you are already using an extended entity implementation class for your entity, you can extend it further with the PLSQLEntityImpl class. For example, if you have a framework extension class named zzEntityImpl, you would create a PLSQLEntityImpl class that extends the zzEntityImpl class.

As shown below, you'll override the doDML() method of the base class to invoke a different helper method based on the operation.

// In PLSQLEntityImpl.java
protected void doDML(int operation, TransactionEvent e) {
  // super.doDML(operation, e);
  if (operation == DML_INSERT)
    callInsertProcedure(e);
  else if (operation == DML_UPDATE)
    callUpdateProcedure(e);
  else if (operation == DML_DELETE)
    callDeleteProcedure(e);
}

In the PLSQLEntityImpl.java base class, you can write the helper methods so that they perform the default processing like this:

// In PLSQLEntityImpl.java
/* Override in a subclass to perform nondefault processing */
protected void callInsertProcedure(TransactionEvent e) {
  super.doDML(DML_INSERT, e);
}
/* Override in a subclass to perform nondefault processing */  
protected void callUpdateProcedure(TransactionEvent e) {
  super.doDML(DML_UPDATE, e);
}
/* Override in a subclass to perform nondefault processing */  
protected void callDeleteProcedure(TransactionEvent e) {
  super.doDML(DML_DELETE, e);
}

After putting this infrastructure in place, when you base an entity object on the PLSQLEntityImpl class, you can use the Source > Override Methods menu item to override the callInsertProcedure(), callUpdateProcedure(), and callDeleteProcedure() helper methods and perform the appropriate stored procedure calls for that particular entity.

Note:

If you do not override these helper methods in a subclass, they will perform the default processing as defined in the superclass. You only need to override the operations in the doDML() method that you want to provide alternative processing for.

To simplify the task of implementing these calls, you could add the callStoredProcedure() helper method (described in Invoking Stored Procedures and Functions) to the PLSQLEntityImpl class as well. This way, any PL/SQL-based entity objects that extend this class can leverage the helper method.

4.18.4 How to Implement the Stored Procedure Calls for DML Operations

To implement the stored procedure calls for DML operations, you will need to create a custom Java class for the entity object and override the operations in it.

Before you begin:

It may be helpful to have an understanding of entity objects that are based on a PL/SQL package API. For more information, see Basing an Entity Object on a PL/SQL Package API.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To create the custom Java class with the override methods:

  1. In the Applications window, double-click the entity object.
  2. In the overview editor, click the Java navigation tab, and then click the Edit Java options icon.
  3. In the Select Java Options dialog, click Classes Extend.
  4. In the Override Base Classes dialog, in the Row field, enter the package and class of the PLSQLEntityImpl class, or click Browse to search and select it.
  5. Select Generate Entity Object Class, and click OK.
  6. In the Applications window, double-click the generated entity object class.
  7. From the main menu, choose Source > Override Methods.
  8. In the Override Methods dialog, select the callInsertProcedure(), callUpdateProcedure(), and callDeleteProcedure() methods, and click OK.
  9. In the source editor, enter the necessary code to override these procedures.

The following example shows some sample code that you would write in these overridden helper methods to invoke insert, update, and delete procedures.

// In ProductImpl.java
protected void callInsertProcedure(TransactionEvent e) {
   callStoredProcedure("products_api.insert_product(?,?,?,?,?,?,?)",
      new Object[] { getId(), getName(), getShortDesc(), getLongtextId(), 
            getImageId(), getSuggestedWhlslPrice(), getWhlslUnits() });
}
protected void callUpdateProcedure(TransactionEvent e) {
   callStoredProcedure("products_api.update_product(?,?,?,?,?,?)",
      new Object[] { getId(), getName(), getShortDesc(), getLongtextId(), 
            getImageId(), getSuggestedWhlslPrice(), getWhlslUnits() });
}
protected void callDeleteProcedure(TransactionEvent e) {
   callStoredProcedure("products_api.delete_product(?)",
      new Object[] { getId() });
}

At this point, if you create a default entity-based view object called ProductView for the Product entity object and add an instance of it to a AppModule application module you can quickly test inserting, updating, and deleting rows from the ProductView view object instance in the Oracle ADF Model Tester.

Often, overriding just the insert, update, and delete operations will be enough. The default behavior that performs the SELECT statement for findByPrimaryKey() and the SELECT FOR UPDATE statement for the lock() against the database view works for most basic kinds of views.

However, if the view is complex and does not support SELECT FOR UPDATE or if you need to perform the findByPrimaryKey() and lock() functionality using additional stored procedures APIs, then you can use the technique described in How to Add Select and Lock Handling.

4.18.5 How to Add Select and Lock Handling

You can handle the lock() and findByPrimaryKey() functionality of an entity object by invoking stored procedures if necessary. Imagine that the PRODUCTS_API package were updated to contain the two additional procedures shown here.

/* Added to PRODUCTS_API package */
procedure lock_product(p_id number,
                       p_name OUT varchar2,
                       p_short_desc OUT varchar2,
                       p_longtext_id OUT number,
                       p_image_id OUT number,
                       p_suggested_whlsl_price OUT number,
                       p_whlsl_units OUT varchar2);
procedure select_product(p_id number,
                       p_name OUT varchar2,
                       p_short_desc OUT varchar2,
                       p_longtext_id OUT number,
                       p_image_id OUT number,
                       p_suggested_whlsl_price OUT number,
                       p_whlsl_units OUT varchar2);

Both the lock_product and select_product procedures accept a primary key attribute as an IN parameter and return values for the remaining attributes using OUT parameters.

To add select and lock handling, you will need to perform the following tasks:

  1. Update the base class to handle lock and select, as described in Updating PLSQLEntityImpl Base Class to Handle Lock and Select.

  2. Update the entity object implementation class to implement the lock and select behaviors, as described in Implementing Lock and Select for the Product Entity.

  3. Override the lock() method in the entity object implementation class to refresh the entity object after a RowInconsistentException has occurred, as described in Refreshing the Entity Object After RowInconsistentException.

4.18.5.1 Updating PLSQLEntityImpl Base Class to Handle Lock and Select

You can extend the PLSQLEntityImpl base class to handle the lock() and findByPrimaryKey() overrides using helper methods similar to the ones you added for insert, update, delete. At runtime, both the lock() and findByPrimaryKey() operations invoke the lower-level entity object method called doSelect(boolean lock). The lock() operation calls doSelect() with a true value for the parameter, while the findByPrimaryKey() operation calls it passing false instead.

The following example shows the overridden doSelect() method in PLSQLEntityImpl to delegate as appropriate to two helper methods that subclasses can override as necessary.

// In PLSQLEntityImpl.java
protected void doSelect(boolean lock) {
  if (lock) {
    callLockProcedureAndCheckForRowInconsistency();
  } else {
    callSelectProcedure();
  }
}

The two helper methods are written to just perform the default functionality in the base PLSQLEntityImpl class:

// In PLSQLEntityImpl.java
/* Override in a subclass to perform nondefault processing */  
protected void callLockProcedureAndCheckForRowInconsistency() {
  super.doSelect(true);
}
/* Override in a subclass to perform nondefault processing */  
protected void callSelectProcedure() {
  super.doSelect(false);
}

Notice that the helper method that performs locking has the name callLockProcedureAndCheckForRowInconsistency(). This reminds you that you need to perform a check to detect at the time of locking the row whether the newly selected row values are the same as the ones the entity object in the entity cache believes are the current database values.

To assist subclasses in performing this old-value versus new-value attribute comparison, you can add one final helper method to the PLSQLEntityImpl class like this:

// In PLSQLEntityImpl
protected void compareOldAttrTo(int attrIndex, Object newVal) {
  if ((getPostedAttribute(attrIndex) == null && newVal != null) || 
      (getPostedAttribute(attrIndex) != null && newVal == null) || 
      (getPostedAttribute(attrIndex) != null && newVal != null && 
       !getPostedAttribute(attrIndex).equals(newVal))) {
    throw new RowInconsistentException(getKey());
  }
}

4.18.5.2 Implementing Lock and Select for the Product Entity

With the additional infrastructure in place in the base PLSQLEntityImpl class, you can override the callSelectProcedure() and callLockProcedureAndCheckForRowInconsistency() helper methods in the entity object implementation class (for example, ProductImpl). Because the select_product and lock_product procedures have OUT arguments, as described in How to Call Other Types of Stored Procedures, you need to use a JDBC CallableStatement object to perform these invocations.

The example below shows the code you would use to invoke the select_product procedure for the ProductImpl entity object implementation class to select a row by primary key. It's performing the following basic steps:

  1. Creating a CallableStatement for the PLSQL block to invoke.
  2. Registering the OUT parameters and types, by one-based bind variable position.
  3. Setting the IN parameter value.
  4. Executing the statement.
  5. Retrieving the possibly updated column values.
  6. Populating the possibly updated attribute values in the row.
  7. Closing the statement.
// In ProductImpl.java
protected void callSelectProcedure() {
   String stmt = "begin products_api.select_product(?,?,?,?,?,?,?);end;";
   // 1. Create a CallableStatement for the PLSQL block to invoke
   CallableStatement st = getDBTransaction().createCallableStatement(stmt, 0);
   try {
      // 2. Register the OUT parameters and types
      st.registerOutParameter(2, VARCHAR2);
      st.registerOutParameter(3, VARCHAR2);
      st.registerOutParameter(4, NUMBER);
      st.registerOutParameter(5, NUMBER);
      st.registerOutParameter(6, NUMBER);
      st.registerOutParameter(7, VARCHAR2);
      // 3. Set the IN parameter value
      st.setObject(1, getId());
      // 4. Execute the statement
      st.executeUpdate();
      // 5. Retrieve the possibly updated column values
      String possiblyUpdatedName = st.getString(2);
      String possiblyUpdatedShortDesc = st.getString(3);
      String possiblyUpdatedLongTextId = st.getString(4);
      String possiblyUpdatedImageId = st.getString(5);
      String possiblyUpdatedSuggestedPrice = st.getString(6);
      String possiblyUpdatedWhlslUnits = st.getString(7);
      // 6. Populate the possibly updated attribute values in the row
      populateAttribute(NAME, possiblyUpdatedName, true, false, 
                        false);
      populateAttribute(SHORTDESC, possiblyUpdatedShortDesc, true, 
                        false, false);
      populateAttribute(LONGTEXTID, possiblyUpdatedLongTextId, true, 
                        false, false);
      populateAttribute(IMAGEID, possiblyUpdatedImageId, true, false, 
                        false);
      populateAttribute(SUGGESTEDWHLSLPRICE, possiblyUpdatedSuggestedPrice, 
                        true, false, false);
      populateAttribute(WHLSLUNITS, possiblyUpdatedWhlslUnits, true, 
                        false, false);
   } catch (SQLException e) {
      throw new JboException(e);
   } finally {
      if (st != null) {
         try {
            // 7. Closing the statement
            st.close();
         } catch (SQLException e) {
         }
      }
   }
}

The following example shows the code to invoke the lock_product procedure. It's doing basically the same steps as those above, with just the following two interesting differences:

  • After retrieving the possibly updated column values from the OUT parameters, it uses the compareOldAttrTo() helper method inherited from the PLSQLEntityImpl to detect whether or not a RowInconsistentException should be thrown as a result of the row lock attempt.

  • In the catch (SQLException e) block, it is testing to see whether the database has thrown the error:

    ORA-00054: resource busy and acquire with NOWAIT specified
    

    and if so, it again throws the ADF Business Components AlreadyLockedException just as the default entity object implementation of the lock() functionality would do in this situation.

With these methods in place, you have and entity object that wraps the PL/SQL package (in this case, a Product entity object with the PRODUCTS_API package) for all of its database operations. Due to the clean separation of the data querying functionality of view objects and the data validation and saving functionality of entity objects, you can now leverage this entity object in any way you would use a normal entity object. You can build as many different view objects as necessary that use this entity object as their entity usage.

// In ProductsImpl.java
protected void callLockProcedureAndCheckForRowInconsistency() {
   String stmt = "begin products_api.lock_product(?,?,?,?,?,?,?);end;";
   CallableStatement st = 
      getDBTransaction().createCallableStatement(stmt, 0);
   try {
      st.registerOutParameter(2, VARCHAR2);
      st.registerOutParameter(3, VARCHAR2);
      st.registerOutParameter(4, NUMBER);
      st.registerOutParameter(5, NUMBER);
      st.registerOutParameter(6, NUMBER);
      st.registerOutParameter(7, VARCHAR2);
      st.setObject(1, getId());
      st.executeUpdate();
      String possiblyUpdatedName = st.getString(2);
      String possiblyUpdatedShortDesc = st.getString(3);
      String possiblyUpdatedLongTextId = st.getString(4);
      String possiblyUpdatedImageId = st.getString(5);
      String possiblyUpdatedSuggestedPrice = st.getString(6);
      String possiblyUpdatedWlhslUnits = st.getString(7);
      compareOldAttrTo(NAME, possiblyUpdatedName);
      compareOldAttrTo(SHORTDESC, possiblyUpdatedShortDesc);
      compareOldAttrTo(LONGTEXTID, possiblyUpdatedLongTextId);
      compareOldAttrTo(IMAGEID, possiblyUpdatedImageId);
      compareOldAttrTo(SUGGESTEDWHLSLPRICE, possiblyUpdatedSuggestedPrice);
      compareOldAttrTo(WHLSLUNITS, possiblyUpdatedWlhslUnits);
   } catch (SQLException e) {
      if (Math.abs(e.getErrorCode()) == 54) {
         throw new AlreadyLockedException(e);
      } else {
         throw new JboException(e);
      }
   } finally {
      if (st != null) {
         try {
            st.close();
         } catch (SQLException e) {
         }
      }
   }
}

4.18.5.3 Refreshing the Entity Object After RowInconsistentException

You can override the lock() method to refresh the entity object after a RowInconsistentException has occurred. The following example shows code that can be added to the entity object implementation class to catch the RowInconsistentException and refresh the entity object.

// In the entity object implementation class 
@Override
public void lock() { 
  try { 
    super.lock(); 
  } 
  catch (RowInconsistentException ex) { 
    this.refresh(REFRESH_UNDO_CHANGES); 
    throw ex; 
  } 
}

4.19 Basing an Entity Object on a Join View or Remote DBLink

Some types of schema do not support the RETURNING clause. So disable this clause in the ADF entity object. The entity object then implements Refresh on Insertand Refresh on Update.

If you need to create an entity object based on either of the following:

  • Synonym that resolves to a remote table over a DBLINK

  • View with INSTEAD OF triggers

Then you will encounter the following error if any of its attributes are marked as Refresh on Insert or Refresh on Update:

JBO-26041: Failed to post data to database during "Update"
## Detail 0 ##
ORA-22816: unsupported feature with RETURNING clause

These types of schema objects do not support the RETURNING clause, which by default the entity object uses to more efficiently return the refreshed values in the same database roundtrip in which the INSERT or UPDATE operation was executed.

4.19.1 How to Disable the Use of the RETURNING Clause

Because some types of schema objects do not support the RETURNING clause, you might need to disable the RETURNING clause in your entity object. The following procedures explains how to do that.

Before you begin:

It may be helpful to have an understanding of the types of schema objects don't support the RETURNING clause. For more information, see Basing an Entity Object on a Join View or Remote DBLink.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

To disable the use of the RETURNING clause for an entity object of this type:

  1. Enable a custom entity definition class for the entity object.
  2. In the custom entity definition class, override the createDef() method to call setUseReturningClause(false).
  3. If the Refresh on Insert attribute is the primary key of the entity object, you must specify some other attribute in the entity as an alternate unique key by setting the Unique Key property on it.

4.19.2 What Happens at Runtime: Refresh Behavior With Disabled RETURNING Clause

At runtime, when you have disabled the use of the RETURNING clause as described in How to Disable the Use of the RETURNING Clause,, the entity object implements the Refresh on Insert and Refresh on Update behavior using a separate SELECT statement to retrieve the values to refresh after insert or update as appropriate.

4.20 Using Inheritance in Your Business Domain Layer

ADF Business Components supports inheritance to allow you to create new components by extending existing ones.

Inheritance is a powerful feature of object-oriented development that can simplify development and maintenance when used appropriately. As shown in Creating Extended Components Using Inheritance, ADF Business Components supports using inheritance to create new components that extend existing ones in order to add additional properties or behavior or modify the behavior of the parent component. Inheritance can be useful in modeling the different kinds of entities in your reusable business domain layer.

Note:

The example in this section is an extension of the oracle.summit.model.polymorphic package in the SummitADF_Examples workspace.

4.20.1 Understanding When Inheritance Can Be Useful

Your application's database schema might contain tables where different logical kinds of business information are stored in rows of the same table. These tables will typically have one column whose value determines the kind of information stored in each row. For example, an application's S_CUSTOMER table stores information about both domestic and international customers in the same table. It contains a CUSTOMER_TYPE_CODE column whose value determines what kind of S_CUSTOMER the row represents.

While the Summit ADF sample application implementation doesn't contain this differentiation, it's reasonable to assume that revisions of the application might require:

  • Managing additional database-backed attributes that are specific to domestic customers or specific to international customers

  • Implementing common behavior for all users that is different for domestic or international customers

  • Implementing new functionality that is specific to only domestic or only international customers

Figure 4-19 shows what the business domain layer would look like if you created distinct Customers, Domestics, and Internationals entity objects to allow distinguishing the different kinds of business information in a more formal way inside your application. Since domestic and international are special kinds of customers, their corresponding entity objects would extend the base Customers entity object. This base Customers entity object would contain all of the attributes and methods common to all types of users. The performCustomerFunction() method in the figure represents one of these common methods.

Then, for the Domestics and Internationals entity objects you can add specific additional attributes and methods that are unique to that kind of user. For example, Domestics has an additional State attribute of type String to track the state in which the domestic customer is located. There is also a performDomesticFunction() method that is specific to domestic customers. Similarly, the Internationals entity object has an additional Language attribute to track whether the customer speaks English. The performInternationalFunction() is a method that is specific to international customers.

Figure 4-19 Distinguishing Customers, Domestics, and Internationals Using Inheritance

Description of Figure 4-19 follows
Description of "Figure 4-19 Distinguishing Customers, Domestics, and Internationals Using Inheritance"

By modeling these different kinds of customers as distinct entity objects in an inheritance hierarchy in your domain business layer, you can simplify having them share common data and behavior and implement the aspects of the application that make them distinct.

4.20.2 How to Create Entity Objects in an Inheritance Hierarchy

To create entity objects in an inheritance hierarchy, you use the Create Entity Object wizard to create each entity.

To create entity objects in an inheritance hierarchy, you will perform the following tasks:

  1. Identify the discriminator column and values, as described in Identifying the Discriminator Column and Distinct Values.

  2. Identify the subset of attributes for each entity object, as described in Identifying the Subset of Attributes Relevant to Each Kind of Entity.

  3. Create the base entity object, as described in Creating the Base Entity Object in an Inheritance Hierarchy.

  4. Create the subtype entity objects, as described in Creating a Subtype Entity Object in an Inheritance Hierarchy.

4.20.2.1 Identifying the Discriminator Column and Distinct Values

Before creating entity objects in an inheritance hierarchy based on a table containing different kinds of information, you should first identify which column in the table is used to distinguish which kind of row it is.

For example, in a S_CUSTOMER table, this might be a CUSTOMER_TYPE_CODE column. Since it helps partition or "discriminate" the rows in the table into separate groups, this column is known as the discriminator column.

Next, determine the valid values that the discriminator column takes on in your table. You might know this, or you could execute a simple SQL statement in the JDeveloper SQL Worksheet to determine the answer. To access the worksheet:

  1. With the application open in JDeveloper, choose Window > Database > Databases from the main menu.
  2. Expand the workspace node, and select the connection.
  3. Right-click the database connection, and choose Open SQL Worksheet.

Figure 4-20 shows the results of performing a SELECT DISTINCT query in the SQL Worksheet on the CUSTOMER_TYPE_CODE column in the S_CUSTOMER table. It confirms that the rows are partitioned into two groups based on the CUSTOMER_TYPE_CODE discriminator values: DOMESTIC and INTERNATIONAL.

Figure 4-20 Using the SQL Worksheet to Find Distinct Discriminator Column Values

Description of Figure 4-20 follows
Description of "Figure 4-20 Using the SQL Worksheet to Find Distinct Discriminator Column Values"

4.20.2.2 Identifying the Subset of Attributes Relevant to Each Kind of Entity

Once you know how many different kinds of business entities are stored in the table, you will also know how many entity objects to create to model these distinct items. You'll typically create one entity object per kind of item. Then, to help determine which entity should act as the base of the hierarchy, you need to determine which subset of attributes is relevant to each kind of item.

For example, assume you determine that all of the attributes except State and Language are relevant to all users, and that:

  • State is specific to domestic customers

  • Language is specific to international customers.

This information leads you to determine that the Customers entity object should be the base of the hierarchy, with the Domestics and Internationals entity objects each extending Customers to add their specific attributes.

4.20.2.3 Creating the Base Entity Object in an Inheritance Hierarchy

To create the base entity object in an inheritance hierarchy, you use the Create Entity Object wizard.

Before you begin:

It may be helpful to have an understanding of entity objects in an inheritance hierarchy. For more information, see Using Inheritance in Your Business Domain Layer.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You will also need to determine the discriminator column and values, as described in Identifying the Discriminator Column and Distinct Values, and determine the attributes for each entity object, as described in Identifying the Subset of Attributes Relevant to Each Kind of Entity.

To create the base entity object

  1. In the Applications window, right-click the project to which you want to add the entity object and choose New > From Gallery.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Entity Object, and click OK.
  3. In the Create Entity Object wizard, on the Name Page, provide a name and package for the entity, and select the schema object on which the entity will be based.

    In this example, name the entity object Customers and base it on the S_CUSTOMER table.

  4. On the Attributes page, select the attributes in the Entity Attributes list that are not relevant to the base entity object (if any) and click Remove to remove them.

    In this example, you would remove the State and Language attributes from the list.

  5. On the Attribute Settings page, use the Select Attribute dropdown list to choose the attribute that will act as the discriminator for the family of inherited entity objects and select the Discriminator checkbox to identify it as such. Importantly, you must also supply a Default Value for this discriminator attribute to identify rows of this base entity type.

    In this example, you would select the CustomerTypeCode attribute, mark it as a discriminator attribute, and set its Default Value to the value "CUSTOMER".

    Note:

    Leaving the Default Value blank for a discriminator attribute is legal. A blank default value means that a row with the discriminator column value IS NULL will be treated as this base entity type.

  6. Then click Finish.

4.20.2.4 Creating a Subtype Entity Object in an Inheritance Hierarchy

To create a subtype entity object in an inheritance hierarchy, you use the Create Entity Object wizard.

Before you begin:

It may be helpful to have an understanding of entity objects in an inheritance hierarchy. For more information, see Using Inheritance in Your Business Domain Layer.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You will also need to perform the following tasks:

  1. Determine the discriminator column and values, as described in Identifying the Discriminator Column and Distinct Values.

  2. Determine the attributes for each entity object, as described in Identifying the Subset of Attributes Relevant to Each Kind of Entity.

  3. Create the parent entity object from which your new entity object will extend, as described in Creating the Base Entity Object in an Inheritance Hierarchy.

  4. Make sure that the parent entity has a discriminator attribute already identified.

    If it does not, use the overview editor to set the Discriminator property on the appropriate attribute of the parent entity before creating the inherited child.

To create the new subtype entity object in the hierarchy:

  1. In the Applications window, right-click the project to which you want to add the entity object and choose New > From Gallery.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Entity Object, and click OK.
  3. In the Create Entity Object wizard, on the Name Page, provide a name and package for the entity, and click the Browse button next to the Extends field to select the parent entity from which the entity being created will extend.

    In this example, you would name the new entity Domestics and select the Customers entity object in the Extends field.

  4. On the Attributes page, the Entity Attributes list displays the attributes from the underlying table that are not included in the base entity object. Select the attributes you do not want to include in this entity object and click Remove.

    In this example, because you are creating the Domestics entity you would remove the Language attribute and leave the State attribute.

  5. Click Override to select the discriminator attribute so that you can customize the attribute metadata to supply a distinct Default Value for the Domestics entity subtype.

    In this example, you would override the CustomerTypeCode attribute.

  6. On the Attribute Settings page, use the Select Attribute dropdown list to select the discriminator attribute. Change the Default Value field to supply a distinct default value for the discriminator attribute that defines the entity subtype being created.

    In this example, you would select the CustomerTypeCode attribute and change its Default Value to the value "DOMESTIC".

  7. Click Finish.

Note:

You can repeat the same steps to define the Internationals entity object that extends Customers to add the additional Language attribute and overrides the Default Value of the CustomerTypeCode discriminator attribute to have the value "INTERNATIONAL".

4.20.3 How to Add Methods to Entity Objects in an Inheritance Hierarchy

To add methods to entity objects in an inheritance hierarchy, enable the custom Java class for the entity object and use the source editor to add the method. Methods that are common to all entity objects in the hierarchy are added to the base entity, while subtype-specific methods are added to the subtype. You can also override methods from the base entity object in the subtypes as necessary.

4.20.3.1 Adding Methods Common to All Entity Objects in the Hierarchy

To add a method that is common to all entity objects in the hierarchy, you add the method to the implementation class of the base entity object.

Before you begin:

It may be helpful to have an understanding of entity objects in an inheritance hierarchy. For more information, see Using Inheritance in Your Business Domain Layer.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You will also need to perform the following tasks:

  1. Create the base entity object and subtypes in the hierarchy, as described in How to Create Entity Objects in an Inheritance Hierarchy.

  2. Create a custom Java implementation class for the base entity object, as described in Generating Custom Java Classes for an Entity Object.

To add a method common to all entity objects in a hierarchy:

  1. In the Applications window, double-click the base entity object implementation class (for example, CustomersImpl.java).
  2. In the source editor, add the method.

    For example, you could add the following method to the CustomersImpl class for the base Customers entity object:

    // In CustomersImpl.java
    public void performCustomerFunction() {
      System.out.println("## performCustomerFunction as Customer");
    }
    

Because this is the base entity object class, the methods you implement here are inherited by all subtype entity objects in the hierarchy.

4.20.3.2 Overriding Common Methods in a Subtype Entity Object

To override a method in a subtype entity object that is common to all entity objects in the hierarchy, you modify the common method inherited from the base entity object in the implementation class of the subtype entity object.

Before you begin:

It may be helpful to have an understanding of entity objects in an inheritance hierarchy. For more information, see Using Inheritance in Your Business Domain Layer.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You will also need to perform the following tasks:

  1. Create the base entity object and subtypes in the hierarchy, as described in How to Create Entity Objects in an Inheritance Hierarchy.

  2. Create the common method in the base entity object, which your subtype entity object will override, as described in Adding Methods Common to All Entity Objects in the Hierarchy.

  3. Create a a custom Java implementation class for the subtype entity object, as described in Generating Custom Java Classes for an Entity Object.

To override a method in a subtype entity object:

  1. In the Applications window, double-click the subtype entity object implementation class (for example, DomesticsImpl.java).
  2. With the subtype entity object implementation class open in the source editor, choose Source > Override Methods from the main menu.
  3. In the Override Methods dialog, select the method you want to override (for example, the performCustomerFunction() method), and click OK.
  4. In the source editor, customize the overridden method's implementation.

    For example, you could override the performCustomerFunction() method in the DomesticsImpl class for the Domestics subtype entity object and change the implementation to look like this:

    // In DomesticsImpl.java
    public void performCustomerFunction() {
      System.out.println("## performCustomerFunction as Domestics");
    }
    

When working with instances of entity objects in a subtype hierarchy, sometimes you will process instances of multiple different subtypes. Because the Domestics and Internationals entity objects are special kinds of Customers, you can write code that works with all of them using the more generic CustomersImpl type that they all have in common. When doing this generic kind of processing of classes that might be one of a family of subtypes in a hierarchy, Java will always invoke the most specific override of a method available.

This means that invoking the performCustomerFunction() method on an instance of CustomersImpl that happens to really be the more specific DomesticsImpl subtype, will the result in printing out the following:

## performCustomerFunction as Domestics

instead of the default result that regular CustomersImpl instances would get:

## performCustomerFunction as Customer

4.20.3.3 Adding Methods Specific to a Subtype Entity Object

To add a method that is specific to a subtype entity object in the hierarchy, you simply add the method in the implementation class of the subtype using the source editor.

Before you begin:

It may be helpful to have an understanding of entity objects in an inheritance hierarchy. For more information, see Using Inheritance in Your Business Domain Layer.

You may also find it helpful to understand additional functionality that can be added using other entity object features. For more information, see Additional Functionality for Entity Objects.

You will also need to perform the following tasks:

  1. Create the base entity object and subtypes in the hierarchy, as described in How to Create Entity Objects in an Inheritance Hierarchy.

  2. Create a custom Java implementation class for the subtype entity object, as described in Generating Custom Java Classes for an Entity Object.

To add a method specific to a subtype entity object:

  1. In the Applications window, double-click the subtype entity object implementation class (for example, InternationalsImpl.java).
  2. In the source editor, add the method.

    For example, you could add a performInternationalFunction() method to the InternationalsImpl class for the subtype Internationals entity object:

    // In InternationalsImpl.java
    public void performInternationalFunction() {
      System.out.println("## performInternationalFunction called");    
    }
    

4.20.4 What You May Need to Know About Using Inheritance

When using inheritance, you can also introduce a new base entity, find subtype entities using a primary key, and create view objects with polymorphic entity usages.

4.20.4.1 Introducing a New Base Entity

For example, where the Customers entity object corresponds to a concrete kind of row in the S_CUSTOMERS table, it also plays the role of the base entity in the inheritance hierarchy. In other words, all of its attributes were common to all entity objects in the hierarchy. A situation might arise, however, where the Customers entity object required a property that was specific to customers, but not common to domestic or international customers.

In this case, you can introduce a new entity object (for example, BaseCustomers) to act as the base entity in the hierarchy. It would have all of the attributes common to all Customers, Domestics, and Internationals entity objects. Then each of the three entities that correspond to concrete rows that appear in the table could have some attributes that are inherited from BaseCustomers and some that are specific to the individual subtype. In the BaseCustomers type, so long as you mark the CustomerTypeCode attribute as a discriminator attribute, you can just leave the Default Value blank (or some other value that does not occur in the CUSTOMER_TYPE_CODE column in the table). Because you will not use instances of the BaseCustomers entity in the application, it doesn't matter what its discriminator default value is.

4.20.4.2 Subtype Entity Objects and the findByPrimaryKey() Method

When you use the findByPrimaryKey() method on an entity definition, it only searches the entity cache for the entity object type on which you call it. For example, this means that if you call CustomersImpl.getDefinitionObject() to access the entity definition for the Customers entity object when you call findByPrimaryKey() on it, you will only find entities in the cache that happen to be customers. Sometimes this is exactly the behavior you want.

However, if you want to find an entity object by primary key allowing the possibility that it might be a subtype in an inheritance hierarchy, then you can use the findByPKExtended() method from the EntityDefImpl class instead.

For example, if you have created subtypes of the Customers entity object, this alternative finder method would find an entity object by primary key whether it is a customer, domestic, or international. You can then use the Java instanceof operator to test which type you found, and then cast the CustomersImpl object to the more specific entity object type to work with features specific to that subtype.

4.20.4.3 View Objects with Polymorphic Entity Usages

When you create an entity-based view object with an entity usage corresponding to a base entity object in an inheritance hierarchy, you can configure the view object to query rows corresponding to multiple different subtypes in the base entity's subtype hierarchy. Each row in the view object will use the appropriate subtype entity object as the entity row part, based on matching the value of the discriminator attribute. See How to Create a Subtype View Object with a Polymorphic Entity Usage, for specific instructions on setting up and using these view objects.