This chapter describes how to organize your ADF Business Components data model project to most efficiently share read-only data accessed from lookup tables or other static data source, such as a flat file. It describes the differences between ADF application modules that you may share at the application level and those that you may share at the session level.
This chapter includes the following sections:
Web applications often utilize data that is required across sessions and does not change very frequently. An example of this type of static data might be displayed in the application user interface in a lookup list. Each time your application accesses the static data, you could incur an unnecessary overhead when the static data caches are repopulated from the database for each application session on every request. In order to optimize performance, a common practice when working with ADF Business Components is to cache the shared static data for reuse across sessions and requests.
Declarative support for shared data caches is available in JDeveloper through the Project Properties dialog. Creating a shared application module allows requests from multiple sessions to share a single application module instance which is managed by an application pool for the lifetime of the web server virtual machine.
Use a shared application module to group view instances when you want to reuse lists of static data across the application. The shared application module can be configured to allow any user session to access the data or it can be configured to restrict access to just the UI components of a single user session. For example, you can use a shared application module to group view instances that access lookup data, such as a list of countries. The use of a shared application module allows all shared resources to be managed in a single place and does not require a scoped managed bean for this purpose.
As shown in Figure 10-1, the Project Properties dialog lets you specify application-level or session-level sharing of the application module's data model. In the case of application-level sharing, any HTTP user session will be able to access the same view instances contained in the shared application module. In contrast, the lifecycle of the session-level shared application module extends to an application module session (
SessionImpl) that is in use by a single HTTP user session and applies to a single root application module. In this case, each distinct root application module used by a given HTTP user session will get its own distinct instance of a session-scoped shared application module. In other words, distinct root application modules used by the same HTTP session do not share data in a session-scoped shared application module.
When you create the data model for the application module that you intend to share, be sure that the data in cached row sets will not need to be changed either at the application level or session level. For example, in the application-level shared application module, view instances should query only static data such as state codes or currency types. If a view object instance queries data that depends on the current user, then the query can be cached at the session level and shared by all components that reference the row-set cache. For example, the session-level shared application module might contain a view instance with data security that takes a manager as the current user to return the list of direct reports. In this case, the cache of direct reports would exist for the duration of the manager's HTTP user session. The ADF Business Components application module pool will recreate the session-scoped application module should an HTTP user session be assigned a recycled application module from the pool. This ensures that the duration of the session-scoped application module is tied to the HTTP session for as long as the HTTP session is able to continue to use the same root application module instance. Note that the cache of direct reports of the session-level shared application module cannot be accessed across distinct root application modules.
To create a shared application module instance, use the Project Properties dialog. You define a logical name for a distinct, separate root application module that will hold your application's read-only data.
Create the application module that you will share as described in Section 9.2.1, "How to Create an Application Module."
In the Application Navigator, right-click the project in which you want to create the shared application module and choose Project Properties.
In the Project Properties dialog, expand Business Components and select Application Module Instances.
In the Available Application Modules list, select the desired application module and shuttle it to the Application Module Instances list.
Assign the application module a unique instance name.
The shared application module instance (of either scope) must have a unique instance name. Supplying a meaningful name will also help to clarify which shared application module instance a given usage is referencing.
Select the Cache level of the shared application module:
When you want to define the shared application module for the context of the application, select Application.
When you want to define the shared application module for the context of the current user session, select Session.
JDeveloper automatically creates the
Shared configuration when you create an application module. The presence of this configuration in the
bc4j.xcfg file informs JDeveloper that the application module is a candidate to be shared, and allows JDeveloper to display the application module in the Available Application Modules list of the Project Properties dialog's Application Module Usage page.
Shared configuration sets these properties on the application module to enable sharing and help to maintain efficient use of the shared resource at runtime:
jbo.ampool.isuseexclusive is set to
false to specify that requests from multiple sessions can share a single instance of the application module, which is managed by the application pool for the lifetime of the web server virtual machine. When you do not enable application module sharing, JDeveloper sets the value
true to repopulate the data caches from the database for each application session on every request.
jbo.ampool.maxpoolsize is set to
1 (one) to specify that only a single application module instance will be created for the ADF Business Components application module pool. This setting enforces the efficient use of the shared application module resource and prevents unneeded multiple instances of the shared application module from being created at runtime.
You can view the shared application module's configuration by choosing Configurations from the context menu on the application module in the Application Navigator. JDeveloper saves the
bc4j.xcfg file in the
./common subdirectory relative to the application module's XML component definition. If you remove the configuration or modify the values of the
jbo.ampool runtime properties (
maxpoolsize), the application module will not be available to use as a shared application module instance.
For example, if you look at the
bc4j.xcfg file in the
./src/oracle/fodemo/storefront/lookups/common directory of the Fusion Order Demo application's
StoreFrontService project, you will see the two named configurations for the
LookupServiceAM application module, as shown in Example 10-1. Specifically, the
LookupServiceAMShared configuration sets the
jbo.ampool runtime properties on the shared application module instance. For more information about the ADF Business Components application module pooling and runtime configuration of application modules, see Chapter 41, "Tuning Application Module Pools and Connection Pools."
<BC4JConfig version="11.1" xmlns="http://xmlns.oracle.com/bc4j/configuration"> <AppModuleConfigBag ApplicationName="oracle.fodemo.storefront.lookups.LookupServiceAM"> <AppModuleConfig DeployPlatform="LOCAL" JDBCName="FOD" jbo.project="StoreFrontService" name="LookupServiceAMLocal" ApplicationName="oracle.fodemo.storefront.lookups.LookupServiceAM"> <Database jbo.locking.mode="optimistic"/> <Security AppModuleJndiName="oracle.fodemo.storefront.lookups.LookupServiceAM"/> </AppModuleConfig> <AppModuleConfig DeployPlatform="LOCAL" JDBCName="FOD" jbo.project="StoreFrontService" name="LookupServiceAMShared" ApplicationName="oracle.fodemo.storefront.lookups.LookupServiceAM"> <AM-Pooling jbo.ampool.dynamicjdbccredentials="false" jbo.ampool.isuseexclusive="false" jbo.ampool.maxpoolsize="1" jbo.ampool.resetnontransactionalstate="false"/> <Database jbo.locking.mode="optimistic"/> <Security AppModuleJndiName="oracle.fodemo.storefront.lookups.LookupServiceAM"/> </AppModuleConfig> </AppModuleConfigBag> </BC4JConfig>
Because the shared application module can be accessed by any Business Components project in the same application workspace, JDeveloper maintains the scope of the shared application module in the Business Components project configuration file (
.jpx). This file is saved in the
src directory of the project. For example, if you look at the
StoreFrontService.jpx file in the
./src directory of the Fusion Order Demo application's
StoreFrontService project, you will see that the
SharedLookupService application module's usage definition specifies
SharedScope = 2, corresponding to application-level sharing, as shown in Example 10-2. An application module that you set to session-level sharing will show
SharedScope = 1.
<JboProject xmlns="http://xmlns.oracle.com/bc4j" Name="StoreFrontService" SeparateXMLFiles="true" PackageName=""> . . . <AppModuleUsage Name="SharedLookupService" FullName="oracle.fodemo.storefront.lookups.LookupServiceAM" ConfigurationName="oracle.fodemo.storefront.lookups.LookupServiceAMShared" SharedScope="2"/> </JboProject>
Defining the shared application module in the Project Properties dialog makes the application module's data model available to other Business Components projects of the same application workspace only. When you want to make the data model available beyond the application workspace, you can publish the data model as an ADF Library, as described in Chapter 33, "Reusing Application Components."
When viewing a data control usage from the
DataBindings.cpx file in the Structure window, do not set the Configuration property to a shared application module configuration. By default, for an application module named AppModuleName, the Property Inspector will list the configurations named AppModuleNameShared and AppModuleNameLocal. At runtime, Oracle Application Development Framework (Oracle ADF) uses the shared configuration automatically when you configure an application as a shared application module, but the configuration is not designed to be used by an application module data control usage. For more information about data control usage, see Section 12.4, "Working with the DataBindings.cpx File."
In JDeveloper, you must define view accessors on the business component definition for the project that will permit access to view instances of the shared application module. The view accessor lets you point from an entity object or view object definition in one Business Components project to a view object definition or view instance in a shared application module. For details about creating view accessors for this purpose, see Section 10.4, "Accessing View Instances of the Shared Service."
Similar to the way application module pooling works in ADF Business Components, shared query collections are stored in a query collection pool. To manage the query collection pool, the ADF Business Components framework removes query collections based on a maximum idle time setting. This behavior limits the growth of the cache and prevents rarely-used query collections from occupying memory space.
As in application module and connection pooling, a query collection pool monitor wakes up after a user-specified sleep interval and then initiates the cleanup operation. Any query collection that exceeds the maximum idle time (length of time since it was last used), will be removed from the pool.
You can change the default values for the maximum idle time for the shared query collection (default is 900000 ms/15 min) and the sleep period for its pool monitor (default is 1800000 ms/30 min). To configure these values, open the Edit Business Components Configuration dialog, select the AppModuleNameShared configuration, and set these properties in the Properties page of the editor:
jbo.qcpool.monitorsleepinterval the time (ms) that the shared query collection pool monitor should sleep between pool checks.
jbo.qcpool.maxinactiveage the maximum amount of time (ms) that a shared query collection may remain unused before it is removed from the pool.
The default connection behavior for all application modules is to allow each root application module to have its own database connection. When your application defines more than one shared application module, you can change the default to optimize database connection usage by defining a named transaction for each root application module to use. The transaction name is an arbitrary string that you set on the
jbo.shared.txn property in the Properties page of the editor for the
bc4j.xcfg file of the root application module. At runtime, the root application modules with the same
jbo.shared.txn property setting (identified by the string you supply) will share the same database connection and entity cache. This optimization can reduce the database resources that the application uses and is particularly useful in shared application modules cases because they are read only and have longer life than transactional application modules.
Currently, the application module configuration parameter
jbo.doconnectionpooling=true is not supported for use with shared application modules. This feature is available to configure non-shared application modules when it is desirable to release JDBC connection objects to the database connection pool.
This feature is intentionally not supported for shared application modules to prevent decreases in performance that would result from managing state for shared access. Instead, the default use of
jbo.doconnectionpooling=false is enforced.
The default connection pooling configuration ensures that each shared application module instance holds onto the JDBC connection object that it acquires from the pool until the application module instance is removed from the application module pool. For more information about the
jbo.doconnectionpooling parameter and connection pool behavior, see Section 41.2.6, "What You May Need to Know About How Database and Application Module Pools Cooperate."
When your application needs to display static data, you can define a shared application module with view instances that most likely will access lookup tables. A lookup table is a static, translated list of data to which the application refers. Lookup table data can be organized in the database in various ways. While it is possible to store related lookup data in separate tables, it is often convenient to combine all of the lookup information for your application within a single table. For example, a column
LOOKUP_TYPE created for the
ORDERS_LOOKUPS table would serve to partition one table that might contain diverse codes such as
FWK_TBX_YES_NO for the values yes and no,
FWK_TBX_COUNTRY for country names, and
FWK_TBK_CURRENCY for the names of national currencies.
When your database schema organizes lookup data in a single database table, you want to avoid creating individual queries for each set of data. Instead, you will use the overview editor to define a single, base view object that maps the desired columns of the lookup table to the view object attributes you define. Since only the value of the
LOOKUP_TYPE column will need to change in the query statement, you can add view criteria on the view object definition to specify a
WHERE clause that will set the
LOOKUP_TYPE value. In this way, your application encapsulates access to the lookup table data in a single view object definition that will be easy to maintain when a
LOOKUP_TYPE value changes or your application needs to query additional lookup types.
The base view object that queries columns of the lookup table will be a read-only view object, since you do not need to handle updating data or require any of the benefits provided by entity-based view objects. (For a description of those benefits, see Section 5.1.2, "Runtime Features Unique to Entity-Based View Objects.")
While read-only view objects you create to access lookup tables are ideal for inclusion in a shared application module, if you intend to share the view object in a shared application module instance, you must create the view object in the same package as the shared application module.
To create a read-only view object, use the Create View Object wizard, which is available from the New Gallery.
In the Application Navigator, locate the shared application module you created in which you want to create the view object, right-click its package node, and choose New.
In the New Gallery, expand Business Tier, select ADF Business Components and then select View Object, and click OK.
In the Create View Object wizard, on the Name page, enter a package name and a view object name.
When naming the package, consider creating a separate package for the lookup.
Select Read-only access through SQL query to indicate that you want this view object to manage data with read-only access and click Next.
On the Query page, enter your SQL statement directly into the Query Statement box.
Your query names the columns of the lookup table, similar to the SQL statement shown in Figure 10-2 to query the
DESCRIPTION columns in the
After entering the query statement, no other changes are required. Click Next.
On the Bind Variables page, click Next.
On the Attribute Mappings page, note the mapped view object attribute names displayed and click Next.
By default, the wizard creates Java-friendly view object attribute names that correspond to the
SELECT list column names.
On the Attribute Settings page, from the Select Attribute dropdown, select the attribute that corresponds to the primary key of the queried table and then enable the Key Attribute checkbox.
Because the read-only view object is not based on an entity object, the Create View Object wizard does not define a key attribute by default. Failure to define the key attribute can result in unexpected runtime behavior for ADF Faces components with a data control based on the read-only view object collection. In the case of read-only view objects, define the key attribute, as shown in Figure 10-3.
If you want to rename individual attributes to use names that might be more appropriate, from the Select Attributes dropdown, choose the attribute and enter the desired name in the Name field. When you are finished, click Next.
For example, the Fusion Order Demo application renames the default attributes
Value respectively. Changes you make to the view object definition will not change the underlying query.
On the Java page, click Next.
On the Application Module page, do not add an instance of the view object to the application module data model. Click Finish.
The shared application module data model will include view instances based on view criteria that you add to the base view object definition. In this way, you do not need to create an individual view object to query each
LOOKUP_TYPE value. For details about adding the view object instances to the data model, see Section 18.104.22.168, "Adding Master-Detail View Object Instances to an Application Module."
When you create the view object definition for the lookup table, JDeveloper first describes the query to infer the following from the columns in the
The Java-friendly view attribute names (for example,
LookupType instead of
By default, the wizard creates Java-friendly view object attribute names that correspond to the
SELECT list column names.
The SQL and Java data types of each attribute
JDeveloper then creates the XML component definition file that represents the view objects's declarative settings and saves it in the directory that corresponds to the name of its package. For example, the XML file created for a view object named
LookupsBaseVO in the
lookups package is
./lookups/LookupsBaseVO.xml under the project's source path.
To view the view object settings, expand the desired view object in the Application Navigator, select the XML file under the expanded view object, and open the Structure Window. The Structure window displays the list of definitions, including the SQL query and the properties of each attribute. To open the file in the editor, double-click the corresponding
.xml node. As shown in Example 10-3, the
LookupsBaseVO.xml file defines one
<SQLQuery> definition and one
<ViewAttribute> definition for each mapped column. Without a view criteria to filter the query results, the view object query returns the
LOOKUP_DESCRIPTION and maps them to view instance attribute values for Value, Name, and Description respectively. Key attributes are defined to ensure proper row set navigation when the base view object collection is bound to an ADF Faces component.
<ViewObject xmlns="http://xmlns.oracle.com/bc4j" Name="LookupsBaseVO" BindingStyle="OracleName" CustomQuery="true" PageIterMode="Full" UseGlueCode="false" FetchMode="FETCH_AS_NEEDED" FetchSize="500"> <SQLQuery> <![CDATA[SELECT L.LOOKUP_TYPE ,L.LOOKUP_CODE ,L.MEANING ,L.DESCRIPTION FROM LOOKUP_CODES L WHERE L.LANGUAGE = USERENV('CLIENT_INFO') ORDER BY L.MEANING]]> </SQLQuery> <DesignTime> <Attr Name="_codeGenFlag2" Value="Access|VarAccess"/> <Attr Name="_isExpertMode" Value="true"/> </DesignTime> <ViewAttribute Name="Type" IsUpdateable="false" IsPersistent="false" IsNotNull="true" PrecisionRule="true" Precision="255" Type="java.lang.String" ColumnType="VARCHAR2" AliasName="LOOKUP_TYPE" Expression="LOOKUP_TYPE" SQLType="VARCHAR"> <DesignTime> <Attr Name="_DisplaySize" Value="30"/> </DesignTime> ... </ViewAttribute> <ViewAttribute Name="Value" IsUpdateable="false" IsPersistent="false" IsNotNull="true" PrecisionRule="true" Precision="30" Type="java.lang.String" ColumnType="VARCHAR2" AliasName="LOOKUP_CODE" Expression="LOOKUP_CODE" SQLType="VARCHAR"> <DesignTime> <Attr Name="_DisplaySize" Value="30"/> </DesignTime> ... </ViewAttribute> <ViewAttribute Name="Name" IsUpdateable="false" IsPersistent="false" IsNotNull="true" PrecisionRule="true" Precision="80" Type="java.lang.String" ColumnType="VARCHAR2" AliasName="MEANING" Expression="MEANING" SQLType="VARCHAR"> <DesignTime> <Attr Name="_DisplaySize" Value="80"/> </DesignTime> ... </ViewAttribute> <ViewAttribute Name="Description" IsUpdateable="false" IsPersistent="false" PrecisionRule="true" Precision="240" Type="java.lang.String" ColumnType="VARCHAR2" AliasName="DESCRIPTION" Passivate="true" Expression="DESCRIPTION" SQLType="VARCHAR"> <DesignTime> <Attr Name="_DisplaySize" Value="240"/> </DesignTime> ... </ViewAttribute> <AttrArray Name="KeyAttributes"> <Item Value="Type"/> <Item Value="Value"/> </AttrArray> . . . </ViewObject>
You create named view criteria definitions in the data model project when you need to filter view object results. View criteria that you define at design time can participate in UI scenarios that require filtering of data.
Use the Edit View Criteria dialog to create the view criteria definition for the lookup base view object you defined to query the lookup table. The editor lets you build a
WHERE clause using attribute name instead of the target view object's corresponding SQL column names. The resulting definition will include:
One view criteria row consisting of one view criteria group, with a single view criteria item used to define the lookup view object's
The view criteria item will consist of an
Type attribute name, the Equal operator, and the value of the
LOOKUP_TYPE that will filter the query results.
Because a single view criteria is defined, no logical conjunctions are needed to bracket the
WHERE clause conditions.
In the Application Navigator, double-click the lookup base view object you defined.
In the overview editor, click the Query navigation tab.
In the Query page, expand the View Criteria section, and click the Create new view criteria button.
In the Create View Criteria dialog, on the View Criteria page, click the Add Item button to add a single criteria item to the view criteria group.
In the Criteria Item panel, define the criteria item as follows:
Choose Type as the attribute (or other name that you defined for the attribute the view object maps to the
Choose equal to as the operator.
Keep Literal as the operand choice and enter the value name that defines the desired type. For example, to query the marital status codes, you might enter the value
MARITAL_STATUS_CODE corresponding to the
Leave all other settings unchanged.
The view object
WHERE clause shown in the editor should display a simple criteria similar to the one shown in Figure 10-4, where the value
MARITAL_STATUS_CODE is set to filter the
Repeat this procedure to define one view criteria for each
LOOKUP_TYPE that you wish to query.
The Create View Criteria dialog in JDeveloper lets you easily create view criteria and save them as named definitions. These named view criteria definitions add metadata to the target view object's own definition. Once defined, named view criteria appear by name in the Query page of the overview editor for the view object.
JDeveloper then creates the XML component definition file that represents the view objects's declarative settings and saves it in the directory that corresponds to the name of its package. For example, the XML file created for a view object named
LookupsBaseVO in the
lookups package is
./lookups/LookupsBaseVO.xml under the project's source path.
To view the view criteria, expand the desired view object in the Application Navigator, select the XML file under the expanded view object, open the Structure window, and expand the View Criteria node. As shown in Example 10-4, the
LookupsBaseVO.xml file specifies the
<ViewCriteria> definition that allows the
LookupsBaseVO to return only the marital types. Other view criteria added to the
LookupsBaseVO are omitted from this example for brevity.
<ViewObject xmlns="http://xmlns.oracle.com/bc4j" Name="LookupsBaseVO" BindingStyle="OracleName" CustomQuery="true" PageIterMode="Full" UseGlueCode="false"> <SQLQuery> <![CDATA[SELECT L.LOOKUP_TYPE ,L.LOOKUP_CODE ,L.MEANING ,L.DESCRIPTION FROM LOOKUP_CODES L WHERE L.LANGUAGE = SYS_CONTEXT('USERENV','LANG') ORDER BY L.MEANING]]> </SQLQuery> ... <ViewCriteria Name="listMaritalStatusTypes" ViewObjectName="oracle.fodemo.storefront.lookups.LookupsBaseVO" Conjunction="AND" Mode="3" <Properties> <CustomProperties> <Property Name="autoExecute" Value="false"/> <Property Name="showInList" Value="true"/> <Property Name="mode" Value="Basic"/> </CustomProperties> </Properties> <ViewCriteriaRow Name="vcrow24"> <ViewCriteriaItem Name="Type" ViewAttribute="Type" Operator="=" Conjunction="AND" Value="MARITAL_STATUS_CODE" Required="Optional"/> </ViewCriteriaRow> </ViewCriteria>
When you create a view instance based on a view criteria, the next time the view instance is executed it augments its SQL query with an additional
WHERE clause predicate corresponding to the view criteria that you've populated in the view criteria rows.
View accessors in ADF Business Components are value accessor objects that point from an entity object attribute (or view object) to a destination view object or shared view instance in the same application workspace. The view accessor returns a row set that by default contains all rows from the destination view object. You can optionally filter this row set by applying view criteria to the view accessor. The base entity object or view object on which you create the view accessor and the destination view object need not be in the same project or application module, but they must be in the same application workspace.
Because view accessors give you the flexibility to reach across application modules to access the queried data, they are ideally suited for accessing view instances of shared application modules. For details about creating a data model of view instances for a shared application module, see Section 10.2.1, "How to Create a Shared Application Module Instance."
This ability to access view objects in different application modules makes view accessors particularly useful for:
Validation rules that you set on the attributes of an entity object. In this case, the view accessor derives the validation rule's values from lookup data corresponding to a view instance attribute in the shared application module.
List of Value (LOV) that you enable for the attribute of any view object. In this case, the view accessor derives the list of values from lookup data corresponding to a view instance attribute in the shared application module.
Validation rules with accessors are useful when you do not want the UI to display a list of values to the user, but you still need to restrict the list of valid values. Alternatively, consider defining an LOV for view object attributes to simplify the task of working with list controls in the user interface. Because you define the LOV on the individual attributes of business components, you can customize the LOV usage for an attribute once and expect to see the list control in the form wherever the attribute appears.
Entity-based view objects inherit view accessors that you define on their base entity objects. Thus, defining the view accessor once on the entity object itself allows you to reuse the same view accessor, whether you want to define validation rules for entity object attributes or to create LOV-enabled attributes for that entity object's view object. However, when you do not anticipate using view accessors for validation rules, you can add the view accessor directly to the view object that defines the LOV-enabled attribute.
Oracle recommends creating unique view accessors whenever your application needs to expose an LOV-enabled attribute. Reusing view accessors to create multiple list of values is discouraged because LOV results are often filtered at runtime. For example, the results of a saved search will filter the row set of the target view object until the end user unapplies the search criteria. Consequently, view accessors that get applied to this same destination view object will have their results filter too. To ensure the view accessor always returns the intended row set at runtime, create unique view accessors for each usage.
Defining view accessors on the entity object should be used carefully since view objects that you create based on the entity object will inherit the view accessors of their base entity objects. While defining the view accessor once on the entity object itself allows you to reuse the same view accessor, the view accessor must not be used in different application scenarios. If you intend to define validation rules for the entity object attributes and create LOV-enabled attributes for that entity object's view object, it is recommended that you create separate view accessors.
For example, in the
StoreFrontModule package of the Fusion Order Demo application, the
AddressEO entity object defines the
Shared_CountriesVA view accessor and the
AddressesVO view object inherits this view accessor. In this case, defining the view accessor on the entity object is useful: the accessor for
AddressEO defines a validation rule on the
CountryId attribute. But a different view accessor for
AddressesVO should be used to enable an LOV on its
When you create a view accessor that accesses a view instance from a shared application module, you may want to use a prefix like
Shared_ to name the view accessor. This naming convention will help you identify the view accessor when you need to select it for the entity object or view object.
You can further refine the list returned by a view accessor by applying view criteria that you define on the view object. To create view criteria for use with a view accessor, see Section 10.3.3, "How to Define the WHERE Clause of the Lookup View Object Using View Criteria."
In the Application Navigator, double-click the entity object or view object on which you want to define the view accessor.
Whether you create the view accessor on the entity object or on the view object will depend on the view accessor's intended usage. Generally, creating view accessors on the entity object ensures the widest possible usage.
In the overview editor, click the View Accessors navigation tab and click the Create new view accessors button to add the accessor to the entity object or view object definition you are currently editing.
In the View Accessors dialog, select the view instance name you created for your lookup table from the shared application module node and shuttle it to the view accessors list.
For example, the View Accessors dialog in the Fusion Order Demo application shows the shared application module
LookupServiceAM with the list of view instances, as shown in Figure 10-5.
The dialog will display all view objects and view instances from your application. If you have not yet enabled application module sharing, you must do so before selecting the view instance. For details, see Section 10.2.1, "How to Create a Shared Application Module Instance."
By default, the view accessor you create will display the same name as the view object instance (or will have an integer appended when it is necessary to distinguish it from a child view object of the same name). You can edit Accessor Name to give it a unique name.
For example, the View Accessors dialog in Figure 10-5 shows the view accessor
SharedLookupService_AddressUsageTypesVA for the
AddressUsageTypes view instance selection in the shared application module
LookupServiceAM. This view accessor is created on the base entity object
AddressUsagesEO and accesses the row set of the
AddressUsageTypes view instance.
Optionally, if you want to apply an existing view criteria to filter the accessor, with the view accessor selected in the overview editor, click the Edit icon.
In the Edit View Accessor dialog, click Edit and perform the following steps to apply the view criteria:
Select the view criteria that you want to apply and shuttle it to the Selected list.
You can add additional view criteria to apply multiple filters (a logical AND operation will be performed at runtime).
Enter the attribute name for the bind variable that defines the controlling attribute for the view accessor row set.
Unlike view criteria that you set directly on a view object (to create a view instance, for example), the controlling attribute of the view accessor's view criteria derives the value from the view accessor's base view object.
Click OK to return to the View Accessors dialog.
View accessors that you create to access the view rows of a destination view object may be used to verify data that your application solicits from the end user at runtime. For example, when the end user fills out a registration form, individual validation rules can verify the title, marital status, and contact code against lookup table data queried by view instances of the shared application module.
You can apply view accessors you have defined on the entity object to these built-in declarative validation rules:
The Compare validator performs a logical comparison between an entity attribute and a value. When you specify a view accessor to determine the possible values, the compare validator applies the
GreaterOrEqualTo operator you select to compare against the values returned by the view accessor.
The List validator compares an entity attribute against a list of values. When you specify a view accessor to determine the valid list values, the List validator applies an
NotIn operator you select against the values returned by the view accessor.
The Collection validator performs a logical comparison between an operation performed on a collection attribute and a value. When you specify a view accessor to determine the possible values, the Collection validator applies the Sum, Average, Count, Min, Max operation on the selected collection attribute to compare against the values returned by the view accessor.
Validation rules that you define to allow runtime validation of data for entity-based view objects are always defined on the attributes of the entity object. You use the editor for the entity object to define the validation rule on individual attributes. Any view object that you later define that derives from an entity object with validation rules defined will automatically receive attribute value validation.
Create the desired entity objects as described in Section 4.2.1, "How to Create Multiple Entity Objects and Associations from Existing Tables."
In the Application Navigator, double-click the desired entity object.
In the overview editor, click the Business Rules navigation tab.
In the Business Rules page, expand the entity object, select the attribute to be validated and then click the Create new validator button to add the validation rule to the entity object attribute.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select Compare, List, or Collection.
Make the selections required by the validator selection.
In the Compare With or List Type dropdown list, select View Accessor Attribute.
In the Select View Accessor Attribute group box, expand the desired view accessor from the shared service and select the attribute you want to provide as validation.
Figure 10-6 shows what the dialog looks like when you use a List validator to select a view accessor attribute.
Click the Failure Handling tab and enter a message that will be shown to the user if the validation rule fails.
When you use a List validator, a
<ListValidationBean> tag is added to an entity object's XML file. Example 10-5 shows the XML code for the
CountryId attribute in the
Address entity object. A List validator has been used to validate the user's entry against the list of country ID values as retrieved by the view accessor from the
Countries view instance.
<Attribute Name="CountryId" IsNotNull="true" Precision="2" ColumnName="COUNTRY_ID" Type="java.lang.String" ColumnType="CHAR" SQLType="VARCHAR" TableName="ADDRESSES"> RetrievedOnUpdate="true" RetrievedOnInsert="true"> <DesignTime> <Attr Name="_DisplaySize" Value="2"/> </DesignTime> <ListValidationBean xmlns="http://xmlns.oracle.com/adfm/validation" Name="CountryId_Rule_1" ResId="CountryId_Rule_0" OnAttribute="CountryId" OperandType="VO_USAGE" Inverse="false" ViewAccAttrName="Value" ViewAccName="SharedCountriesVA"> <ResExpressions> <Expression Name="0"><![CDATA[SharedCountriesVA.Value]]> </Expression> </ResExpressions> </ListValidationBean> <Properties> <SchemaBasedProperties> <LABEL ResId="CountryId_LABEL"/> </SchemaBasedProperties> </Properties> </Attribute>
View accessors that you create to access the view rows of a destination view object may be used to display a list of values to the end user at runtime. You first create a view accessor with the desired view instance as its data source, and then you can add the view accessor to an LOV-enabled attribute of the displaying view object. You will edit the view accessor definition for the LOV-enabled attribute so that it points to the specific lookup attribute of the view instance. Because you want to populate the row set cache for the query with static data, you would locate the destination view instance in a shared application module.
While the list usage is defined on the attribute of a view object bound to a UI list control, the view accessor definition exists on either the view object or the view object's base entity object. If you choose to create the view accessor on the view object's entity object, the View Accessors page of the overview editor for the view object will display the inherited view accessor, as shown in Figure 10-7. Alternatively, if you choose to create the view accessor on the attribute's view object, you can accomplish this from either the editor for the LOV definition or from the View Accessors page of the overview editor.
For additional examples of how to work with LOV-enabled attributes, see Section 5.12, "Working with List of Values (LOV) in View Object Attributes."
Create the desired view objects as described in Section 5.2.1, "How to Create an Entity-Based View Object," and Section 5.2.3, "How to Create an Expert Mode, Read-Only View Object."
In the Application Navigator, right-click the view object that contains the desired attribute and choose Open ViewObjectName.
In the overview editor, click the View Accessors navigation tab.
In the View Accessors page, check to see whether the view object inherited the desired view accessor from its base entity object. If no view accessor is present, either create the view accessor on the desired entity object or click the Create new view accessors button to add the accessor to the view object you are currently editing.
Validation rules that you define are always defined on the attributes of the view object's base entity object. It may therefore be convenient to define view accessors at the level of the base entity objects when you know that you will also validate entity object attributes using a view accessor list.
For details about creating a view accessor, see Section 10.4.1, "How to Create a View Accessor for an Entity Object or View Object."
In the overview editor, click the Attributes navigation tab.
In the Attributes page, select the attribute that is to display the LOV, and then expand the List of Values section and click the Add list of values button.
In the List of Values dialog, select the view accessor from the List Data Source dropdown list.
The view accessor you select, will be the one created for the lookup table view object instances to use as the data source.
Select the attribute from this view accessor from the List Attribute dropdown list that will return the list of values for the attribute you are currently editing.
The editor creates a default mapping between the view object attribute and the LOV-enabled attribute. In this use case, the attributes are the same. For example, the attribute
OrderId from the
OrdersView view object would map to the attribute
OrderId from the
Shared_OrdersVA view accessor.
Optionally, when you want to specify supplemental values that your list returns to the base view object, click Add icon in List Return Values and map the desired view object attributes to the same attributes accessed by the view accessor. Supplemental attribute return values are useful when you do not require the user to make a list selection for the attributes, yet you want those attributes values, as determined by the current row, to participate in the update.
For example, to map the attribute
StartDate from the
OrdersView view object, you would choose the attribute
StartDate from the
Shared_OrdersVA view accessor. Do not remove the default attribute mapping for the attribute for which the list is defined.
When you add an LOV to a view object attribute, JDeveloper updates the view object's XML file with an
LOVName property in the
<ViewAttribute> element. The definition of the LOV appears in a new
<ListBinding> element. The metadata in Example 10-6 shows that the
MaritalStatusCode attribute refers to the
MaritalStatusLOV LOV and sets the
choice control type to display the LOV. The LOV definition for
MaritalStatusLOV appears in the
<ViewObject xmlns="http://xmlns.oracle.com/bc4j" Name="CustomerRegistrationVO" ... <ViewAttribute Name="MaritalStatusCode" IsNotNull="true" PrecisionRule="true" EntityAttrName="MaritalStatusCode" EntityUsage="PersonEO" AliasName="MARITAL_STATUS_CODE" LOVName="MaritalStatusCodeLOV"> <Properties> <SchemaBasedProperties> <CONTROLTYPE Value="choice"/> </SchemaBasedProperties> </Properties> </ViewAttribute> ... <ListBinding Name="MaritalStatusLOV" ListVOName="PersonEO.MaritalStatusVA" ListRangeSize="-1" NullValueFlag="start" NullValueId="LOVUIHints_NullValueId" MRUCount="0"> <AttrArray Name="AttrNames"> <Item Value="MaritalStatusCode"/> </AttrArray> <AttrArray Name="ListAttrNames"> <Item Value="Value"/> </AttrArray> <AttrArray Name="ListDisplayAttrNames"> <Item Value="Name"/> </AttrArray> <DisplayCriteria/> <AttrArray Name="DerivedAttrNames"/> </ListBinding> ... </ViewObject>
If you need to ensure that your view accessor always queries the latest data from the lookup table, you can set the Auto Refresh property on the destination view object. This property allows the view object instance to refresh itself after a change in the database. The typical use case is when you define a view accessor for the destination view object.
Because the auto-refresh feature relies on the database change notification feature, observe these restrictions when enabling auto-refresh for your view object:
The view objects should query as few read-only tables as possible. This will ensure the best performance and prevent the database invalidation queue from becoming too large.
The database user must have database notification privileges. For example, to accomplish this with a SQL*Plus command use
grant change notification to <user name>.
When these restrictions are observed, the refresh is accomplished through the Oracle database change notification feature. Prior to executing the view object query, the framework will use the JDBC API to register the query for database notifications. When a notification arrives, the row sets of the corresponding view object instance are marked for refresh during the next checkout of the application module. Because the shared application module waits until the next checkout, the row set currency of the current transaction is maintained and the end user is not hampered by the update.
For example, assume that an LOV displays a list of zip codes that is managed in read-only fashion by a database administrator. After the administrator adds a new zip code as a row to the database, the shared application module detects a time when there are no outstanding requests and determines that a pending notification exists for the view instance that access the list of zip codes; at that point, the view object refreshes the data and all future requests will see the new zip code.
In the Application Navigator, double-click the view object that you want to receive database change notifications.
In the Property Inspector expand the Tuning Database Retrieve section, and select True for the Auto Refresh property.
The ADF Business Components runtime adds functionality in the attribute setters of the view row and entity object to facilitate the LOV-enabled attribute behavior. In order to display the LOV-enabled attribute values in the user interface, the LOV facility fetches the data source, and finds the relevant row attributes and mapped target attributes.
Unlike entity-based view objects, read-only view objects that you create in expert mode, will not define a key attribute by default. While it is possible to create a read-only view object without defining its key attribute, in expert mode it is a best practice to select the attribute that corresponds to the queried table's primary key and mark it as the key attribute. The presence of a key attribute ensure the correct runtime behavior for row set navigation. For example, the user interface developer may create a LOV component based on the read-only view object collection. Without a key attribute to specify the row key value, the LOV may not behave properly and a runtime error can result.
When one view object extends another, you can create the LOV-enabled attribute on the base object. Then when you define the child view object in the overview editor, the LOV definition will be visible on the corresponding view object attribute. This inheritance mechanism allows you to define an LOV-enabled attribute once and apply it later across multiple view objects instances for the same attribute. For details about extending a view object from another view object definition, see Section 37.8.2, "How To Extend a Component After Creation."
You can also use the overview editor to extend the inherited LOV definition. For example, you may add extra attributes already defined by the base view object's query to display in selection list. Alternatively, you can create a view object instance that uses a custom
WHERE clause to query the supplemental attributes not already queried by the base view object. For information about customizing entity-based view objects, see Section 5.10, "Working with Bind Variables."
If you have created an LOV-enabled attribute for a view object, there is no need to validate the attribute using a List validator. You use an attribute validator only when you do not want the list to display in the user interface but still need to restrict the list of valid values. A List validator may be a simple static list or it may be a list of possible values obtained through a view accessor you define. Alternatively, you might prefer to use a Key Exists validator when the attribute displayed in the UI is one that references a key value (such as a primary, foreign, or alternate key). For information about declarative validation in ADF Business Components, see Chapter 7, "Defining Validation and Business Rules Declaratively."
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 Business Component Browser can often be the quickest way of exercising the data functionality of your business service during development.
The application module is the transactional component that the Business Component Browser (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. You can use the Business Component Browser to test that the accessors you defined yield the expected validation result and that they display the correct LOV attribute values.
To create an application module, use the Create Application Module wizard, which is available in the New Gallery. For more information, see Section 9.2, "Creating and Modifying an Application Module."
To test the view objects you added to an application module, use the Business Component Browser, which is accessible from the Application Navigator.
In the Application Navigator, expand the project containing the desired application module and view objects.
Right-click the application module and choose Run.
Alternatively, choose Debug when you want to run the application in the Business Component Browser with debugging enabled. For example, when debugging using the Business Component Browser, you can view status message and exceptions, step in and out of source code, and manage breakpoints. JDeveloper opens the debugger process panel in the Log window and the various debugger windows.
For details about receiving diagnostic messages specific to ADF Business Components debugging, see Section 6.3.8, "How to Enable ADF Business Components Debug Diagnostics."
In the Select Business Components Configuration dialog, choose the desired application module configuration from the Business Component Configuration Name list to run the Business Component Browser.
By default, an application module has only its default configurations, named
Shared. If you have created additional configurations for your application module and want to test it using one of those instead, just select the desired configuration from the Business Components Configuration dropdown list on the Connect dialog before clicking Connect.
Click Connect to start the application module using the selected configuration.
To execute a view object in the Business Component Browser, expand the tree list and double-click the desired view object node.
Note that the view object instance may already appear executed in the testing session. In this case, the tester panel on the right already displays query results for the view object instance, as shown in Figure 10-8. The fields in the tester panel of a read-only view object will always appear disabled since the data it represents is not editable.
To test the LOV you created for a view object attribute, use the Business Component Browser, which is accessible from the Application Navigator. For details about displaying the Browser and the supported control types, see Section 5.12.7, "How to Test LOV-Enabled Attributes Using the Business Component Browser."
When you launch the Business Component Browser, JDeveloper starts the tester tool in a separate process and the Business Component Browser appears. The tree at the left of the dialog displays all of the view object instances in your application module's data model. Figure 10-8 shows just one instance in the expanded tree, called
ProductImages. After you double-click the desired view object instance, the Business Component Browser will display a panel to inspect the query results, as shown in Figure 10-8.
The test panel will appear disabled for any read-only view objects you display because the data is not editable. But even for the read-only view objects, the tool affords some useful features:
You can validate that the UI hints based on the Label Text control hint and format masks are defined correctly.
You can also scroll through the data using the toolbar buttons.
The Business Component Browser becomes even more useful when you create entity-based view objects that allow you to simulate inserting, updating, and deleting rows, as described in Section 6.3.2, "How to Test Entity-Based View Objects Interactively."
When a shared application module with application scope is requested by an LOV, then the ADF Business Components runtime will create an
ApplicationPool object for that usage. There is only one
ApplicationPool created for each shared usage that has been defined in the Business Components project file (
.jpx). The runtime will then use that
ApplicationPool to acquire an application module instance that will be used like a user application module instance, to acquire data. The reference to the shared application module instance will be released once the application-scoped application module is reset. The module reference is released whenever you perform an unmanaged release or upon session timeout.
Since multiple threads will be accessing the data caches of the shared application module, it is necessary to partition the iterator space to prevent race conditions between the iterators of different sessions. This will help ensure that the next request from one session does not change the state of the iterator that is being used by another session. The runtime uses ADF Business Components support for multiple iterators on top of a single
RowSet to prevent these race conditions. So, the runtime will instantiate as many iterators as there are active sessions for each
An application-scoped shared application module lifecycle is similar to the lifecycle of any application module that is managed by the
ApplicationPool object. For example, once all active sessions have released their shared application module, then the application module may be garbage-collected by the
ApplicationPool object. The shared pool may be tuned to meet specific application requirements.
Session-scoped shared application modules are simply created as nested application module instances within the data model of the root, user application module. For details about nested application modules, Section 9.4, "Defining Nested Application Modules."