Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers
10g Release 3 (10.1.3.0) B25947-01 |
|
![]() Previous |
![]() Next |
The entity object offers numerous declarative features to simplify implementing typical enterprise business applications. Depending on your task, sometimes the declarative facilities alone may satisfy your needs. However, when you need to go beyond the declarative behavior to implement more complex business logic or validation rules for your business domain layer, that is possible as well. This chapter focuses on giving you a solid understanding of the declarative features. In Chapter 9, "Implementing Programmatic Business Rules in Entity Objects", you'll study some of the most typical ways that you extend entity objects with custom code.
To configure the declarative runtime behavior of an entity object, use the Entity Object Editor. You access the editor by selecting an entity in the Application Navigator and choosing Edit from the context menu. Figure 6-13 shows what the editor looks like for the ServiceRequest
entity object.
On the Name page, you can see the entity object's name and configure the database table to which it relates. On the Attributes page, you create or delete the attributes that represent the data relevant to an entity object. By expanding this node, you can access the properties of each of the entity object's attributes.
On the Tuning page, you set options to make database operations more efficient when you create, modify, or delete multiple entities of the same type in a single transaction. On the Publish page, you 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. On the Subscribe page, you enroll your entity object to be notified when selected events of other entity objects fire. On the Authorization page, you define role-based updatability permissions for any or all attributes. And finally, on the Custom Properties page, you can define custom metadata you can access at runtime on the entity.
Note: If your entity has a long list of attribute names, there's a quick way to find the one you're looking for. With the Attributes node in the tree 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. |
All of the declarative settings that describe and control an entity object's runtime behavior are stored in its XML component definition file. When you modify settings of your entity using the editor, pressing OK updates the component's XML definition file and optional custom java files. If you need to immediately apply changes and continue working in the editor, use the Apply button. Applying changes while editing is typically useful only when you enable the generation of a custom Java file for the component for the first time and you want JDeveloper to generate those files before you open another page in the editor.
Since much of the entity object's declarative functionality is related to the settings of its attributes, this section covers the important options shown in Figure 6-13 in detail.
The Persistent property controls whether the attribute value corresponds to a column in the underlying table, or whether it is just a transient value. If the attribute is persistent, the Database Column area lets you change the name of the underlying column that corresponds to the attribute and indicate its column type with precision and scale information (e.g. VARCHAR2(40)
or NUMBER(4,2)
). Based on this information, at runtime the entity object enforces the maximum length and precision/scale of the attribute value, throwing an exception if a value does not meet the requirements.
Both the Business Components from Tables wizard and the Create Entity Object wizard automatically 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. The Attribute Type field allows you to change the Java type of the entity attribute to any type you might need. The Database Column Type field reflects the SQL type of the underlying database column to which the attribute is mapped. The value of the Database Column Name field controls the column to which the attribute is mapped.
Your entity object can handle tables with column types, as listed in Table 6-1. With the exception of the java.lang.String
class, the default Java attribute types are all in the oracle.jbo.domain
and oracle.ord.im
packages and support efficiently working with Oracle database data of the corresponding type. The dropdown list for the Java Type field includes a number of other common types that are also supported.
Table 6-1 Default Entity Object Attribute Type Mappings
Oracle Column Type | Entity Column Type | Entity Java Type |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 thejava.io.Serializable interface.
|
When working with types that support defining a maximum length like VARCHAR2(n)
, the Database Column Type field includes the maximum attribute length as part of the value. So, for example, an attribute based on a VARCHAR2(10)
column in the database will initially reflect the maximum length of 10 characters by showing VARCHAR2(10)
as the Database Column Type. If for some reason you want to restrict the maximum length of the String
-valued attribute to fewer characters than the underlying column will allow, just change the maximum length of the Database Column Type value. For example, the EMAIL
column in the USERS
table is VARCHAR2(50)
, so by default the Email
attribute in the Users
entity object defaults to the same. If you know that the actual email addresses are always 8 characters or less, 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
])
. So, for example, to restrict an attribute based on a NUMBER(7,2)
column in the database to have a precision of 5 and a scale of 1 instead, just update the Database Column Type to be NUMBER(5,1)
.
The Updatable setting controls when the value of a given attribute can be updated. If set to:
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
The Mandatory property controls whether the field is required.
The Primary Key property indicates whether the attribute is part of the key that uniquely identifies the entity. Typically, you will 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(s) of the primary key attribute(s) 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, the ServiceHistory
entity object has multiple primary key attributes SvrId
and LineNo
. On the Attributes page of the Entity Object Editor, SvrId
is first, and LineNo
is second; an array of values encapsulated by the Key
object for a entity row of type ServiceHistory
will have these two attribute values in exactly this order. The reason why it is crucial to understand this point is that 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.
The Default field specifies a static default value for the attribute. For example, you might 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
.
If you know that the underlying column value will be updated by a database trigger during insert or update operations, you can check the respective Refresh After Insert or Refresh After Update checkboxes to have the framework automatically retrieve the modified value to keep the entity object and database row in sync. The entity object uses 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 round-trip.
Note: If you create an entity object for a synonym that resolves to a remote table over a DBLINK, use of this feature will give an error at runtime like:JBO-26041: Failed to post data to database during "Update" ## Detail 0 ## ORA-22816: unsupported feature with RETURNING clause Section 26.5, "Basing an Entity Object on a Join View or Remote DBLink" describes a technique to circumvent this database limitation. |
One common case where Refresh After Insert comes into play is a primary key attribute whose 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 similar to this:
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;
Set the Attribute Type to the built-in datatype named DBSequence
, as shown in Figure 6-14, and the primary key will be assigned automatically by the database sequence. Setting this datatype automatically enables the Refresh After Insert property.
When you create a new entity row whose primary key is a DBSequence
, a unique negative number gets 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. 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.
Note: As shown in Figure 6-14, 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 option. The end user never needs to update this value. |
Figure 6-14 Setting Primary Key Attribute to DBSequence Type Automates Trigger-Assigned Key Handling
Note: The sequence name shown on the Sequence tab only comes into play at design time when you use the Create Database Tables... feature described in Section 6.2.6, "Creating Database Tables from Entity Objects". The sequence indicated here will be created along with the table on which the entity object is based. |
At runtime the framework provides automatic "lost update" detection for entity objects to ensure that a user cannot unknowingly modify data that another user has updated and committed in the meantime. Typically this check is performed by comparing the original values of each persistent entity attribute against the corresponding current column values in the database at the time the underlying row is locked. If an entity object detects that it would be updating a row that is now inconsistent with the current state of the database, it raises the RowInconsistentException
.
You can make the lost update detection more efficient by identifying an attribute of your entity whose value you know will get updated whenever the entity gets modified. Typical candidates include a version number column or an updated date column in the row. The change indicator attribute value might be assigned by a database trigger you've written and refreshed in the entity object using the Refresh After Insert and Refresh After Update properties. Alternatively, you can indicate that the entity object should manage updating the change indicate attribute's value using the history attribute feature described in the next section. To detect whether the row has been modified since the user queried it in the most efficient way, select the Change Indicator to compare only the change indicator attribute values.
Frequently, you'll need to keep track of historical information in your entity object like:
Who created this entity?
When did they create it?
Who last modified this entity?
When did they modify it?
How many times has this row been modified?
Entity objects store this information in a History Column attribute, as shown in Figure 6-15.
If an attribute's datatype is Number
, String
, or Date
, and 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 attribute gets handled depends on the history attribute type that you indicate. If you choose the Version Number type of history column, ADF will automatically increment the value of the numeric attribute every time the object is updated. If you choose Created By, Created On, Modified By, or Modified On, the value will be updated with the current user's username or the current date, respectively, when the object is created or modified.
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. The value of the EMPLOYEE_TYPE
column indicates with a value like H
, S
, or C
, whether a given row represents an hourly, salaried, or contract employee respectively. While many employee attributes and behavior are the same for all employees, certain properties and business logic depends on the type of employee. In this situation it can be convenient to represent these different types of employees using an inheritance hierarchy. 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 property is used to indicate which attribute's value distinguishes the type of row. Section 26.6, "Using Inheritance in Your Business Domain Layer" explains how to set up and use inheritance.
When an entity object composes other entities, it exhibits additional runtime behavior to correctly play its role as a logical container of other nested entity object parts. The following features are always enabled for composing 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. Failure to provide a value for the foreign key at create time, or providing a value that does not identify an existing entity object, throws an InvalidOwnerException
instead of allowing an "orphaned" child row to be created with no well-identified parent entity.
Note: The existence check performed finds new pending entities in the current transaction, as well as existing ones in the database if necessary. |
This feature ensures that the sequence of 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.
When a new entity row having a Refresh On Insert primary key is saved, after its trigger-assigned primary value is retrieved, any composed entities will automatically 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 Association Editor. Figure 6-16 shows this page for the ServiceHistoriesForServiceRequest
association between the ServiceRequest
and ServiceHistory
entity objects. These settings are the defaults that result from reverse-engineering the composition from an ON DELETE CASCADE
foreign key constraint in the database.
The additional features, and the properties that affect their behavior, include the following:
You can either enable or prevent the deletion of a composing parent while composed children entities exist. When the Implement Cascade Delete is unchecked, the removal of the composing entity object is prevented if it contains any composed children. When checked, this option allows the composing entity object to be removed unconditionally and composed children entities are also removed. If the related Optimize for Database Cascade Delete option is unchecked, then the composed entity objects perform their normal DELETE
statement at transaction commit time to make the changes permanent. If the option is checked, 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.
By checking the Cascade Update Key Attributes option, you can enable the automatic update of the foreign key attribute values in composed entities when the primary key value of the composing entity is changed.