8 Testing View Instance Queries
This chapter includes the following sections:
About View Instance Queries
Use the Oracle ADF Model Tester to achieve interactive ADF application module testing of the data model.
JDeveloper includes an interactive application module testing tool that you can use to test all aspects of its data model without having to use your application user interface or write a test client program. Running the Oracle ADF Model Tester can often be the quickest way of exercising the data functionality of your business service during development.
Note:
When you want to test an application module programmatically, you can write a test client. See How to Create a Command-Line Java Test Client.
View Instance Use Cases and Examples
Using the Oracle ADF Model Tester, you can simulate an end user interacting with your application module data model before you have started to build any custom user interface of your own. Even after you have your UI pages constructed, you will come to appreciate using the Oracle ADF Model Tester to assist in diagnosing problems when they arise. You can reproduce the issues in the Oracle ADF Model Tester to discover if the issue lies in the view or controller layers of the application, or is instead a problem in the business service layer application module itself.
Additional Functionality for Testing View Instances
You may find it helpful to understand other Oracle ADF features before you start working with view instances. Following are links to other functionality that may be of interest.
-
For details about using debugging tools when you run the Oracle ADF Model Tester, see Using the Oracle ADF Model Tester for Testing and Debugging.
-
For details about creating a data model consisting of view object instances, see Implementing Business Services with Application Modules.
-
For a quick reference to the most common code that you will typically write, use, and override in your custom ADF Business Components classes, see Most Commonly Used ADF Business Components Methods.
-
For API documentation related to the
oracle.jbo
package, see the following Javadoc reference document:
Creating an Application Module to Test View Instances
Define an ADF application module and build a data model using view object instances. Use the Oracle ADF Model Tester to test the application module if required.
Before you can test view objects that you create in your data model project, you must create an application module where you will define instances of the view objects you want to test. The application module is the transactional component that the Oracle ADF Model Tester (or UI client) will use to work with application data. The set of view objects used by an application module defines its data model, in other words, the set of data that a client can display and manipulate through a user interface.
To test the view objects you added to an application module, use the Oracle ADF Model Tester, which is accessible from the Applications window. For details about using the Oracle ADF Model Tester, see Testing View Object Instances Using the Oracle ADF Model Tester.
How to Create the Application Module with Individual View Object Instances
To create an application module that will define instances of individual view objects, use the Create Application Module wizard, which is available in the New Gallery.
Before you begin:
It may be helpful to have an understanding of application modules. For more information, see Creating an Application Module to Test View Instances.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.
You will need to complete this task:
- Create the desired view objects, as described in How to Create an Entity-Based View Object and How to Create a Custom SQL Mode View Object.
To create an application module to test individual view object instances:
How to Create the Application Module with Master-Detail View Object Instances
You can also use the Create Application Module wizard to create a hierarchy of view objects for an application module, based on a master-detail relationship that the view objects represent.
Before you begin:
It may be helpful to have an understanding of application modules. For more information, see Creating an Application Module to Test View Instances.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.
You will need to complete this task:
- Create hierarchical relationships between view objects, as described in Working with Multiple Tables in a Master-Detail Hierarchy.
To create an application module based on view object relationships:
Testing View Object Instances Using the Oracle ADF Model Tester
Use Oracle ADF Model Tester to test and diagnose the ADF application module using simulated end user interaction before building a custom user interface.
Using the Oracle ADF Model Tester, you can simulate an end user interacting with your application module data model before you have started to build any custom user interface of your own. Even after you have your UI pages constructed, you will come to appreciate using the Oracle ADF Model Tester to assist in diagnosing problems when they arise. You can reproduce the issues in the Oracle ADF Model Tester to discover whether the problem lies in the view or controller layers of the application, or whether there is instead a problem in the business service layer application module itself.
How to Run the Oracle ADF Model Tester
To test the view objects you added to an application module, use the Oracle ADF Model Tester, which is accessible from the Applications window. When you run the tester from the Applications window, the default application module configuration is used to connect to the database.
Before you begin:
It may be helpful to have an understanding of the Oracle ADF Model Tester. For more information, see Testing View Object Instances Using the Oracle ADF Model Tester.
You may also find it helpful to understand how to use debugging tools when you run the tester. For more information, see Using the Oracle ADF Model Tester for Testing and Debugging.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.
To test view objects using an application module configuration:
How to Run the Oracle ADF Model Tester Using Configurations
To test the view objects you added to an application module, use the Oracle ADF Model Tester, and run it using a specific configuration. The configuration you select will determine the database connection used by the tester.
Before you begin:
It may be helpful to have an understanding of the Oracle ADF Model Tester. For more information, see Testing View Object Instances Using the Oracle ADF Model Tester.
You may also find it helpful to understand how to use debugging tools when you run the tester. For more information, see Using the Oracle ADF Model Tester for Testing and Debugging.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.
To test view objects using a specific application module configuration:
How to Test Language Message Bundles and UI Hints
When your application defines alternative languages in your resource message bundles, you can configure the Oracle ADF Model Tester to recognize these languages. In the Oracle ADF Model Tester, you can then display the Locale menu and select among the available language choices.
To specify a default language for the Oracle ADF Model Tester:
- In the main menu, choose Tools and then Preferences.
- In the Preferences dialog, expand ADF Business Components and select Tester.
- In the Oracle ADF Model Tester page, add any locale for which you have created a resource message bundle to the Selected list.
Alternatively, you can configure the default language choice by setting ADF Business Components runtime configuration properties for a specific application module configuration. These runtime properties also determine which language the Oracle ADF Model Tester will display as the default. To set runtime configuration properties, double-click the application module in the Applications window and, in the overview editor, select the Configurations navigation tab. Then, in the Configurations page of the overview editor, click the configuration hyperlink. In the application module configuration overview editor (on the bc4j.xcfg
file), select the Properties tab and click Add Property to select the following properties from the Add Property dialog and click OK.
-
jbo.default.country
-
jbo.default.language
Then, in the Properties list enter the desired country code for the country and language. For example, to specify the Italian language, you would enter IT
and it
for these two properties:
-
jbo.default.country
=IT
-
jbo.default.language
=it
Testing the language message bundles in the Oracle ADF Model Tester lets you verify that the translations of the UI hints are correctly located. Or, if the message bundle defines date formats for specific attributes, the tool lets you verify that date formats change (like 04/12/2013
to 12/04/2013
).
How to Test Entity-Based View Objects Interactively
You test entity-based view objects interactively in the same way as read-only ones. Just add instances of the desired view objects to the data model of some application module, and then test that application module using the Oracle ADF Model Tester.
You'll find the Oracle ADF Model Tester invaluable for quickly testing and debugging your application modules. Table 8-1 gives an overview of the operations that the Oracle ADF Model Tester toolbar buttons perform when you display an entity-based view object.
Table 8-1 Oracle ADF Model Tester Toolbar Buttons
Button | Operation | Usage |
---|---|---|
![]() |
Move to ... row |
Changes the current row displayed by the Oracle ADF Model Tester. Moves to the first, previous, next, or last row. |
![]() |
Insert a new row |
Creates and inserts a new row. |
![]() |
Delete the current row |
Deletes the current row. |
![]() |
Save changes to the database |
Posts and commits changes that you made in the ADF Business Components cache. |
![]() |
Discard all changes since last save |
Discards changes that you made in the ADF Business Components cache and restores the original values, rolling back any changes posted to the database. |
![]() |
Specify view criteria |
Displays the View Criteria dialog that you can use to create and apply view criteria to the master view object instance. |
![]() |
Validate row |
Validates the current row by applying validation rules defined for all entity object instances. Disabled unless at least one field is editable. |
![]() |
Edit bind variables |
Displays the Bind Variable dialog that you can use to enter values for bind parameters used in the view object query. Disabled unless the view object query uses bind parameters in the query statement. |
To test the entity-based view objects you added to an application module, use the Oracle ADF Model Tester, which is accessible from the Applications window.
Before you begin:
It may be helpful to have an understanding of the Oracle ADF Model Tester. See Testing View Object Instances Using the Oracle ADF Model Tester.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. See Additional Functionality for Testing View Instances.
To test entity-based view objects using an application module configuration:
How to Update the Oracle ADF Model Tester to Display Project Changes
Normally, changes that you make to the data model project will not be picked up automatically by running the Oracle ADF Model Tester. You can, however, force the Oracle ADF Model Tester to reload metadata from the data model project any time you want to synchronize the displayed data model and the data model project. This option is an alternative to quitting the Oracle ADF Model Tester, editing your project, and rerunning the Oracle ADF Model Tester to view the latest changes.
Using the Reload Application option saves time, especially as you work iteratively between the Oracle ADF Model Tester and JDeveloper. For example, while running the Oracle ADF Model Tester you might determine the need to modify the data model with a new view instance or you might find that a view instance is missing an LOV attribute definition. You can return to JDeveloper and use the Business Components overview editors to make the changes that alter the data model metadata. Then, after you recompile the project (a necessary step), you can return to the Oracle ADF Model Tester to reload the updated metadata from the project's class path.
Before you begin:
It may be helpful to have an understanding of the Oracle ADF Model Tester. For more information, see Testing View Object Instances Using the Oracle ADF Model Tester.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.
To reload the data model metadata in the running Oracle ADF Model Tester:
What Happens When You Use the Oracle ADF Model Tester
When you launch the Oracle ADF Model Tester, JDeveloper starts the tool in a separate process and the Oracle ADF Model Tester appears. The tree at the left of the dialog displays all of the view object instances in your application module's data model. After you double-click the desired view object instance, the Oracle ADF Model Tester will display a data view page to inspect the query results. For example, Figure 8-9 shows the view instance Products
that has been double-clicked in the expanded tree to display the data for this view instance in the data view page on the right.
The data view page will appear disabled for any read-only view objects you display because the data is not editable. But even for a read-only view object, the tool affords some useful features:
-
You can validate that the UI hints based on the Label Text hint and format masks are defined correctly.
-
You can also scroll through the data using the toolbar buttons.
-
You can enter Query-by-Example criteria to find a particular row whose data you want to inspect. By clicking the Specify View Criteria button in the toolbar, the View Criteria dialog displays the list of available Query-by-Example criteria.
For example, as shown in Figure 8-9, you can select a view criteria like
FindByProductNameCriteria
and enter a query criteria like "P%
" for aProductName
attribute and click Find to narrow the search to only those products with a name that begins with the letterP
.
The Oracle ADF Model Tester becomes even more useful when you create entity-based view objects that allow you to simulate inserting, updating, and deleting rows, as described in How to Test Entity-Based View Objects Interactively.
Figure 8-9 Built-in Query-by-Example Functionality

