Skip Headers
Oracle® Application Development Framework Development Guidelines Manual
10g Release 2 (10.1.2)  
Part No. B14362-02
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents

Previous
Previous
Next
Next
 

3 ADF Business Components in Depth

ADF Business Components technology is a fully-featured, XML-based framework for creating business services. ADF Business Components evolved from the Business Components for Java (BC4J) technology distributed with Oracle9i JDeveloper and earlier releases. The ADF Business Components runtime library handles most business service functionality, which you can customize declaratively (by changing the XML files using JDeveloper's RAD tools) or programatically (by extending library classes). Oracle ADF Business Components technology:

All of the above functionality is fully customizable: if you do not like the way ADF Business Components handles O/R mappings, for example, you can override it.

This chapter introduces ADF Business Components technology. After you have read this chapter, you will be familiar with the ADF BC component types and with some basic issues in ADF Business Components design.

3.1 Summary

3.2 ADF Entity Object Definitions

ADF entity object definitions are business components that encapsulate the business model, including data, rules, and persistence behavior, for items that are used in your application. For example, entity objects can represent:

Entity object definitions map to single objects in the data source. In the vast majority of cases, these are tables, views, synonyms, or snapshots in a database. Advanced programmers can base entity objects on objects from other data sources, such as spreadsheets, XML files, or flat text files.

An entity object definition is the template for entity object instances, which are single Java objects representing individual rows in a database table. For example, the entity object definition called "Departments" provides a template for entity object instances that represent individual rows of the DEPARTMENTS table.

An entity object definition can have up to four parts:

3.2.1 Attributes and Accessors

When entity objects are based on database objects, columns in the database object (such as a database table) map to single entity object attributes in the entity object, although the mapping is not necessarily one-to-one. The definitions of these attributes reflect the properties of these columns, such as the columns' data types, column constraints, and precision and scale specifications. When entity objects are based on objects from other data sources, entity object attributes map to "columns" from those objects, as defined by the programmer.

There are two sorts of entity object attributes:

  • Persistent attributes are those attributes that do map to data source object columns.

  • Transient attributes are all those attributes that do not map to data source object columns. Transient attributes may be derived from information in a database, and are often used for temporary storage and retrieval.

The values of entity attributes can be read and changed in one of two ways:

  • The EntityImpl class provides methods, getAttribute() and setAttribute(), that accept the attribute's name as a String. For example, if the entity object has an attribute called "DepartmentName," you can access this value by calling getAttribute("DepartmentName") or setAttribute("DepartmentName", "Marketing").

  • If you generate an entity object class, it will contain typesafe getters and setters for each attribute. For example, if the entity object has an attribute, "DepartmentName," and you have generated an entity object class, you can access this value by calling getDepartmentName() or setDepartmentName("Marketing").

Typesafe getters and setters are ideal places to put attribute-level business rules, such as validation logic. They contain calls to the methods EntityImpl.getAttributeInternal() and EntityImpl.setAttributeInternal() respectively, and by wrapping that call in additional code (such as an if-then block), you can place conditions on attribute change or access, or enforce logic before or after the attribute is accessed or changed.

3.2.2 Validators

In addition to adding validation logic to getters and setters, you can also declaratively attach validators to entity attributes, whether or not you have generated the entity object class. JDeveloper comes with four simple validators:

  • CompareValidator, which compares an attribute to a value (either a literal value or a value drawn from the data source).

  • ListValidator, which checks to see whether an attribute is in a list of values (either a literal list or the results of a query).

  • RangeValidator, which checks to see whether an attribute is between two literal values.

  • MethodValidator, which can invoke any method in the entity object class which returns a boolean value. Validation is passed if the method returns true.

In addition, you can create your own custom validators. These require coding to create initially, but once created, they can be applied declaratively in many different projects. The validator classes must implement the interfaceoracle.jbo.server.rules.JbiValidator, which has three requirements:

  • A method, validateValue() , which accepts a java.lang.Object (the new attribute value) as a parameter and returns a boolean value, true or false. Generally, you will use this method to return true if the attribute value is acceptable and false if it is not.

  • A method, vetoableChange() , which accepts a parameter of type oracle.jbo.server.util.PropertyChangeEvent. This is the method that EntityImpl calls when the attribute value is changed. Generally, you will use this method to extract the value from the PropertyChangeEvent instance, call validateValue() , and throw an exception if validateValue() returns false.

  • A field (with accessor methods), description. ADF does not use this field, but it is required by the interface.

In addition to these requirements, you can add additional fields to the validation rule. When you apply the rule to an entity attribute, you can customize it declaratively by supplying values for the fields.

3.2.3 The validateEntity() Method

Row-level validation is useful when you need to validate two or more attributes at the same time. You implement this type of validation at the entity level by overriding EntityImpl.validateEntity().

Whenever an instance of an entity object loses its currency (the client is done looking at a particular row), or whenever a client attempts to commit a transaction, validateEntity() is called. If validateEntity() throws an exception, the entity object is prevented from losing currency until the error is fixed.

You should always include a call to super.validateEntity() as part of your extended method. JDeveloper contains tools that will generate a skeleton validateEntity() method that does this. You can, however, add additional code before the call to throw exceptions if your validation logic is not satisfied.

3.2.4 Creation and Deletion Logic

Whenever an entity object instance is created or marked for deletion, it calls the method EntityImpl.create() or EntityImpl.remove(). You can override either or both of these methods to implement business rules that fire when rows are created or deleted.

You should always include a call to super.create() or super.remove() as part of your extended method. JDeveloper contains tools that will generate skeleton create() and create() methods that do this. By adding additional logic after these calls, you can perform additional initialization or cleanup tasks.

3.2.5 DML Customization

ADF BC technology will automatically handle DML operations for you, issuing INSERT, UPDATE, or DELETE commands to the database as necessary. However, you can override the method EntityImpl.doDML() to implement different persistence behavior (such as using stored procedures or writing to a data source other than a database).

doDML() takes as a parameter an int, operation, representing the DML operation requested. The value of the int will always be one of three numbers, each represented by a final variable:

  • DML_INSERT, for row insertion requests

  • DML_DELETE, for row deletion requests

  • DML_UPDATE, for row update requests

By checking the value of operation against these three values, you can override the framework's default behavior.

3.2.6 Security

If you want, you can use Oracle ADF Business Components with the Java Authentication and Authorization Service (JAAS) to provide authentication of users of Oracle ADF applications. Oracle ADF Business Components works with both the OracleAS JAAS Provider and with any JAAS-compliant foreign implementation.

Oracle ADF Business Components uses OracleAS Single Sign-On (SSO) through either the Oracle Internet Directory (OID) or a lightweight JAZN-XML file (useful for testing and scenarios where there is a small number of authorized users) to manage identity.

If you elect to use JAAS authorization with your business components, you can attach permissions to entity object attributes to allow only particular users or members of particular groups access (a process called authorization). You can set access levels to no access, read-only access, or full access.

You can also use entity object attributes called history column attributes to maintain audit trails in the database. These trails contain information about which authenticated users inserted or modified rows and when the changes took place.

3.3 ADF Associations

Relationships between entity object definitions are handled by Oracle ADF associations, which define a relationship between two Oracle ADF entity object definitions based on sets of entity attributes from each.

Associations map to relationships between single objects in the data source. In the vast majority of cases, these are relationships among tables, views, synonyms, and snapshots in a database. Advanced programmers can use associations to represent relationships within other data sources, such as spreadsheets, XML files, or flat text files.

When the data source is a database, associations often map to foreign key relationships between tables in the database. Although you do not need to actually create a foreign key constraint between tables to create a one-to-one or one-to-many association between the corresponding entity objects, there should at least be an appropriate logical relationship between the tables.

3.3.1 Accessor Attributes

When you create an association between two entity object definitions, you can elect to add accessor attributes to the source entity object definition, the destination entity object definition, or both. These accessor attributes function much like other attributes:

  • Their names can be passed as arguments to EntityImpl.getAttribute().

  • If you generate an entity object class, a getter method for the accessor attributes will be included in the class.

What is returned by the call to getAttribute() or the getter method depends on the cardinality of the association.

3.3.2 Cardinality

Associations can range from simple one-to-many relationships based on foreign keys to complex many-to-many relationships. For example, associations can represent:

  • The one-to-many relationship between a customer and all orders placed by that customer

  • The one-to-one relationship between a product and its extended description (if these are represented by separate entity objects)

  • The many-to-many relationship between products and the warehouses that currently stock them

One-to-one and one-to-many associations work much like foreign key relationships: a set of attributes (such as those representing a primary key) of the source entity object are matched with a set of attributes (such as those representing a foreign key) of the destination entity object definition.

Many-to-many associations are effectively the same as two one-to-many relationships involving the source and destination entity object definitions and a third entity object definition, the intersection. For example, the many-to-many relationship between products and the warehouses that stock them can be thought of as two one-to-many relationships:

  • The one-to-many relationship between products and the warehouse inventory entries that mention them

  • The one-to-many relationship between warehouses and their inventory entries

These relationships require three entity objects: one representing products (the source), one representing warehouses (the destination), and one representing warehouse inventory entries (the intersection).

The cardinality of an association affects what is returned by its association accessors:

  • Association accessors returning the "one" end of a one-to-many or a one-to-one association return individual entity object instances.

  • Association accessors returning the "many" end of a one-to-many or a many-to-many association return row iterators.

3.3.3 Row Iterators

Row iterators are containers of entity object instances or view rows. Although row iterators occur in multiple places in the ADF BC architecture, the row iterators returned by association accessors contain entity object instances.

Row iterators contain a current row pointer that points to one particular entity object instance or view row. This pointer can be moved around and used to extract rows from the iterator.

Row iterators contain a number of methods to help you navigate and extract individual rows from them:

  • next() advances the current row pointer in the row iterator and returns that row.

  • hasNext() checks to make sure that the row iterator has more rows after the current row pointer. You can use next() and hasNext() together to create a loop to cycle through the rows in the iterator.

  • first() moves the current row pointer to the first row in the iterator and returns that row.

  • last() moves the current row pointer to the last row in the iterator and returns that row.

  • previous() steps the current row pointer back one row and returns that row.

  • hasPrevious() checks to make sure that the row iterator has more rows after the current row pointer. You can use previous() and hasPrevious() together to create a loop to cycle backwards through the rows in the iterator.

3.3.4 Compositions

A composition is an association in which the source object acts as a container for the destination objects. For inserts, updates, and deletes, instances of the destination entity object are considered parts of instances of the source entity object, rather than independent entities that are merely related to them. An example of this sort of relationship is that between a purchase order and the line items in that order. Unlike the relationship between a department and its employees (employees are independently existing entities that merely have membership in a department), line items are truly part of the purchase order, with no existence independent of it.

Making an association into a composition has the following effects:

  • It prevents instances of the destination from existing independently of their source. You can have ADF Business Components automatically delete destination instances when the source instance is deleted; alternatively, you can have it throw an exception if a source instance is deleted while it still has destination instances.

  • It marks source instances as needing revalidation whenever destination instances are changed.

  • It validates destination instances as part of the validation of source instances.

3.4 ADF Domains

An ADF domain is a special datatype used for Oracle ADF Business Components attributes, such as entity attributes. Oracle ADF Business Components attributes must be objects: they can't be primitive Java types. Attributes can be of standard Java types, such as java.lang.String, or they can be special Oracle ADF BC components called domains. The Oracle typemap maps all SQL types except VARCHAR2 to domains by default (VARCHAR2 is mapped to String).

There are three types of domains:

3.4.1 Predefined Domains

Predefined domains are Java classes in the ADF BC library that wrap JDBC classes (which, in turn, provide Java wrappers for SQL datatypes such as NUMBER). You can use these domains as attribute types when standard Java classes such as String are not appropriate.

The primary domains for use against an Oracle database are all in the package oracle.jbo.domain. They are listed in the following table:

Domain JDBC Class
Array oracle.sql.ARRAY
BFileDomain oracle.sql.BFILE
BlobDomain oracle.sql.BLOB
Char oracle.sql.CHAR
ClobDomain oracle.sql.CLOB
Date oracle.sql.DATE
Number oracle.sql.NUMBER
Raw oracle.sql.RAW
RowID oracle.sql.ROWID
Struct oracle.sql.STRUCT
Timestamp oracle.sql.TIMESTAMP

Three of the above domains, Char, Date, and Number, are generic domains that can be used with any implementation of JDBC (and therefore against any JDBC-compliant database).

oracle.jbo.domain also contains the DBSequence domain. This domain functions like Number, but it maintains a temporary sequence value in memory until the data is posted, thus preventing the wasting of database sequence numbers.

In addition to oracle.jbo.domain, the ADF BC library also contains the package oracle.ord.im. This package contains domains that allow ADF BC to integrate with Oracle interMedia types for multimedia applications.

3.4.2 Oracle Object Type Domains

Oracle object types can be represented in either of two ways:

  • If you use the Oracle object type as a type for object tables only, you can simply use an entity object definition to represent those tables. The entity attributes will match the columns in the object type.

  • If you use the Oracle object type as a type for object columns, the object type will be represented as a custom domain.

Domains for Oracle object types have attributes representing each column in the object type. You can access column values using getter and setter methods, much as you do for entity object attributes.

3.4.3 Validation Domains

You can create custom domains to provide type-level validation. These domains wrap other types that could be used as attribute values (such as predefined domains or standard Java classes like String). After creating such domains, you can use them in place of the other datatype. For example, suppose the Employees entity object definition has an attribute, Email, of type String. You could create a domain, EmailDomain, that wraps String, and use it as the type of Email instead.

Validation domains contain a method, validate(), that is called whenever the domain is instantiated. You can write code in this method to perform tests and throw an exception if the tests are not passed. Doing so has the effect of adding validation logic to all attributes that use the domain as their type.

3.5 ADF View Object Definitions

ADF view object definitions are business components that collect data from the data source, shape that data for use by clients, and allow clients to change that data in the Oracle ADF Business Components cache. For example, a view object definition can gather all the information needed to:

View object definitions must have a mechanism for retrieving data from the data source. Usually, the data source is a database, and the mechanism is a SQL query. Oracle ADF Business Components can automatically use JDBC to pass this query to the database and receive the result.

When view object definitions use a SQL query, query columns map to view attributes in the view object definition. The definitions of these attributes reflect the properties of these columns, such as the columns' data types and precision and scale specifications. When view object definitions use other data sources, view object attributes map to "columns" of data from those data sources, as defined by the programmer.

A view object definition is a template for view object instances, which represent particular caches of rows of data. Different users will always use different view object instances, but the same user may also use multiple view object instances if they need separately maintained caches from the same query.

A view object definition can have up to four parts:

3.5.1 Attribute Mappings

Like entity attributes, the values of view attributes can be read or changed using the methods getAttribute() and setAttribute() in the ViewRowImpl class or by using generated getters and setters in a custom view row class.

There are two different types of view attributes, however, for which these accesssor methods function quite differently:

  • SQL-only view attributes are not mapped to entity attributes. For these attributes, the accessor methods read values from and make changes to data in the view object instance's cache of view rows.

  • Entity-derived view attributes are mapped to attributes in an entity object definition. For these attributes, the accessor methods will call getAttribute() and setAttribute() on the relevant entity object instance. The data will be changed within the entity collection's cache of entity object instances, not within the view object instance's cache of view rows.

Because entity object definitions handle DML operations, attributes that will be used to make changes to the database must be entity-derived. However, if a view object definition will be used for data retrieval only, there is an advantage to making all its attributes SQL-only: such view object definitions, called SQL-only view object definitions, bypass the entity collection's cache entirely, avoiding the overhead and resources required to create entity object instances.

3.5.2 Navigating Through Result Sets

View object instances are row iterators. In particular, they are row iterators of view rows.

Like other row iterators, view object instances contain a current row pointer that points to one particular view row. This pointer can be moved around and used to extract rows from the iterator.

Row iterators contain a number of methods to help you navigate and extract individual rows from them:

  • next() advances the current row pointer in the row iterator and returns that row.

  • hasNext() checks to make sure that the row iterator has more rows after the current row pointer. You can use next() and hasNext() together to create a loop to cycle through the rows in the iterator.

  • First() moves the current row pointer to the first row in the iterator and returns that row.

  • Last() moves the current row pointer to the last row in the iterator and returns that row.

  • Previous() steps the current row pointer back one row and returns that row.

  • hasPrevious() checks to make sure that the row iterator has more rows after the current row pointer. You can use previous() and hasPrevious() together to create a loop to cycle backwards through the rows in the iterator.

3.5.3 Creating and Deleting Rows

ViewObjectImpl also contains methods to create rows:

  • createRow() creates a view row appropriate to the view object definition.

  • insertRow() inserts the row into the view cache.

You can mark a row for deletion by calling Row.remove() or ViewObjectImpl.removeCurrentRow().

3.5.4 Keys

A key is a set of attributes that allow you to quickly retrieve one or more rows from a view object instance's query result. Persistent view object attributes based on primary keys are automatically part of the view object's key; you can make other attributes part of the view object's key as well.

You can use an array containing a partial or complete list of attribute values to set up an object of type oracle.jbo.Key. You can then pass this object into the method ViewObjectImpl.findByKey() to return an array of rows that match the key values.

3.5.5 View Criteria

View criteria are structured criteria that you can use to create searches.

View criteria are collections of view criteria rows. A view criteria row specifies query-by-example requirements for one or more view object attributes. A view row matches if it meets all of the requirements.

When you apply view criteria to a view object instance, the query is restricted to return those view rows that match at least one of the view criteria rows. Effectively, therefore, view criteria assemble a WHERE clause in conjunctive normal form: the WHERE clause is a disjunction of conjunctions of query-by-example requirements.

View criteria are implemented by the class oracle.jbo.ViewCriteria; view criteria rows, by the class oracle.jbo.ViewCriteriaRow.

3.6 ADF View Link Definitions

Relationships between view object definitions are handled by Oracle ADF view link definitions, which define a relationship between two Oracle ADF view object definitions based on sets of entity attributes from each. Like associations, these can range from simple one-to-many relationships based on foreign keys to complex many-to-many relationships.

Individual instances of view objects can also be related by individual instances of view links, which create a master-detail relationship between the query result sets. For example, suppose that you have view object definitions representing a query for department information and a query for employee information, and a view link between the view objects representing the relationship between a department and its employees. If an instance of the former view object definition, allDepartments, is related to an instance of the latter, employeesInDepartment, by an instance of the view link, those instances will be synchronized: whenever a particular row of allDepartments is selected, employeesInDepartment will only display details of that row.

3.6.1 Accessor Attributes

When you create a view link definition between two view object definitions, you can elect to add accessor attributes to the source view object definition, the destination view object definition, or both. These accessor attributes function much like the accessor attributes to associations:

  • Their names can be passed as arguments to ViewObjectImpl.getAttribute().

  • If you generate a view row class, a getter method for the accessor attributes will be included in the class.

  • The accessor method will return a view row or a row iterator, depending on the cardinality of the view link definition.

An accessor attribute returns a row or row iterator that is static, not one that maintains a synchronized master-detail relationship. For example, suppose DepartmentEmployees is an accessor attribute that returns rows of EmployeesView from rows of DepartmentView. Suppose you execute the following code on the current row of DepartmentView:

RowIterator details = current.getAttribute("DepartmentEmployees");

Then suppose the current row of DepartmentView changes. The row iterator in details will not change: it will still contain details of the original row.

To maintain a synchronized master-detail relationship, you should use view link instances in an application module instance.

3.6.2 Cardinality

Like associations, view link definitions can be one-to-one, one-to-many, or many-to-many. One-to-one and one-to-many view link definitions can either be based on associations or they can use attribute matching the way associations do. Many-to-many view link definitions must be based on many-to-many associations.

3.7 ADF Application Module Definitions

Oracle ADF application module definitions are business components that represent particular application tasks. The application module definition provides a data model for the task by aggregating the view object and view link instances required for the task. It also provides services that help the client accomplish the task. For example, an application module can represent and assist with tasks such as:

The most important feature of an application module is its data model—the view object and view link instances it contains. These specify what data the client will have access to, and what relationships hold within that data.

You can use application module definitions in two different ways:

An application module definition can have one or two parts:

3.7.1 View Object and View Link Instances

One of the primary functions of an application module definition is to provide applications with the data they need to complete a specific task. This data can be represented by a tree—the application module's data model—which, in turn, contains view object and view link instances.

A view object instance manages a single cache of retrieved data. View object instances use data retrieval mechanisms (usually SQL queries) provided in view object definitions. However, these mechanisms can be customized on an instance level. In other words, by dynamically adding or changing clauses in the query of one view object instance, you do not automatically make similar changes in other view object instances, even if the instances share a definition. Moreover, executing a query on one view object instance does not automatically execute the others' queries.

All view object instances have a name assigned to them when they are first added to a data model. This name is used by clients and service methods to access the instance and the data stored in the instance's cache. This name is not necessarily related to the view object definition name. For example, the same data model could contain two view object instances, called AllOrders and OrdersForCustomer, based on the same view object definition, called OrdersView.

A view link instance provides a master-detail relationship between view object instances. View link instances are based on view link definitions, which relate the relevant view object definitions.

Adding a view link instance to the data model puts two view object instances in a master-detail relationship; removing the view link makes the detail view object instance completely independent. This, rather than view link accessor attributes, is the way to dynamically maintain master-detail relationships: the detail view object instance's cache will contain only those rows that are details of the master view object instance's current row. When that row changes, the detail cache will automatically change as well.

3.7.2 Transactions

A transaction object is an Oracle ADF Business Components object that represents a database transaction. A transaction object maintains pointers to entity and view caches; it maintains a database connection; and it is responsible for post, commit, and rollback operations.

Unlike database transactions, transaction objects survive commit and rollback operations. Therefore, a single transaction object can correspond to several database transactions over its lifetime.

In general, there is exactly one transaction object per root-level (non-nested) application module instance. If you access a transaction object from a root-level application module instance, any of its nested application modules, or any of the entity object instances in its caches, you will retrieve the same object. If you access transaction objects from two root-level application module instances, you will retrieve different objects.

3.7.3 Service Methods

Service methods are methods on ADF application module definitions that perform complex operations on data. Applications can call these methods in a single network round-trip, saving processing on the client and reducing network chattiness.

Service methods are implemented in an application module's class, and exposed on tier-independent interfaces that allow clients to access the application module consistently, whether it is deployed locally or as an EJB session bean. Inside the service method, you can do any of the following:

  • Dynamically add view object and view link instances to the data model

  • Remove view object and view link instances from the data model

  • Find view object instances and perform operations on their row sets

  • Retrieve and manipulate transaction objects

3.7.4 Application Module Pooling

An application module pool is a resource manager for top-level application module instances. Since storing the view object and entity object caches associated with a transaction and application module instance can be expensive, the application module pool maintains some instances in memory and reuses others. There is one application module pool for each application module definition: all instances of that application module are stored in the pool.

When an application is actively using an application module instance (for example, during a Struts data action), that application module instance is described as checked out. As soon as the application is done with the instance, the instance is checked in to the pool. When the application needs the instance again, it will attempt to check it out again.

When an application requests an application module instance for the first time, the application module pool checks to see how many instances it already contains. If this number is below a parameter called the recycle threshold, the pool creates a new instance for the application.

If the application module pool contains a number of instances equal to or higher than the recycle threshold, it recycles one of the instances. It does so using the following process:

  1. The pool finds the application module instance that has been checked in for the longest time.

  2. The pool writes a redo log of the instance's transaction to the database table PS_TXN.

  3. The instance clears its caches.

  4. The pool passes the newly cleared caches to the application.

By default, the recycle threshold is 10. Many applications will perform better with a higher recycle threshold: setting the threshold is a balance between not having too much data in memory (which can degrade the performance of the application server) and not recycling too many times (because recycling is a time-consuming process). Trial and error using a load tester is often the best way to find this balance.

3.8 ADF Business Components Design Decisions

Two central design decisions face developers who are using ADF Business Components technology as their business services: where to put the code that implements their applications' business rules and whether to base view object definitions on entity object definitions or to make them SQL-only.

3.8.1 Where to Implement Business Rules

Business rules can be provided at multiple levels of an application—in the database, the view, or the business services layer. There are times where each of these locations is appropriate.

Adding business rules to the database, in the form of triggers or stored procedures, provides the maximum level of robustness. These business rules are guaranteed to be available and respected by any application—even by SQL commands run directly from a SQL*Plus prompt. However, business rules coded in the database are not highly responsive. They do not fire until data is posted to the database, which either requires waiting for an explicit post command or requires posting data after every change, which will degrade performance by requiring excessive JDBC round-trips. In addition, adding business rules to the database requires the database to perform tasks other than handling data, which reduces its efficiency and your application's modularity. Finally, adding business rules to the database requires you to integrate your Java or web application with business logic written in PL/SQL code.

Adding business rules to the view layer, in Java for Java client applications or JavaScript for web applications, provides the maximum level of responsiveness. Business rules that trigger as each character is typed into a field, for example, or as a mouse pointer wanders over a graphical image, must be implemented at the view level. However, business rules added to the view layer are not robust. If users access the data through any other user interface, business logic added to the view layer will not be available or enforced.

Adding business rules to ADF BC components is a compromise between these alternatives. Business rules in ADF BC components are more responsive than business rules coded in the database, because they are enforced as soon as changes are made to Java objects in memory, and they avoid the other disadvantages of adding business rules to the database. They are more robust than business rules coded in the view layer, because they will be enforced by any application that uses the components.

Your most critical business rules should be implemented in the database, or redundantly in the database and business services (for increased responsiveness and easier Java integration at the cost of some productivity). Business rules that require truly immediate responsiveness must be implemented in the view layer. The remainder of your business rules can be implemented in ADF Business Components. For this sort of business rule, the hooks provided by ADF BC provide a distinct advantage over other business services technologies.

If you decide to implement a business rule in ADF BC components, you should generally implement it in entity object definitions. Because entity object definitions perform all DML operations, any changes that will affect the database will trigger any appropriate business rules in entity object definitions. Business rules implemented in view object definitions are less robust—they will not be invoked when changes are made through other view object definitions, even those based upon the same entity object definition.

As discussed earlier in this chapter, there are still several choices for where to put business rules in entity object definitions:

  • In the entity object class

  • In validators

  • In domains

If you have business rules other than validation rules, they must be placed in the entity object class. Validation rules can be placed in any of these locations, but there are reasons to choose one over another:

Validation code in the entity object class cannot easily be reused by other entity object definitions. However, it is the easiest way to initially create validation code—simply edit the entity object class and add a method. It's also the only place you can put validation logic that traverses associations.

Validators are highly reusable: they can be reused with multiple entity objects, on attributes of differing type, and customized declaratively. However, they take more work to set up initially than the other forms of validation logic—you must create a validator class, implement the JbiValidator interface, create a property editor if you want to use one to customize the validator, and register the validator with JDeveloper.

Domains represent a compromise between these two options. Creating a validation domain involves creating the domain class and writing the code, but it is still significantly more straightforward than creating a validator. It is also reusable across many attributes in many entity object definitions, although all the attributes must have the same underlying type.

Some architects of large projects have reported that they have found it necessary to impose a single location for business rules. They have found that having some business rules in the entity object class, some in domains, and some in validators makes maintenance considerably more difficult.

3.8.2 Whether to Use Entity Object Definitions

If you want to be able to make changes to the database through a view object definition, you must base it on an entity object definition, because entity object definitions handle all DML operations. Basing view object definitions on entity object definitions has the following other advantages as well:

  • If the view object definition's query is a join query (such as SELECT * FROM DEPARTMENTS, EMPLOYEES WHERE DEPARTMENTS.DEPARTMENT_ID=EMPLOYEES.DEPARTMENT_ID), each row from the master table will appear in many rows of the query result set. If you create an entity object definition for the master table and use it in the view object definition, the data from each row in the master table will be stored only once, in the entity cache. If you do not use an entity object definition, the data will be stored redundantly, in the view cache, for each row in the query result set.

  • Each view object instance maintains its own view cache. If multiple view object instances query data from the same table, and they use an entity object definition to represent the table, the data will need to be stored only once. If they do not use an entity object definition to represent the table, the data must be stored in each view cache.

  • Two entity-derived view attributes mapped to the same entity attribute will show synchronized values. If you call setAttribute() on one view row to change an attribute value for an entity-derived attribute, and getAttribute() on another to read the value of an attribute mapped to the same entity attribute, getAttribute() will return the changed value. If the attributes are SQL-only, they will not be synchronized in this way.

If you need to perform DML operations with a view object definition, you should definitely base it on entity object definitions, and if any of the above considerations apply to the view object definition, you should at least consider the option. Otherwise, you should generally not base view object definitions on entity object definitions—this saves time and resources by not creating entity object instances.