Description of "Figure 8-9 Built-in Query-by-Example Functionality"
How to Simulate End-User Interaction in the Oracle ADF Model Tester
When you launch the Oracle ADF Model Tester, the tree at the left of the display shows the hierarchy of the view object instances that the data model of your application module defines. If the data model defines master-detail view instance relationships, the tree will display them as parent and child nodes. A node between the master-detail view instances represent the view link instance that performs the active master-detail coordination as the current row changes in the master. For example, in Figure 8-10 the tree is expanded to show the master-detail relationship between the master Products
view instance and the detail WarehouseStockLevels
view instance. The selected node, ProductsToWarehouseStockLevels1
, is the view link instance that defines the master-detail relationship.
Figure 8-10 Application Module Data Model in the Oracle ADF Model Tester

Double-clicking the view link instance executes the master object and displays the master-detail data in the data view page. For example, in Figure 8-11, double-clicking the ProductsToWarehouseStockLevels1
view link instance in the tree executes the Products
master view instance in the top portion of the data view page and the WarehouseStockLevels
view instance in the bottom portion of the data view page. Additional context menu items on the view object node allow you to reexecute the query if needed, remove the view object from the data model panel, and perform other tasks.
In the master-detail data view page, you can scroll through the query results. Additionally, because instance of entity-based view objects are fully editable, Instead of displaying disabled UI controls showing read-only data for a read-only view object, the data view page displays editable fields. You are free to experiment with creating, inserting, updating, validating, committing, and rolling back.
Figure 8-11 Master-Detail Data View Page in the Oracle ADF Model Tester

For example, you can view multiple levels of master-detail hierarchies, opening multiple data view pages at the same time. Use the Detach context menu item to open any tab into a separate window and visualize multiple view object's data at the same time.
Using just the master-detail data view page, you can test several functional areas of your application.
Testing Master-Detail Coordination
When you click the navigation buttons on the Oracle ADF Model Tester toolbar, you can see that the rows for the current master view object are correctly coordinated. For example, Figure 8-11 shows a master-detail hierarchy with products and warehouses. If you click the Next Row button in the master panel, the master panel will display the next product (identified by a product ID) and the detail panel will update to display the list of warehouses and quantities available for the product.
Testing UI Hints
The entity-based view object attributes inherit their UI hints from those on the underlying entity object attribute. The prompts displayed in the data view page help you see whether you have correctly defined a user-friendly label text UI hint for each attribute. For details on setting up the hint on your entity object, see Defining UI Hints for View Objects.
Testing Business Domain Layer Validation
Depending on the validation rules you have defined, you can try entering invalid values to trigger and verify validation exceptions. For example, when you have defined a range validation rule, enter a value outside the range and see an error similar to:
(oracle.jbo.AttrSetValException) Valid product codes are between 100 and 999
Click the rollback button in the toolbar to revert data to the previous state.
Testing View Objects That Reference Entity Usages
By scrolling through the data — or using the Specify View Criteria button in the Oracle ADF Model Tester toolbar to search — you can verify whether you have correctly altered the WHERE
clause in an entity-based view object's query to use an outer join. The rows should appear as expected.
You also can try changing a primary key attribute of a master view object. This will allow you to verify that the corresponding reference information is automatically updated to reflect the new primary key value.
Use the Oracle ADF Model Tester to verify that UI hints defined at the view object level override the ones it would normally inherit from the underlying entity object. If you notice that several attributes share the same label text, you can edit the UI hint for the desired attributes at the view object level. For example, you can set the Label Text hint to Member Since for the RegisteredDate
attribute and Provisioned? for the ProvisionedFlag
attribute.
Testing Row Creation and Default Value Generation
When displaying an entity-based view object, click the Create Row button in the Oracle ADF Model Tester toolbar for the view object instance to create a new blank row. Any fields that have a declarative default value will appear with that value in the blank row. If the DBSequence
-valued attribute is used, a temporary value will appear in the new row. After entering all the required fields, click the Commit button to commit the transaction. The actual, trigger-assigned primary key should appear in the field after successful commit.
Testing That New Detail Rows Have Correct Foreign Keys
If you click Create Row in the Oracle ADF Model Tester toolbar to try adding a new row to an existing detail entity-based view object instance, you'll notice that the view link automatically ensures that the foreign key attribute value in the new row is set to the value of the current master view instance row.
How to Test Multiuser Scenarios in the Oracle ADF Model Tester
When view objects and entity objects cooperate at runtime, two exceptions can occur when you run the application in a multiuser environment. To anticipate these exceptions, you can simulate a multiuser environment for testing purposes using the Oracle ADF Model Tester. For example, when the application displays edit forms for view object queries, what is the expected behavior when two users attempt to modify the same attribute in their forms?
To understand the expected behavior, open two instances of the Oracle ADF Model Tester on the application module to simulate two users editing the same view object attribute. Keep both instances open and perform the following two tests to demonstrate how multiuser exceptions can arise:
-
In one instance of the Oracle ADF Model Tester, modify an attribute of an existing view object and tab out of the field. Then, in the other tester instance, try to modify the same view object attribute in some way. You'll see that the second user gets the
oracle.jbo.AlreadyLockedException
.You can then change the value of
jbo.locking.mode
to bepessimistic
on the Properties page of the Oracle ADF Model Tester Connect dialog and try repeating the test (the default mode is set tooptimistic
). You'll see the error occurs for the second user immediately after changing the value instead of after committing the change. -
In one instance of the Oracle ADF Model Tester, modify an attribute of an existing view object and tab out of the field. Then, in the other tester instance, retrieve (but don't modify) the same view object attribute. Back in the first window, commit the change. If the second user then tries to modify that same attribute, you'll see that the second user gets the
oracle.jbo.RowInconsistentException
. The row has been modified and committed by another user since the second user retrieved the row into the entity cache.
How to Customize Configuration Options Before Running the Tester
Using the overview editor for the application module, you can select a predefined configuration to run the tool using that named set of runtime configuration properties. The Configurations page of the overview editor also lets you open the overview editor for application module configurations (defined in the bc4j.xcfg
file) to specify properties of the configuration before running the tester.
To display the application module configuration overview editor, double-click the application module in the Applications window and, in the overview editor, select the Configurations navigation tab. Then, in the Configurations page of the overview editor, click the configuration hyperlink. In the application module configuration overview editor, select the Properties tab and click Add Property to select the desired property from the Add Property dialog and click OK.
For example, you could alter the default language for the UI hints by editing the configuration and choosing the Properties tab to add and then set the following two properties with the desired country code (in this case, IT
for Italy):
-
jbo.default.country = IT
-
jbo.default.language = it
How to Enable ADF Business Components Debug Diagnostics
When launching the Oracle ADF Model Tester, if you have configured diagnostic logging for the oracle.jbo
logger, JDeveloper will direct ADF Business Components debug diagnostics messages to the JDeveloper Log window. Figure 8-12 shows the oracle.jbo
logger set to a log level of FINEST
to enable ADF Business Components debug diagnostics.
Figure 8-12 Configuring a Logger for ADF Business Components Debugging

The oracle.jbo
logger can be configured either before running the application or while the application is running in Integrated WebLogic Server. The logging will begin without the need to restart the server. You do not need to run the application in debug mode to log diagnostic messages.
With the oracle.jbo
logger configured, the next time you run the Oracle ADF Model Tester and double-click the view object, you'll see detailed diagnostic output in the Log window, as shown in the following example. Configuring the oracle.jbo
logger with a log level FINEST
will allow you to visualize everything the ADF Business Components framework components are doing for your application.
: [355] Oracle SQLBuilder: Registered driver: oracle.jdbc.OracleDriver [356] Creating a new pool resource [357] **** DBTransactionImpl establishNewConnection [358] Successfully logged in [359] JDBCDriverVersion: xx.xx.x.x-Production [360] DatabaseProductName: Oracle [361] DBTransactionImpl initTransaction [362] Replacing: null with: StoreServiceAM_AddressesPageDef [363] Replacing: null with: StoreServiceAM_MostPopularProductsByCategoriesPageDef ... [537] Orders ViewRowSetImpl.execute caused params to be "un"changed [538] Column count: 41 [539] ViewObject: Orders Created new QUERY statement [540] Orders>#q computed SQLStmtBufLen: 952, actual=865, storing=895 [541] SELECT OrderEO.ORDER_ID, OrderEO.ORDER_DATE, OrderEO.ORDER_SHIPPED_DATE, FROM ORDERS OrderEO ORDER BY OrderEO.ORDER_DATE desc [542] Bind params for ViewObject: Orders
For backward compatibility, it remains possible to enable ADF Business Components debug diagnostics on the data model project using the Java system property jbo.debugoutput=console
. To set the property, open the Run/Debug/Profile page in the Project Properties dialog for your data model project. Click Edit to edit the chosen run configuration, and add -Djbo.debugoutput=console
to the Java Options field in the page. Other legal values for this property are silent
(the default, if not specified) and file
. If you choose the file option, diagnostics are written to the system temp
directory.
Other legal values for the -Djbo.debugoutput
system property are silent
(the default, if not specified) and file
. If you enter the file
option, diagnostics are written to the system temp
directory.
What Happens at Runtime: How View Objects and Entity Objects Cooperate
On their own, view objects and entity objects simplify two important jobs that every enterprise application developer needs to do:
-
Work with SQL query results
-
Modify and validate rows in database tables
Entity-based view objects can query any selection of data that you want the end user to be able to view and modify. Any data the end user is allowed to change will be validated and saved by your reusable business domain layer. The key ingredients you provide as the developer are the ones that only you can know:
-
You decide what business logic should be enforced in your business domain layer
-
You decide what queries describe the data you need to put on the screen
These are the things that make your application unique. The built-in functionality of your entity-based view objects handles the rest of the implementation details.
Note:
Understanding row keys and what role the entity cache plays in the transaction are important concepts that help to clarify the nature of the entity-based view objects. These two concepts are addressed in ViewObject Interface Methods for Working with the View Object's Default RowSet.
What Happens at Runtime: After a View Object Executes Its Query
After adding an instance of an entity-based view object to the application module's data model, you can see what happens at runtime when you execute the query. Like a read-only view object, an entity-based view object sends its SQL query straight to the database using the standard Java Database Connectivity (JDBC) API, and the database produces a result set. In contrast to its read-only counterpart, however, as the entity-based view object retrieves each row of the database result set, it partitions the row attributes based on which entity usage they relate to. This partitioning occurs by creating an entity object row of the appropriate type for each of the view object's entity usages, populating them with the relevant attributes retrieved by the query, and storing each of these entity rows in its respective entity cache. Then, rather than storing duplicate copies of the data, the view row simply points at the entity row parts that comprise it.
Figure 8-13 illustrates how the entity cache partitions the result set attributes of two entity-based view objects. In this example, the highlighted row in the database result set is partitioned into an Order
entity row with primary key 112
and a CustomerInfo
entity row with primary key 301
.
As described in The Role of the Entity Cache in the Transaction, the entity row that is brought into the cache using findByPrimaryKey()
contains all attributes of the entity object. In contrast, an entity row created by partitioning rows from the entity-based view object's query result contains values only for attributes that appear in the query. It does not include the complete set of attributes. This partially populated entity row represents an important runtime performance optimization.
Since the ratio of rows retrieved to rows modified in a typical enterprise application is very high, you can save memory by bringing only the attributes into memory that you need to display instead of bringing all attributes into memory all the time.
Figure 8-13 Entity Cache Partitions View Rows into Entity Rows

Description of "Figure 8-13 Entity Cache Partitions View Rows into Entity Rows"
By partitioning queried data this way into its underlying entity row constituent parts, the first benefit you gain is that all of the rows that include some data queried will display a consistent result when changes are made in the current transaction. In other words, if one view object allows the PaymentType
attribute of customer 301
to be modified, then all rows in any entity-based view object showing the PaymentType
attribute for customer 301
will update instantly to reflect the change. Since the data related to customer 301
is stored exactly once in the CustomerInfo
entity cache in the entity row with primary key 301
, any view row that has queried the order's PaymentType
attribute is just pointing at this single entity row.
Luckily, these implementation details are completely hidden from a client working with the rows in a view object's row set. The client works with a view row, getting and setting the attributes, and is unaware of how those attributes might be related to entity rows behind the scenes.
What Happens at Runtime: After a View Row Attribute Is Modified
When a user attempts to update the attribute of a view row, a series of steps occur to automatically coordinate this view row attribute modification with the underlying entity row. These steps ensure that a validation rule defined on the entity-mapped attribute will be triggered before the value is changed.
Figure 8-14 illustrates the basic steps that occur at runtime when the user attempts to update an entity-mapped attribute. In this example, the modified attribute Status
is mapped to an entity usage where a validation rule is defined.
-
The user attempts to set the
Status
attribute to the valueShip
. -
Since
Status
is an entity-mapped attribute from theOrder
entity usage, the view row delegates the attribute set to the appropriate underlying entity row in theOrder
entity cache having primary key112
. -
Any attribute-level validation rules on the
Status
attribute of theOrder
entity object are evaluated and the modification attempt will fail if any rule does not succeed.Assume that some validation rule for the
Status
attribute programmatically references theShipDate
attribute (for example, to enforce a business rule that anOrder
cannot be shipped the same day it is placed). TheShipDate
was not one of theOrder
attributes retrieved by the query, so it is not present in the partially populated entity row in theOrder
entity cache. -
To ensure that business rules can always reference all attributes of the entity object, the entity object detects this situation and "faults-in" the entire set of
Order
entity object attributes for the entity row being modified using the primary key (which must be present for each entity usage that participates in the view object). -
After the attribute-level validations all succeed, the entity object attempts to acquire a lock on the row in the
ORDERS
table before allowing the first attribute to be modified. -
If the row can be locked, the attempt to set the
Status
attribute in the row succeeds and the value is changed in the entity row.
Note:
The jbo.locking.mode
configuration property controls how rows are locked. The default value is optimistic
. Typically, Fusion web applications will use the default setting optimistic
, so that rows aren't locked until transaction commit time. In pessimistic
locking mode, the row must be lockable before any change is allowed to it in the entity cache.
Figure 8-14 View Row Attribute Updates Delegate to the Entity

Description of "Figure 8-14 View Row Attribute Updates Delegate to the Entity"
What Happens at Runtime: After a Foreign Key Attribute is Changed
When a user attempts to update a foreign key attribute, a series of steps occur to automatically coordinate this view row attribute modification with the underlying entity row. These steps ensure that a validation rule defined on the foreign key, entity-mapped attribute will be triggered before the value is changed. They also ensure that the view row for the changed foreign key attribute reflects the correct attributes of all referenced entity objects.
Figure 8-15 illustrates the basic steps that occur at runtime when the user attempts to update a foreign key, entity-mapped attribute. In this example, the modified attribute CustomerInfoId
is mapped to an entity usage Order
where the attribute is associated with another entity object CustomerInfo
.
-
The user attempts to set the
CustomerInfoId
attribute to the value300
. -
Since
CustomerInfoId
is an entity-mapped attribute from theOrder
entity usage, the view row delegates the attribute set to the appropriate underlying entity row in theOrder
entity cache, which has primary key112
. -
Any attribute-level validation rules on the
CustomerInfoId
attribute of theOrder
entity object are evaluated and the modification attempt will fail if any rule does not succeed. -
The row is already locked, so the attempt to set the
CustomerInfoId
attribute in the row succeeds and the value is changed in the entity row. -
Since the
CustomerInfoId
attribute on theOrder
entity usage is associated with theCustomerInfo
entity object, this change of foreign key value causes the view row to replace its current entity row part for customer301
with the entity row corresponding to the newCustomerInfoId = 300
. This effectively makes the view row for order112
point to the entity row for300
, so the value of thePaymentType
in the view row updates to reflect the correct reference information for this newly assigned customer.
Figure 8-15 After Updating a Foreign Key, View Row Points to a New Entity

Description of "Figure 8-15 After Updating a Foreign Key, View Row Points to a New Entity"
What Happens at Runtime: After a Transaction is Committed
Suppose the user is satisfied with the changes, and commits the transaction. As shown in Figure 8-16, there are two basic steps:
-
The
Transaction
object validates any invalid entity rows in its pending changes list. -
The entity rows in the pending changes list are saved to the database.
The figure depicts a loop in Step 1 before the act of validating one modified entity object might programmatically affect changes to other entity objects. Once the transaction has processed its list of invalid entities on the pending changes list, if the list has entities, the transaction will complete another pass. It will attempt up to ten passes through the list. If by that point there are still invalid entity rows, it will throw an exception because this typically means you have an error in your business logic that needs to be investigated.
Figure 8-16 Committing the Transaction Validates Invalid Entities, Then Saves Them

Description of "Figure 8-16 Committing the Transaction Validates Invalid Entities, Then Saves Them"
What Happens at Runtime: After a View Object Requeries Data
When you reexecute a view object's query, by default the view rows in its current row set are "forgotten" in preparation for reading in a fresh result set. This view object operation does not directly affect the entity cache, however. The view object then sends the SQL to the database and the process begins again to retrieve the database result set rows and partition them into entity row parts.
Note:
Typically when the view object requeries data, you expect it to retrieve the latest database information. If instead you want to avoid a database roundtrip by restricting your view object to querying only over existing entity rows in the cache, or over existing rows already in the view object's row set, see Performing In-Memory Sorting and Filtering of Row Sets.
How Unmodified Attributes are Handled During Requery
As part of the entity row partitioning process during a requery, if an attribute on the entity row is unmodified, then its value in the entity cache is updated to reflect the newly queried value.
How Modified Attributes are Handled During Requery
However, if the value of an entity row attribute has been modified in the current transaction, then during a requery the entity row partitioning process does not refresh its value. Uncommitted changes in the current transaction are left intact so the end-user's logical unit of work is preserved. As with any entity attribute value, these pending modifications continue to be consistently displayed in any entity-based view object rows that reference the modified entity rows.
Note:
End-user row inserts and deletes are also managed by the entity cache, which permits new rows to appear and deleted rows to be skipped during requerying. For more information about new row behavior, see Maintaining New Row Consistency Between View Objects Based on the Same Entity.
For example, Figure 8-17 illustrates the scenario where a user "drills down" to a different page that uses the Orders
view object instance to retrieve all details about order 112
and that this happens in the context of the current transaction's pending changes. That view object has two entity usages: a primary Orders
usage and a reference usage for CustomerInfo
. When its query result is partitioned into entity rows, it ends up pointing at the same Order
entity row that the previous OrderInfo
view row had modified. This means the end user will correctly see the pending change, that the order is assigned to sking
in this transaction.
Figure 8-17 Entity Cache Merges Sets of Entity Attributes from Different View Objects

How Overlapping Subsets of Attributes are Handled During Requery
Two different view objects can retrieve two different subsets of reference information and the results are merged whether or not they have matching sets of attributes. For example, Figure 8-17 also illustrates the situation, where the Orders
view object queries the user's Email
, while the OrderInfo
view object queried the user's PaymentOption
. The figure shows what happens at runtime: if while partitioning the retrieved row, the entity row part contains a different set of attributes than does the partially populated entity row that is already in the cache, the attributes get "merged". The result is a partially populated entity row in the cache with the union of the overlapping subsets of user attributes. In contrast, for jchen
(user 302), who wasn't in the cache already, the resulting new entity row contains only the Email
attribute, but not the PaymentOption
.
What You May Need to Know About Optimizing View Object Runtime Performance
The view object provides tuning parameters that let you control how SQL is executed and how data is fetched from the database. These tuning parameters play a significant role in the runtime performance of the view object. If the fetch options are not tuned correctly for the application, then your view object may fetch an excessive amount of data and may make too many roundtrips to the database.
You can use the Tuning section of the General page of the overview editor to configure the fetch options shown in Table 8-2.
Table 8-2 Parameters to Tune View Object Performance
Fetch Tuning Parameters | Usage |
---|---|
Fetch Mode |
The default fetch option is the All Rows option, which will be retrieved As Needed ( |
Fetch Size |
In conjunction with the Fetch Mode option, the in Batches of field controls the number of records fetched at one time from the database ( |
Max Fetch Size |
The default max fetch size for a view object is -1, which means that there is no limit to the number of rows the view object can fetch. In cases where the result set should contain only n rows of data, the option Only up to row number should be selected and set to n. The developer can alternatively call For view objects whose As mentioned earlier, setting a maximum fetch size of 0 (zero) makes the view object insert-only. In this case, no select query will be issued, so no rows will be fetched. When you want to specify a global threshold for all view object queries in the application, you can configure the Row Fetch Limit property in the |
Forward-only Mode |
If a data set will only be traversed going forward, then forward-only mode can help performance when iterating through the data set. This can be configured by programmatically calling |
When you tune view objects, you should also consider these issues:
-
Large data sets: View objects provide a mechanism to page through large data sets such that a user can jump to a specific page in the results. This is configured by calling
setRangeSize(
n
)
followed bysetAccessMode(RowSet.RANGE_PAGING)
on the view object where n is the number of rows contained within one page. When the user navigates to a specific page in the data set, the application can callscrollToRangePage(P)
on the view object to navigate to page P. Range paging fetches and caches only the current page of rows in the view object row cache at the cost of another query execution to retrieve each page of data. Range paging is not appropriate where it is beneficial to have all fetched rows in the view object row cache (for example, when the application needs to read all rows in a dataset for an LOV or page back and forth in records of a small data set. -
Spillover: There is a facility to use the data source as "virtual memory" when the JVM container runs out of memory. By default, this is disabled and can be turned on as a last resort by setting
jbo.use.pers.coll=true
. Enabling spillover can have a large performance impact. -
SQL platform: If the generic SQL92 SQL platform is used to connect to generic SQL92-compliant databases, then some view object tuning options will not function correctly. The parameter that choosing the generic SQL92 SQL platform affects the most is the fetch size. When SQL92 SQL platform is used, the fetch size defaults to 10 rows regardless of what is configured for the view object. You can set the SQL platform when you define the database connection or you can define it as global project setting in the
adf-config.xml
file. By default, the SQL platform will beOracle
. To manually override the SQL platform, you can also pass the parameter-Djbo.SQLBuilder="SQL92"
to the JVM upon startup.
Additionally, you have some options to tune the view objects' associated SQL for better database performance:
-
Bind variables: If the query associated with the view object contains values that may change from execution to execution, use bind variables. Using bind variables in the query allows the query to reexecute without needing to reparse the query on the database. You can add bind variables to the view object in the Query page of the overview editor for the view object. For more information, see Working with Bind Variables.
-
Query optimizer hints: The view object can pass hints to the database to influence which execution plan to use for the associated query. The optimizer hints can be specified in the Retrieve from the Database group box in the Tuning section of the overview editor for the view object. For information about optimizer hints, see Specify a Query Optimizer Hint if Necessary.
Testing View Object Instances Programmatically
You can use client programs to test an ADF application module programmatically.
When you are ready to test a working application module containing at least one view object instance, you can build a simple test client program to illustrate the basics of working programmatically with the data in the contained view object instances.
From the point of view of a client accessing your application module's data model, the API's to work with a read-only view object and an entity-based view object are identical. The key functional difference is that entity-based view objects allow the data in a view object to be fully updatable. The application module that contains the entity-based view objects defines the unit of work and manages the transaction. This section presents test client programs that work with the SummitADF_Examples
workspace to illustrate:
-
Iterating master-detail-detail hierarchy
-
Finding a row and updating a foreign key value
-
Creating a new order
-
Retrieving the row key identifying a row
ViewObject Interface Methods for Working with the View Object's Default RowSet
The ViewObject
interface in the oracle.jbo
package provides the methods to easily perform any data-retrieval task. Some of these methods used in the example include:
-
executeQuery()
, to execute the view object's query and populate its row set of results -
setWhereClause()
, to add a dynamic predicate at runtime to narrow a search -
setNamedWhereClauseParam()
, to set the value of a named bind variable -
hasNext()
, to test whether the row set iterator has reached the last row of results -
next()
, to advance the row set iterator to the next row in the row set -
getEstimatedRowCount()
, to count the number of rows a view object's query would return
Typically, when you work with a view object, you will work with only a single row set of results at a time. To simplify this overwhelmingly common use case, as shown in Figure 8-18, the view object contains a default RowSet
, which, in turn, contains a default RowSetIterator
. The default RowSetIterator
allows you to call all of the data-retrieval methods directly on the ViewObject
component itself, knowing that they will apply automatically to its default row set.
Figure 8-18 ViewObject Contains a Default RowSet and RowSetIterator

Note:
Defining Polymorphic View Objects presents situations when you might want a single view object to produce multiple distinct row sets of results. You can also find scenarios for creating multiple distinct row set iterators for a row set. Most of the time, however, you'll need only a single iterator.
The phrase "working with the rows in a view object," when used in this guide more precisely means working with the rows in the view object's default row set. Similarly, the phrase "iterate over the rows in a view object," more precisely means you will use the default row set iterator of the view object's default row set to loop over its rows.
The Role of the Key Object in a View Row or Entity Row
When you work with view rows you use the Row
interface in the oracle.jbo
package. As shown in Figure 8-19, the interface contains a method called getKey()
that you can use to access the Key
object that identifies any row. Notice that the Entity
interface in the oracle.jbo.server
package extends the Row
interface. This relationship provides a concrete explanation of why the term entity row is so appropriate. Even though an entity row supports additional features for encapsulating business logic and handling database access, you can still treat any entity row as a Row
.
An entity-based view object delegates the task of finding rows by key to its underlying entity row parts.
Recall that both view rows and entity rows support either single-attribute or multiattribute keys, so the Key
object related to any given Row
will encapsulate all of the attributes that comprise its key. Once you have a Key
object, you can use the findByKey()
method on any row set to find a row based on its Key
object. When you use the findByKey()
method to find a view row by key, the view row proceeds to use the entity definition's findByPrimaryKey()
method to find each entity row contributing attributes to the view row key.
In the case of a read-only view object with no underlying entity row to which to delegate this task, the view object implementation automatically enables the manageRowsByKey
flag when at least one primary key attribute is detected. This ensures that the findByKey()
method is successful in the case of read-only view objects. If the manageRowsByKey
flag is not enabled, then UI operations like setting the current row with the key, which depend on the findByKey()
method, would not work.
Figure 8-19 Any View Row or Entity Row Supports Retrieving Its Identifying Key

Description of "Figure 8-19 Any View Row or Entity Row Supports Retrieving Its Identifying Key"
Note:
When you define an entity-based view object, by default the primary key attributes for all of its entity usages are marked with their Key Attribute property set to true
. In any nonupdatable reference entity usages, you should disable the Key Attribute property for the key attributes. Since view object attributes related to the primary keys of updatable entity usages must be part of the composite view row key, their Key Attribute property cannot be disabled.
The Role of the Entity Cache in the Transaction
An application module is a transactional container for a logical unit of work. At runtime, it acquires a database connection using information from the named configuration you supply, and it delegates transaction management to a companion Transaction
object. Since a logical unit of work may involve finding and modifying multiple entity rows of different types, the Transaction
object provides an entity cache as a "work area" to hold entity rows involved in the current user's transaction. Each entity cache contains rows of a single entity type, so a transaction involving two or more entity objects holds the working copies of those entity rows in separate caches.
By using an entity object's related entity definition, you can write code in an application module to find and modify existing entity rows. As shown in Figure 8-20, by calling findByPrimaryKey()
on the entity definition for the Order
entity object, you can retrieve the row with that key. If it is not already in the entity cache, the entity object executes a query to retrieve it from the database. This query selects all of the entity object's persistent attributes from its underlying table, and finds the row using an appropriate WHERE
clause against the column corresponding to the entity object's primary key attribute. Subsequent attempts to find the same entity row by key during the same transaction will find it in the cache, preventing the need for a trip to the database. In a given entity cache, entity rows are indexed by their primary key. This makes finding an entity row in the cache a fast operation.
When you access related entity rows using association accessor methods, they are also retrieved from the entity cache. If related entity rows are not in the cache, then they are retrieved from the database. Finally, the entity cache is also the place where new entity rows wait to be saved. In other words, when you use the createInstance2()
method on the entity definition to create a new entity row, it is added to the entity cache.
Figure 8-20 Entity Cache Stores Entity Rows During the Transaction

Description of "Figure 8-20 Entity Cache Stores Entity Rows During the Transaction"
When an entity row is created, modified, or removed, it is automatically enrolled in the transaction's list of pending changes. When you call commit()
on the Transaction
object, it processes its pending changes list, validating new or modified entity rows that might still be invalid. When the entity rows in the pending list are all valid, the Transaction
issues a database SAVEPOINT
and coordinates saving the entity rows to the database. If all goes successfully, it issues the final database COMMIT
statement. If anything fails, the Transaction
performs a ROLLBACK TO SAVEPOINT
to allow the user to fix the error and try again.
The Transaction
object used by an application module represents the working set of entity rows for a single end-user transaction. By design, it is not a shared, global cache. The database engine itself is an extremely efficient shared, global cache for multiple, simultaneous users. Rather than attempting to duplicate all the work of fine-tuning that has gone into the database's shared, global cache functionality, ADF Business Components consciously embraces it. To refresh a single entity object's data from the database at any time, you can call its refresh()
method. You can setClearCacheOnCommit()
or setClearCacheOnRollback()
on the Transaction
object to control whether entity caches are cleared at commit or rollback. The defaults are false
and true
, respectively. The Transaction
object also provides a clearEntityCache()
method you can use to programmatically clear entity rows of a given entity type (or all types). When you clear an entity cache, you allow entity rows of that type to be retrieved from the database fresh the next time they are either found by primary key or retrieved by an entity-based view object.
How to Create a Command-Line Java Test Client
To the create a test client program, use the Create Java Class wizard, which is accessible from the New Gallery.
Generating a Test Client with Skeleton Code
When you use the Create Java Class wizard to create the test client program, JDeveloper will open your program file in the source editor and allow you to add code from a predefined code template to complete the test client.
Before you begin:
It may be helpful to have an understanding of programmatic testing. For more information, see Testing View Object Instances Programmatically.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.
To generate a skeleton Java test client:
package oracle.summit.model.test; public class TestClient { public static void main(String[] args) { TestClient testClient = new TestClient(); } }
Modifying the Skeleton Code to Create the Test Client
After you generate skeleton code for the test client, you can proceed to edit the file using the skeleton code template you created.
Before you begin:
It may be helpful to have an understanding of programmatic testing. For more information, see Testing View Object Instances Programmatically.
You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.
You will need to complete this task:
- Create create the test client skeleton code, as described in Generating a Test Client with Skeleton Code.
To insert the skeleton code:
Your skeleton test client for your application module should contain source code like what you see in the following example.
Note:
The examples throughout Working Programmatically with an Application Module's Client Interface expand this test client sample code to illustrate calling custom application module service methods, too.
package oracle.summit.model.viewobjects; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient { public static void main(String[] args) { String amDef = "oracle.summit.model.viewobjects.AppModule"; String config = "AppModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); // 1. Find the Customer view object instance. ViewObject customerList = am.findViewObject("SCustomerView1"); // Work with your appmodule and view object here Configuration.releaseRootApplicationModule(am,true); } }
The method call createRootApplicationModule()
allows test clients to create an application module instance where the ADF Model layer is unavailable. Normally, in the Fusion web applications your code finds the view object on the application module through ADF binding objects. For more information about how to work with the ADF Model layer, see How to Access an Application Module Client Interface in a Fusion Web Application.
Replace // Work with your appmodule and view object here
, with code that will execute the view objects you want to test. For example, to execute the view object's query, display the number of rows it will return, and loop over the result to fetch the data and print it out to the console, you can adapt the code shown in the following example for your data model project components.
// 2. Execute the query customerList.executeQuery(); // 3. Iterate over the resulting rows while (customerList.hasNext()) { Row customer = customerList.next(); // 4. Print the customer's name System.out.println("Customer: " + customer.getAttribute("Name")); // 5. Get related rowset of Orders using view link accessor attribute RowSet orders = (RowSet)customer.getAttribute("SOrdView"); // 6. Iterate over the Orders rows while (orders.hasNext()) { Row order = orders.next(); // 7. Print out some order attribute values System.out.println(" ["+order.getAttribute("CustomerId")+"] "+ order.getAttribute("Id")+": "+ order.getAttribute("Total")); if(!order.getAttribute("OrderFilled").equals("Y")) { // 8. Get related rowset of OrderItems RowSet items = (RowSet)order.getAttribute("SItemView"); // 9. Iterate over the OrderItems rows while (items.hasNext()) { Row item = items.next(); // 10. Print out some order items attributes System.out.println(" "+item.getAttribute("ItemId")+": "+ item.getAttribute("Price")); } } } }
The first line calls the executeQuery()
method to execute the view object's query. This produces a row set of zero or more rows that you can loop over using a while
statement that iterates until the view object's hasNext()
method returns false
. Inside the loop, the code puts the current Row
in a variable named customer
, then invokes the getAttribute()
method twice on that current Row
object to get the value of the Name
and Orders
attributes to print order information to the console. A second while
statement performs the same task for the line items of the order.
What Happens When You Run a Test Client Program
The call to createRootApplicationModule()
on the Configuration
object returns an instance of the application module to work with. As you might have noticed in the debug diagnostic output, the ADF Business Components runtime classes load XML documents as necessary to instantiate the application module and the instance of the view object component that you've defined in its data model at design time. The findViewObject()
method on the application module finds a view object instance by name from the application module's data model. After the loop described in Modifying the Skeleton Code to Create the Test Client, the test client executes releaseRootApplicationModule()
on the Configuration
object. This signals that you're done using the application module and it allows the framework to clean up resources, like the database connection that was used by the application module.
What You May Need to Know About Running a Test Client
The createRootApplicationModule()
and releaseRootApplicationModule()
methods are very useful for command-line access to application module components. However, you typically won't need to write these two lines of code in the context of an ADF-based web application. The ADF Model layer cooperates automatically with the ADF business services layer to acquire and release ADF application module components for you in those scenarios. For more information about how to work with the ADF Model layer, see How to Access an Application Module Client Interface in a Fusion Web Application.
How to Count the Number of Rows in a Row Set
The getEstimatedRowCount()
method is used on a RowSet
to determine how many rows it contains:
long numOrders = orders.getEstimatedRowCount();
The implementation of the getEstimatedRowCount()
initially issues a SELECT COUNT(*)
query to calculate the number of rows that the query will return. The query is formulated by "wrapping" your view object's entire query in a statement like:
SELECT COUNT(*) FROM ( ... your view object's SQL query here ... )
The SELECT COUNT(*)
query allows you to access the count of rows for a view object without necessarily retrieving all the rows themselves. This approach permits an important optimization for working with queries that return a large number of rows, or for testing how many rows a query would return before proceeding to work with the results of the query.
Once the estimated row count is calculated, subsequent calls to the method do not reexecute the COUNT(*)
query. The value is cached until the next time the view object's query is executed, since the fresh query result set returned from the database could potentially contain more, fewer, or different rows compared with the last time the query was run. The estimated row count is automatically adjusted to account for pending changes in the current transaction, adding the number of relevant new rows and subtracting the number of removed rows from the count returned.
You can also override getEstimatedRowCount()
to perform a custom count query that suits your application's needs.
How to Access a Detail Collection Using the View Link Accessor
Once you've retrieved the RowSet
of detail rows using a view link accessor, as described in Programmatically Accessing a Detail Collection Using the View Link Accessor, you can loop over the rows it contains using the same pattern used by the view object's row set of results, as shown in the following example.
while (orders.hasNext()) { Row curOrder = orders.next(); System.out.println("--> (" + curOrder.getAttribute("Id") + ") " + curOrder.getAttribute("Total")); }
The following example shows the main()
method sets a dynamic WHERE
clause to restrict the CustomerView
view object instance to show only customers whose credit_rating_id
has the value 4
. Additionally, the executeAndShowResults()
method accesses the view link accessor attribute (SOrdView
) and prints out the order Id
and Total
attribute for each customer.
To access the a detail collection using a view link accessor, follow these basic steps (as illustrated in the following example):
-
Find the master view object instance.
-
Execute the query.
-
Iterate over the master view object rows.
-
Get the related row set of the detail view object using the view link accessor attribute.
-
Iterate over the detail view object rows.
-
Optionally, do something with the detail row set attributes.
Performance Tip:
If the code you write to loop over the rows does not need to display them, then you can call the closeRowSet()
method on the row set when you're done. This technique will make memory use more efficient. The next time you access the row set, its query will be reexecuted.
package oracle.summit.model.viewobjects; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient2 { public static void main(String[] args) { String amDef = "oracle.summit.model.viewobjects.AppModule"; String config = "AppModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); ViewObject vo = am.findViewObject("SCustomerView1"); // Add an extra where clause with a new named bind variable vo.setWhereClause("credit_rating_id = :theCreditRating"); vo.defineNamedWhereClauseParam("theCreditRating", null, null); vo.setNamedWhereClauseParam("theCreditRating", "4"); // Show results when :theCreditRating = '4' executeAndShowResults(vo); Configuration.releaseRootApplicationModule(am, true); } private static void executeAndShowResults(ViewObject vo) { System.out.println("---"); vo.executeQuery(); while (vo.hasNext()) { Row curCustomer = vo.next(); // Access the row set of details using the view link accessor attr. RowSet orders = (RowSet)curCustomer.getAttribute("SOrdView"); long numOrders = orders.getEstimatedRowCount(); System.out.println(curCustomer.getAttribute("Id") + " " + curCustomer.getAttribute("Name")+" ["+ numOrders+" orders]"); while (orders.hasNext()) { Row curOrder = orders.next(); System.out.println("--> (" + curOrder.getAttribute("Id") + ") " + curOrder.getAttribute("Total")); } } } }
Running TestClient2.java
produces output in the Log window, as shown in the following example. Each customer is listed, and for each customer that has some orders, the order ID and total appears beneath their name.
--- 202 Simms Athletics [11 orders] --> (98) 595 --> (113) 4990 --> (114) 567 --> (115) 866.7 --> (116) 3661.24 --> (117) 17489 212 Hamada Sport [22 orders] --> (108) 149570 --> (196) 761 --> (197) 516.75 ...
If you run TestClient2.java
with debug diagnostics enabled, you will see the SQL queries that the view object performed. The view link WHERE
clause predicate is used to automatically perform the filtering of the detail service request rows for the current row in the CustomerView
view object.
How to Iterate Over a Master-Detail-Detail Hierarchy
To iterate over a master-detail with an additional level of nesting, follow these basic steps (as illustrated in the following example):
-
Find the master view object instance.
-
Executes the query.
-
Iterate over the resulting rows.
-
Optionally, do something with the attributes of the master row set.
-
Get the related row set of the detail view object using the view link accessor attribute.
-
Iterate over the detail row set rows.
-
Optionally, do something with the attributes of the detail row set.
-
Get the related row set of the second detail view object using the view link accessor attribute.
-
Iterates over the second detail row set rows.
-
Optionally, do something with the second detail row set attributes.
Other than having one additional level of nesting, the following example uses the same API's used in the TestClient
program that was iterating over master-detail read-only view objects in How to Access a Detail Collection Using the View Link Accessor.
If you use JDeveloper's Refactor > Duplicate functionality on an existing TestClient.java
class, you can quickly "clone" it to create a TestClient2.java
class. For example, the TestClient.java
class described in How to Access a Detail Collection Using the View Link Accessor is suited to this technique.
package oracle.summit.model.viewobjects; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient { public static void main(String[] args) { TestClient testClient = new TestClient(); String amDef = "oracle.summit.model.viewobjects.AppModule"; String config = "AppModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); // Work with your appmodule and view object here // 1. Find the Customer view object instance. ViewObject customerList = am.findViewObject("SCustomerView1"); // 2. Execute the query customerList.executeQuery(); // 3. Iterate over the resulting rows while (customerList.hasNext()) { Row customer = customerList.next(); // 4. Print the person's email System.out.println("Customer: " + customer.getAttribute("Name")); // 5. Get related rowset of Orders using view link accessor attr. RowSet orders = (RowSet)customer.getAttribute("SOrdView"); // 6. Iterate over the Orders rows while (orders.hasNext()) { Row order = orders.next(); // 7. Print out some order attribute values System.out.println(" ["+order.getAttribute("CustomerId")+"] "+ order.getAttribute("Id")+": "+ order.getAttribute("Total")); if(order.getAttribute("OrderFilled").equals("Y")) { // 8. Get related rowset of Items RowSet items = (RowSet)order.getAttribute("SItemView"); // 9. Iterate over the Items rows while (items.hasNext()) { Row item = items.next(); // 10. Print out some order items attributes System.out.println(" "+ item.getAttribute("ItemId")+": $"+ item.getAttribute("Price")); } } } } } }
Running the program produces the output shown in the following example.
Customer: Unisports [201] 97: 84000 210: $9 211: $1500 Customer: Simms Athletics [202] 98: 595 212: $85 [202] 113: 4990 328: $9 [202] 114: 567 329: $28 330: $40.95 331: $25 ...
How to Find a Row and Update a Foreign Key Value
To find a row and update a foreign key value, follow these basic steps (as illustrated in the following example):
-
Find the view object instance.
-
Construct a
Key
object to look up the row for the view instance. -
Use
findByKey()
to find the row. -
Optionally, do something with the row's attribute.
The following example shows the main()
method finds and updates a foreign key value to find a row of the Orders
view object instance. The sample then prints out the existing value of the OrderStatusCode
attribute before changing the value on the row.
package oracle.summit.model.viewobjects; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import oracle.jbo.ApplicationModule; import oracle.jbo.JboException; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient3 { public TestClient3() { super(); } public static void main(String[] args) { String amDef = "oracle.summit.model.viewobjects.AppModule"; String config = "AppModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); // 1. Find the Orders view object instance ViewObject vo = am.findViewObject("SOrdView1"); // 2. Construct a new Key to find Order # 100 Key orderKey = new Key(new Object[] { 100 }); // 3. Find the row matching this key Row[] ordersFound = vo.findByKey(orderKey, 1); if (ordersFound != null && ordersFound.length > 0) { Row order = ordersFound[0]; // 4. Print some order information Date dateOrdered = (Date) order.getAttribute("DateOrdered"); String orderDate = dateOrdered.toString(); System.out.println("Order Date is: " + orderDate); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Calendar c = Calendar.getInstance(); try { c.setTime(sdf.parse(orderDate)); } catch (ParseException e) { } c.add(Calendar.DAY_OF_MONTH, -2); // // number of days to add orderDate = sdf.format(c.getTime()); System.out.println(orderDate); try { // 5. Try setting the ship date to an illegal value order.setAttribute("DateShipped", orderDate); am.getTransaction().commit(); } catch (JboException ex) { System.out.println("ERROR: " + ex.getMessage()); } // 6. Set the ship date to a legal value order.setAttribute("DateShipped", orderDate); // 7. Show the value of the ship date was updated successfully System.out.println("Ship date is: " + order.getAttribute("DateShipped")); // 8. Show the current value of the customer for this order System.out.println("Customer: " + order.getAttribute("CustomerId")); // 9. Reassign the order to customer # 210 order.setAttribute("CustomerId", 210); // 10. Show the value of the reference information now System.out.println("Customer: " + order.getAttribute("CustomerId")); // 11. Rollback the transaction am.getTransaction().rollback(); System.out.println("Transaction canceled"); } Configuration.releaseRootApplicationModule(am, true); } }
Running this example produces the output shown in the following example.
Order Date is: 2012-08-29 2012-08-27 ERROR: JBO-oracle.summit.model.viewobjects.SOrd_Rule_0: You cannot have a shipping date that is before the order date Ship date is: 2012-08-27 Customer: 204 Customer: 210 Transaction canceled
How to Create a New Row for a View Object Instance
To create a new view row instance, follow these basic steps (as illustrated in the following example):
-
Find the view object instance.
-
Create a new row and insert it into the row set.
-
Set the values of the required attributes in the new row.
-
Commit the transaction.
The following example shows the main()
method finds the OrdView
view object instance and inserts a new row into the row set. Because the OrdView
view object is entity-based, the CreatedBy
attribute derives its value from the mapped entity object attribute. The sample then sets values for the remaining attributes before committing the transaction.
package oracle.summit.model.viewobjects; import java.util.Date; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.jbo.domain.DBSequence; public class TestClient4 { public static void main(String[] args) { String amDef = "oracle.summit.model.viewobjects.AppModule"; String config = "AppModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); // 1. Find the Orders view object instance. ViewObject orders = am.findViewObject("SOrdView1"); // 2. Create a new row and insert it into the row set Row newOrder = orders.createRow(); orders.insertRow(newOrder); // Show the entity object-related defaulting for DateOrdered attribute System.out.println("DateOrdered defaults to: " + newOrder.getAttribute("DateOrdered")); // 3. Set values for some of the required attributes newOrder.setAttribute("CustomerId", 201); newOrder.setAttribute("SalesRepId", 12); newOrder.setAttribute("PaymentTypeId", 2); newOrder.setAttribute("OrderFilled", "N"); // 4. Commit the transaction am.getTransaction().commit(); // 5. Retrieve and display the trigger-assigned order id DBSequence id = (DBSequence)newOrder.getAttribute("Id"); System.out.println("Thanks, reference number is " + id.getSequenceNumber()); Configuration.releaseRootApplicationModule(am, true); } }
Running this example produces the results shown in the following example.
DateOrdered defaults to: 2013-3-11 Thanks, reference number is 6
How to Retrieve the Row Key Identifying a Row
To retrieve a row key to identify a row, follow these basic steps (as illustrated in the following example):
-
Find the view object instance.
-
Construct a key using a supplied value.
-
Find the row with this key.
-
Optionally, do something with the key of the row.
The following example shows the main()
method finds the OrdView
view object instance and constructs a row key to find an order number. The findByKey()
method find the OrdView
rows with the specified key. The sample then displays the key of the row, accesses the row set using the ItemView
view link accessor, and iterates over the rows to display the key of each ItemView
row.
package oracle.summit.model.viewobjects; import oracle.jbo.ApplicationModule; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient5 { public static void main(String[] args) { String amDef = "oracle.summit.model.viewobjects.AppModule"; String config = "AppModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); // 1. Find the Orders view object instance ViewObject vo = am.findViewObject("SOrdView1"); // 2. Construct a key to find order number 100 Key orderKey = new Key(new Object[] { 100 }); // 3. Find the Orders row with the key Row[] ordersFound = vo.findByKey(orderKey, 1); if (ordersFound != null && ordersFound.length > 0) { Row order = ordersFound[0]; // 4. Displays the key of the Orders row showKeyFor(order); // 5. Accesses row set of Orders using OrderItems view link accessor RowSet items = (RowSet)order.getAttribute("SItemView"); // 6. Iterates over the Items row while (items.hasNext()) { Row itemRow = items.next(); // 4. Displays the key of each Items row showKeyFor(itemRow); } } Configuration.releaseRootApplicationModule(am, true); } private static void showKeyFor(Row r) { // get the key for the row passed in Key k = r.getKey(); // format the key as "(val1,val2)" String keyAttrs = formatKeyAttributeNamesAndValues(k); // get the serialized string format of the key, too String keyStringFmt = r.getKey().toStringFormat(false); System.out.println("Key " + keyAttrs + " has string format " + keyStringFmt); } // Build up "(val1,val2)" string for key attributes private static String formatKeyAttributeNamesAndValues(Key k) { StringBuffer sb = new StringBuffer("("); int attrsInKey = k.getAttributeCount(); for (int i = 0; i < attrsInKey; i++) { if (i > 0) sb.append(","); sb.append(k.getAttributeValues()[i]); } sb.append(")"); return sb.toString(); } }
Running the example produces the results shown in the following example. Notice that the serialized string format of a key is a hexadecimal number that includes information in a single string that represents all the attributes in a key.
Key (100) has string format 000100000003313030 Key (100,156) has string format 000200000003C2020200000002C102 Key (100,157) has string format 000200000003C2020200000002C102 ...
What You May Need to Know About Using Partial Keys with findByKey()
View objects based on multiple entity usages support the ability to find view rows by specifying a partially populated key. A partial key is a multi-attribute Key
object with some of its attributes set to null
. However, there are strict rules about what kinds of partial keys can be used to perform the findByKey()
.
If a view object is based on n entity usages, where n > 1, then the view row key is by default comprised of all of the primary key attributes from all of the participating entity usages. Only the ones from the first entity object are required to participate in the view row key, but by default all of them do.
If you allow the key attributes from some secondary entity usages to remain as key attributes at the view row level, then you should leave all of the attributes that form the primary key for that entity object as part of the view row key. Assuming you have left the one or more key attributes in the view row for m
of the n entity usages, where (m <= n), then you can use findByKey()
to find rows based on any subset of these m entity usages. Each entity usage for which you provide values in the Key
object, requires that you must provide non-null values for all of the attributes in that entity's primary key.
You have to follow this rule because when a view object is based on at least one or more entity usages, its findByKey()
method finds rows by delegating to the findByPrimaryKey()
method on the entity definition corresponding to the first entity usage whose attributes in the view row key are non-null. The entity definition's findByPrimaryKey()
method requires all key attributes for any given entity object to be non-null in order to find the entity row in the cache.
As a concrete example, imagine that you have a OrderInfoVO
view object with a OrderEO
entity object as its primary entity usage, and an AddressEO
entity as secondary reference entity usage. Furthermore, assume that you leave the Key Attribute property of both of the following view row attributes set to true
:
-
OrderId
— primary key for theOrderEO
entity -
AddressId
— primary key for theAddressEO
entity
The view row key will therefore be the (OrderId
, AddressId
) combination. When you do a findByKey()
, you can provide a Key
object that provides:
-
A completely specified key for the underlying
OrderEO
entityKey k = new Key(new Object[]{new Number(200), null});
-
A completely specified key for the underlying
AddressEO
entityKey k = new Key(new Object[]{null, new Number(118)});
-
A completely specified key for both entities
Key k = new Key(new Object[]{new Number(200), new Number(118)});
When a valid partial key is specified, the findByKey()
method can return multiple rows as a result, treating the missing entity usage attributes in the Key object as a wildcard.
How to Authenticate Test Users in the Test Client
If you have enabled ADF Security for your application and provisioned the jazn-data.xml
file with test users, you will need to include method calls to authenticate a user before you run the test client. To authenticate a user in the test client, follow these basic steps (as illustrated in the following exxample):
-
Create the authentication service.
-
Pass in the login credentials for a test user defined in the
jazn-data.xml
file. -
If authentication succeeds, then test the application module.
-
Log the user out.
For details about how to run the Configure ADF Security wizard to enable ADF Security and how to create test users in JDeveloper's identity store, see ADF Security Process Overview.
package oracle.summit.model.test; import oracle.jbo.ApplicationModule; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.adf.share.security.AuthenticationService; import oracle.adf.share.security.authentication.AuthenticationServiceUtil; import oracle.adf.share.ADFContext; import oracle.adf.share.security.SecurityContext; import oracle.adf.share.security.SecurityEnv; import javax.security.auth.Subject; public class TestAuthenticationClient { public static void main(String[] args) { String amDef = "test.TestAuthModule"; String config = "TestAuthModuleLocal"; // 1. Create authentication service. AuthenticationService authService = AuthenticationServiceUtil.getAuthenticationService(); try { // 2. Pass in user id and password defined in local identity store. authService.login("tester1", "welcome1"); } catch (Exception e) { } // Uncomment to output authentication status // String userName = ADFContext.getCurrent().getSecurityContext().getUserName(); // System.out.println("*** userName : " + userName); // Subject subject = ADFContext.getCurrent().getSecurityContext().getSubject(); // System.out.println("Subject : " + subject); // 3. Test application module if authentication succeeds. if (ADFContext.getCurrent().getSecurityContext().isAuthenticated()) { ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); ViewObject vo = am.findViewObject("TestView"); // Work with your appmodule and view object here Configuration.releaseRootApplicationModule(am,true); // 4. Log out test user. authService.logout(); // Uncomment to report logout success // boolean isAuthenticated = ADFContext.getCurrent().getSecurityContext().isAuthenticated(); // System.out.println("*** isAuthenticated : " + isAuthenticated); } } }