This chapter explains the key ADF entity object features for implementing the most common kinds of validation rules in an ADF application.
This chapter includes the following sections:
Section 7.3, "Adding Validation Rules to Entity Objects and Attributes"
Section 7.4, "Using the Built-in Declarative Validation Rules"
Section 7.5, "Using Groovy Expressions For Validation and Business Rules"
Section 7.8, "Setting the Severity Level for Validation Exceptions"
The easiest way to create and manage validation rules is through declarative validation rules. Declarative validation rules are defined using the overview editor, and once created, are stored in the entity object's XML file. Declarative validation is different from programmatic validation (covered in Chapter 8, "Implementing Validation and Business Rules Programmatically"), which is stored in an entity object's Java file.
Oracle ADF provides built-in declarative validation rules that satisfy many of your business needs. If you have custom validation rules you want to reuse, you can code them and add them to the IDE, so that the rules are available directly from JDeveloper. Custom validation rules are an advanced topic and covered in Section 38.9, "Implementing Custom Validation Rules." You can also base validation on a Groovy expression, as described in Section 7.5, "Using Groovy Expressions For Validation and Business Rules."
When you add a validation rule, you supply an appropriate error message and can later translate it easily into other languages if needed. You can also define how validation is triggered and set the severity level.
One benefit of using declarative validation (versus writing your own validation) is that the validation framework takes care of the complexities of batching validation exceptions, which frees you to concentrate on your application's specific validation rule logic.
Note:
It is possible to go beyond the declarative behavior to implement more complex validation rules for your business domain layer when needed. Section 8.2, "Using Method Validators" explains how to use the Method validator to invoke custom validation code and Section 38.9, "Implementing Custom Validation Rules" details how to extend the basic set of declarative rules with custom rules of your own.In an ADF Business Components application, most of your validation code is defined in your entity objects. Encapsulating the business logic in these shared, reusable components ensures that your business information is validated consistently in every view object or client that accesses it, and it simplifies maintenance by centralizing where the validation is stored.
In the model layer, ADF Model validation rules can be set for the attributes of a collection. Many of the declarative validation features available for entity objects are also available at the model layer, should your application warrant the use of model-layer validation in addition to business-layer validation.
When you use the ADF Business Components application module data control, you do not need to use model-layer validation. Consider defining all or most of your validation rules in the centralized, reusable, and easier to maintain entity objects of your business layer. With other types of data controls, model-layer validation can be more useful.
Each entity row tracks whether or not its data is valid. When an existing entity row is retrieved from the database, the entity is assumed to be valid. When the first persistent attribute of an existing entity row is modified, or when a new entity row is created, the entity is marked invalid.
When an entity is in an invalid state, the declarative validation you have configured and the programmatic validation rules you have implemented are evaluated again before the entity can be considered valid again. You can determine whether a given entity row is valid at runtime by calling the isValid()
method on it.
Note:
Because attributes can (by default) be left blank, validations are not triggered if the attribute contains no value. For example, if a user creates a new entity row and does not enter a value for a given attribute, the validation on that attribute is not run. To force the validation to execute in this situation, set the Mandatory flag on the attribute.Entity object validation rules fall into two basic categories: attribute-level and entity-level.
Attribute-level validation rules are triggered for a particular entity object attribute when either the end user or the program code attempts to modify the attribute's value. Since you cannot determine the order in which attributes will be set, attribute-level validation rules should be used only when the success or failure of the rule depends exclusively on the candidate value of that single attribute.
The following examples are attribute-level validations:
The value of the OrderDate
of an order should not be a date in the past.
The ProductId
attribute of a product should represent an existing product.
All other kinds of validation rules are entity-level validation rules. These are rules whose implementation requires considering two or more entity attributes, or possibly composed children entity rows, in order to determine the success or failure of the rule.
The following examples are entity-level validations:
The value of the OrderShippedDate
should be a date that comes after the OrderDate
.
The ProductId
attribute of an order should represent an existing product.
Entity-level validation rules are triggered by calling the validate()
method on a Row
. This occurs when:
You call the method explicitly on the entity object
You call the method explicitly on a view row with an entity row part that is invalid
A view object's iterator calls the method on the current row in the view object before allowing the current row to change
During transaction commit, processing validates an invalid entity (in the list of pending changes) before proceeding with posting the changes to the database
As part of transaction commit processing, entity-level validation rules can fire multiple times (up to a specified limit). For more information, see Section 7.2.4, "Avoiding Infinite Validation Cycles."
Transaction commit processing happens in three basic phases:
Ensure that any invalid entity rows on the pending changes list are valid.
Post the pending changes to the database by performing appropriate DML operations.
Commit the transaction.
If you have business validation logic in your entity objects that executes queries or stored procedures that depend on seeing the posted changes in the SELECT
statements they execute, they should be coded in the beforeCommit()
method described in Section 8.5.3, "What You May Need to Know About Row Set Access with View Accessors." This method fires after all DML statements have been applied so queries or stored procedures invoked from that method can "see" all of the pending changes that have been saved, but not yet committed.
Caution:
don't use the transaction-levelpostChanges()
method in web applications unless you can guarantee that the transaction will definitely be committed or rolled-back during the same HTTP request. This method exists to force the transaction to post unvalidated changes without committing them. Failure to heed this advice can lead to strange results in an environment where both application modules and database connections can be pooled and shared serially by multiple different clients.Because a composed child entity row is considered an integral part of its composing parent entity object, any change to composed child entity rows causes the parent entity to be marked invalid. For example, if a line item on an order were to change, the entire order would now be considered to be changed, or invalid.
Therefore, when the composing entity is validated, it causes any currently invalid composed children entities to be validated first. This behavior is recursive, drilling into deeper levels of invalid composed children if they exist.
If your validation rules contain code that updates attributes of the current entity or other entities, then the act of validating the entity can cause that or other entities to become invalid. As part of the transaction commit processing phase that attempts to validate all invalid entities in the pending changes list, the transaction performs multiple passes (up to a specified limit) on the pending changes list in an attempt to reach a state where all pending entity rows are valid.
The maximum number of validation passes is specified by the transaction-level validation threshold setting. The default value of this setting is 10. You can increase the threshold count to greater than one if the entities involved contain the appropriate logic to validate themselves in the subsequent passes.
If after 10 passes, there are still invalid entities in the list, you will see the following exception:
JBO-28200: Validation threshold limit reached. Invalid Entities still in cache
This is a sign that you need to debug your validation rule code to avoid inadvertently invalidating entities in a cyclic fashion.
To change the validation threshold, use the SetValidationThreshold()
method as shown in Example 7-1. In this example, the new threshold is 12.
When an entity object's validation rules throw exceptions, the exceptions are bundled and returned to the client. If the validation failures are thrown by methods you've overridden to handle events during the transaction postChanges
processing, then the validation failures cause the transaction to roll back any database INSERT
, UPDATE
, or DELETE
statements that might have been performed already during the current postChanges
cycle.
Note:
The bundling of exceptions is the default behavior for ADF Model-based web applications, but not for Business Component Browser or Swing bindings. Additional configuration is required to bundle exceptions for the Business Component Browser or Swing clients.When an entity row is in memory, it has an entity state that reflects the logical state of the row. Figure 7-1 illustrates the different entity row states and how an entity row can transition from one state to another. When an entity row is first created, its status is New
. You can use the setNewRowState()
method to mark the entity as being Initialized
, which removes it from the transaction's list of pending changes until the user sets at least one of its attributes, at which time it returns to the New
state. This allows you to create more than one initialized row and post only those that the user modifies.
The Unmodified
state reflects an entity that has been retrieved from the database and has not yet been modified. It is also the state that a New
or Modified
entity transitions to after the transaction successfully commits. During the transaction in which it is pending to be deleted, an Unmodified
entity row transitions to the Deleted
state. Finally, if a row that was New
and then was removed before the transaction commits, or Unmodified
and then successfully deleted, the row transitions to the Dead
state.
You can use the getEntityState()
and getPostState()
methods to access the current state of an entity row in your business logic code. The getEntityState()
method returns the current state of an entity row with regard to the transaction, while the getPostState()
method returns the current state of an entity row with regard to the database after using the postChanges()
method to post pending changes without committing the transaction.
For example, if you start with a new row, both getEntityState()
and getPostState()
return STATUS_NEW
. Then when you post the row (before commit or rollback), the row will have entity state of STATUS_NEW
and a post state of STATUS_UNMODIFIED
. If you subsequently remove that row, the entity state will remain STATUS_NEW
because for the transaction the row is still new. But the post state will be STATUS_DEAD
.
An application module provides a feature called bundled exception mode which allows web applications to easily present a maximal set of failed validation exceptions to the end user, instead of presenting only the first error that gets raised. By default, the ADF Business Components application module pool enables bundled exception mode for web applications.
You typically will not need to change this default setting. However it is important to understand that it is enabled by default since it effects how validation exceptions are thrown. Since the Java language and runtime only support throwing a single exception object, the way that bundled validation exceptions are implemented is by wrapping a set of exceptions as details of a new "parent" exception that contains them. For example, if multiple attributes in a single entity object fail attribute-level validation, then these multiple ValidationException
objects will be wrapped in a RowValException
. This wrapping exception contains the row key of the row that has failed validation. At transaction commit time, if multiple rows do not successfully pass the validation performed during commit, then all of the RowValException
objects will get wrapped in an enclosing TxnValException
object.
When writing custom error processing code, you can use the getDetails()
method of the JboException
base exception class to recursively process the bundled exceptions contained inside it.
Note:
All the exception classes mentioned here are in theoracle.jbo
package.The process for adding a validation rule to an entity object is similar for most of the validation rules, and is done using the Add Validation Rule dialog. You can open this dialog from the overview editor by clicking the Add icon on the Business Rules page.
It is important to note that when you define a rule declaratively using the Add Validation Rule dialog, the rule definition you provide specifies the valid condition for the attribute or entity object. At runtime, the entry provided by the user is evaluated against the rule definition and an error or warning is raised if the entry fails to satisfy the specified criteria. For example, if you specify a Length validator on an attribute that requires it to be Less Than or Equal To 12
, the validation fails if the entry is more than 12 characters, and the error or warning is raised.
To add a declarative validation rule to an entity object, use the Business Rules page of the overview editor.
In the Application Navigator, double-click the desired entity object.
Click the Business Rules navigation tab on the overview editor.
Select the object for which you want to add a validation rule, and then click the Add icon.
To add a validation rule at the entity object level, select Entity.
To add a validation rule for an attribute, expand Attributes and select the desired attribute.
When you add a new validation rule, the Add Validation Rule dialog appears.
Select the type of validation rule desired from the Rule Type dropdown list.
Use the dialog settings to configure the new rule.
The controls will change depending on the kind of validation rule you select. For more information about the different validation rules, see Section 7.4, "Using the Built-in Declarative Validation Rules."
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Note:
For Key Exists and Method entity validators, you can also use the Validation Execution tab to specify the validation level.Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
The Business Rules page of the overview editor for entity objects displays the validation rules for an entity and its attributes in a tree control. To see the validation rules that apply to the entity as a whole, expand in the Entity node. To see the validation rules that apply to an attribute, expand the Attributes node and then expand the attribute.
The validation rules that are shown on the Business Rules page of the overview editor include those that you have defined as well as database constraints, such as mandatory or precision. To open a validation rule for editing, double-click the rule or select the rule and click the Edit icon.
When you add a validation rule to an entity object, JDeveloper updates its XML component definition to include an entry describing what rule you've used and what rule properties you've entered. For example, if you add a range validation rule to the DiscountAmount
attribute, this results in a RangeValidationBean
entry in the XML file, as shown in Example 7-2.
Example 7-2 Range Validation Bean
<Attribute Name="DiscountAmount" IsNotNull="true" ColumnName="DISCOUNT_AMOUNT" . . . <validation:RangeValidationBean xmlns="http://xmlns.oracle.com/adfm/validation" Name="DiscountAmount_Rule_0" ResId="DiscountAmount_RangeError_0" OnAttribute="DiscountAmount" OperandType="LITERAL" Inverse="false" MinValue="0" MaxValue="40"/> . . . </Attribute>
At runtime, the rule is enforced by the entity object based on this declarative information.
Declarative validation enforces both entity-level and attribute-level validation, depending on where you place the rules. Entity-level validation rules are enforced when a user tries to commit pending changes or navigates between rows. Attribute-level validation rules are enforced when the user changes the value of the related attribute.
The Unique Key validator (described in Section 7.4.1, "How to Ensure That Key Values Are Unique") can be used only at the entity level. Internally the Unique Key validator behaves like an attribute-level validator. This means that users see the validation error when they tab out of the key attribute for the key that the validator is validating. This is done because the internal cache of entities can never contain a duplicate, so it is not allowed for an attribute value to be set that would violate that. This check needs to be performed when the attribute value is being set because the cache consistency check is done during the setting of the attribute value.
Best Practice:
If the validity of one attribute is dependent on one or more other attributes, enforce this rule using entity validation, not attribute validation. Examples of when you would want to do this include the following:You have a Compare validator that compares one attribute to another.
You have an attribute with an expression validator that examines the value in another attribute to control branching in the expression to validate the attribute differently depending on the value in this other attribute.
You make use of conditional execution, and your precondition expression involves an attribute other than the one that you are validating.
Entity object validators are triggered whenever the entity, as a whole, is dirty. To improve performance, you can indicate which attributes play a role in your rule and thus the rule should be triggered only if one or more of these attributes are dirty. For more information on triggering attributes, see, Section 7.6, "Triggering Validation Execution."
The built-in declarative validation rules can satisfy many, if not all, of your business needs. These rules are easy to implement because you don't write any code. You use the user-interface tools to choose the type of validation and how it is used.
Built-in declarative validation rules can be used to:
Ensure that key values are unique (primary key or other unique keys)
Determine the existence of a key value
Make a comparison between an attribute and anything from a literal value to a SQL query
Validate against a list of values that might be a literal list, a SQL query, or a view attribute
Make sure that a value falls within a certain range, or that it is limited by a certain number of bytes or characters
Validate using a regular expression or evaluate a Groovy expression
Make sure that a value satisfies a relationship defined by an aggregate on a child entity available through an accessor
Validate using a validation condition defined in a Java method on the entity
The Unique Key validator ensures that primary key values for an entity object are always unique. The Unique Key validator can also be used for a non-primary-key attribute, as long as the attribute is defined as an alternate key. For information on how to define alternate keys, see Section 4.10.15, "How to Define Alternate Key Values."
Whenever any of the key attribute values change, this rule validates that the new key does not belong to any other entity object instance of this entity object class. (It is the business-logic tier equivalent of a unique constraint in the database.) If the key is found in one of the entity objects, a TooManyObjectsException
is thrown. The validation check is done both in the entity cache and in the database.
There is a slight possibility that unique key validation might not be sufficient to prevent duplicate rows in the database. It is possible for two application module sessions to simultaneously attempt to create records with the same key. To prevent this from happening, create a unique index in the database for any unique constraint that you want to enforce.
To ensure that a key value is unique:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select the Entity folder, and click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select UniqueKey.
In the Keys box, select the primary or alternate key.
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Best Practice:
While it is possible to add a precondition for a Unique Key validator, it is not a best practice. If a Unique Key validator fails to fire, for whatever reason, the cache consistency check is still performed and an error will be returned. It is generally better to add the validator and a meaningful error message.Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
When you use a Unique Key validator, a <UniqueKeyValidationBean>
tag is added to the entity object's XML file. Example 7-3 shows the XML for a Unique Key validator.
The Compare validator performs a logical comparison between an entity attribute and a value. When you add a Compare validator, you specify an operator and something to compare with. You can compare the following:
Literal value
When you use a Compare validator with a literal value, the value in the attribute is compared against the specified literal value. When using this kind of comparison, it is important to consider data types and formats. The literal value must conform to the format specified by the data type of the entity attribute to which you are applying the rule. In all cases, the type corresponds to the type mapping for the entity attribute.
For example, an attribute of column type DATE maps to the oracle.jbo.domain.Date
class, which accepts dates and times in the same format accepted by java.sql.TimeStamp
and java.sql.Date
. You can use format masks to ensure that the format of the value in the attribute matches that of the specified literal. For information about entity object attribute type mappings, see Section 4.10.1, "How to Set Database and Java Data Types for an Entity Object Attribute." For information about the expected format for a particular type, refer to the Javadoc for the type class.
Query result
When you use this type of validator, the SQL query is executed each time the validator is executed. The validator retrieves the first row from the query result, and it uses the value of the first column in the query (of that first row) as the value to compare. Because this query cannot have any bind variables in it, this feature should be used only when selecting one column of one row of data that does not depend on the values in the current row.
View object attribute
When you use this type of validator, the view object's SQL query is executed each time the validator is executed. The validator retrieves the first row from the query result, and it uses the value of the selected view object attribute from that row as the value to compare. Because you cannot associate values with the view object's named bind variables, those variables can only take on their default values. Therefore this feature should be used only for selecting an attribute of one row of data that does not depend on the values in the current row.
View accessor attribute
When defining the view accessor, you can assign row-specific values to the validation view object's bind variables.
Expression
For information on the expression option, see Section 7.5, "Using Groovy Expressions For Validation and Business Rules."
Entity attribute
The entity attribute option is available only for entity-level Compare validators.
To validate based on a comparison:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select where you want to add the validator.
To add an entity-level validator, select the Entity folder.
To add an attribute-level validator, expand the Attributes folder and select the appropriate attribute.
Click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select Compare. Note that the subordinate fields change depending on your choices.
Select the appropriate operator.
Select an item in the Compare With list, and based on your selection provide the appropriate comparison value.
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
Figure 7-2 shows what the dialog looks like when you use an entity-level Compare validator with a entity attribute.
When you use a Compare validator, a <CompareValidationBean>
tag is added to an entity object's XML file. Example 7-4 shows the XML code for the Email
attribute in the PersonEO
entity object.
The List validator compares an attribute against a list of values (LOV). When you add a List validator, you specify the type of list to choose from:
Literal values - The validator ensures that the entity attribute is in (or not in, if specified) the list of values.
Query result - The validator ensures that the entity attribute is in (or not in, if specified) the first column of the query's result set. The SQL query validator cannot use a bind variable, so it should be used only on a fixed, small list that you have to query from a table. All rows of the query are retrieved into memory.
View object attribute - The validator ensures that the entity attribute is in (or not in, if specified) the view attribute. The View attribute validator cannot use a bind variable, so it should be used only on a fixed, small list that you have to query from a table. All rows of the query are retrieved into memory.
View accessor attribute - The validator ensures that the entity attribute is in (or not in) the view accessor attribute. The view accessor is probably the most useful option, because it can take bind variables and after you've created the LOV on the user interface, a view accessor is required.
Best Practice:
When using a List validator, the view accessor is typically the most useful choice because you can define a view criteria on the view accessor to filter the view data when applicable; and when defining an LOV on a view attribute, you typically use a view accessor with a view criteria.To validate using a list of values:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select where you want to add the validator.
To add an entity-level validator, select the Entity folder.
To add an attribute-level validator, expand the Attributes folder and select the appropriate attribute.
Click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select List.
In the Attribute list, choose the appropriate attribute.
In the Operator field, select In or NotIn, depending on whether you want an inclusive list or exclusive.
In the List Type field, select the appropriate type of list.
Depending on the type of list you selected, you can either enter a list of values (each value on a new line) or an SQL query, or select a view object attribute or view accessor attribute.
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
Figure 7-3 shows what the dialog looks like when you use a List validator with a view accessor attribute.
When you validate using a list of values, a <ListValidationBean>
tag is added to an entity object's XML file. Example 7-5 shows the PaymentOptionEO.PaymentTypeCode
attribute, which uses a view accessor attribute for the List validator.
The List validator is designed for validating an attribute against a relatively small set of values. If you select the Query Result or View Object Attribute type of list validation, keep in mind that the validator retrieves all of the rows from the query before performing an in-memory scan to validate whether the attribute value in question matches an attribute in the list. The query performed by the validator's SQL or view object query does not reference the value being validated in the WHERE
clause of the query.
It is inefficient to use a validation rule when you need to determine whether a user-entered product code exists in a table of a large number of products. Instead, Section 8.5, "Using View Objects for Validation" explains the technique you can use to efficiently perform SQL-based validations by using a view object to perform a targeted validation query against the database. See also Section 5.12.10.2, "Using Validators to Validate Attribute Values."
Also, if the attribute you're comparing to is a key, the Key Exists validator is more efficient than validating a list of values; and if these choices need to be translatable, you should use a static view object instead of the literal choice.
The Range validator performs a logical comparison between an entity attribute and a range of values. When you add a Range validator, you specify minimum and maximum literal values. The Range validator verifies that the value of the entity attribute falls within the range (or outside the range, if specified).
If you need to dynamically calculate the minimum and maximum values, or need to reference other attributes on the entity, use the Script Expression validator and provide a Groovy expression. For more information, see Section 3.6.1, "Referencing Business Components Objects in Groovy Expressions" and Section 3.6.3, "Manipulating Business Component Attribute Values in Groovy Expressions."
To validate within a certain range:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select where you want to add the validator.
To add an entity-level validator, select the Entity folder.
To add an attribute-level validator, expand the Attributes folder and select the appropriate attribute.
Click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select Range.
In the Attribute list, select the appropriate attribute.
In the Operator field, select Between or NotBetween.
In the Minimum and Maximum fields, enter appropriate values.
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
When you validate against a range, a <RangeValidationBean>
tag is added to the entity object's XML file. Example 7-6 shows the PersonEO.CreditLimit
attribute with a minimum credit limit of zero and a maximum of 10,000.
The Length validator validates whether the string length (in characters or bytes) of an attribute's value is less than, equal to, or greater than a specified number, or whether it lies between a pair of numbers.
To validate against a number of bytes or characters:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select where you want to add the validator.
To add an entity-level validator, select the Entity folder.
To add an attribute-level validator, expand the Attributes folder and select the appropriate attribute.
Click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select Length.
In the Attribute list, select the appropriate attribute.
In the Operator field, select how to evaluate the value.
In the Comparison Type field, select Byte or Character and enter a length.
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
When you validate using length, a <LengthValidationBean>
tag is added to the entity object's XML file, as shown in Example 7-7. For example, you might have a field where the user enters a password or PIN and the application wants to validate that it is at least 6 characters long, but not longer than 10. You would use the Length validator with the Between operator and set the minimum and maximum values accordingly.
The Regular Expression validator compares attribute values against a mask specified by a Java regular expression.
If you want to create expressions that can be personalized in metadata, you can use the Script Expression validator. For more information, see Section 7.5, "Using Groovy Expressions For Validation and Business Rules."
To validate using a regular expression
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select where you want to add the validator.
To add an entity-level validator, select the Entity folder.
To add an attribute-level validator, expand the Attributes folder and select the appropriate attribute.
Click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select Regular Expression.
In the Operator field, you can select Matches or Not Matches.
To use a predefined expression (if available), you can select one from the dropdown list and click Use Pattern. Otherwise, write your own regular expression in the field provided.
Note:
You can add your own expressions to the list of predefined expressions. To add a predefined expression, add an entry in thePredefinedRegExp.properties
file in the BC4J subdirectory of the JDeveloper system directory (for example, C:\Documents and Settings\
username
\Application Data\JDeveloper\
system##
\o.BC4J\PredefinedRegExp.properties
).You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
Figure 7-4 shows what the dialog looks like when you select a Regular Expression validator and validate that the Email
attribute matches a predefined Email Address expression.
When you validate using a regular expression, a <RegExpValidationBean>
tag is added to the entity object's XML file. Example 7-8 shows an Email
attribute that must match a regular expression.
You can use collection validation on the average, count, sum, min, or max of a collection. This validator is available only at the entity level. It is useful for validating the aggregate calculation over a collection of associated entities by way of an entity accessor to a child entity (on the many end of the association). You must select the association accessor to define the Collection validator.
To validate using an aggregate calculation:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select the Entity folder and click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select Collection.
In the Operation field, specify the operation (sum, average, count, min, or max) to perform on the collection for comparison.
Select the appropriate accessor and attribute for the validation.
The accessor you choose must be a composition association accessor. Only accessors of this type are displayed in the dropdown list.
Specify the operator and the comparison type and value.
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
When you validate using a Collection validator, a <CollectionValidationBean>
tag is added to the entity object's XML file, as in Example 7-9.
The Key Exists validator is used to determine whether a key value (primary, foreign, or alternate key) exists.
There are a couple of benefits to using the Key Exists validator:
The Key Exists validator has better performance because it first checks the cache and only goes to the database if necessary.
Since the Key Exists validator uses the cache, it will find a key value that has been added in the current transaction, but not yet committed to the database. For example, you add a new Department
and then you want to link an Employee
to that new department.
To determine whether a value exists:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select where you want to add the validator.
To add an entity-level validator, select the Entity folder.
To add an attribute-level validator, expand the Attributes folder and select the appropriate attribute.
Click the Add icon.
In the Add Validation Rule dialog, select Key Exists from the Rule Type list.
Select the type of validation target (Entity Object, View Object, or View Accessor).
If you want the Key Exists validator to be used for all view objects that use this entity attribute, select Entity Object.
Depending on the validation target, you can choose either an association or a key value.
If you are searching for an attribute that does not exist in the Validation Target Attributes list, it is probably not defined as a key value. To create alternate keys, see Section 4.10.15, "How to Define Alternate Key Values."
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and the validation level (entity or transaction). For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
Figure 7-5 shows a Key Exists validator that validates whether the MembershipId
entered in the PersonEO
entity object exists in the MembershipBaseEO
entity object.
When you use a Key Exists validator, an <ExistsValidationBean>
tag is created in the XML file for the entity object, as in Example 7-10.
When using declarative validators you must consider how your validation will interact with expected input. The combination of declarative validators and view accessors provides a simple yet powerful alternative to coding. But, as powerful as the combination is, you still need to consider how data composition can impact performance.
Consider a scenario where you have the following:
A ServiceRequestEO
entity object with Product
and RequestType
attributes, and a view accessor that allows it to access the RequestTypeVO
view object
A RequestTypeVO
view object with a query specifying the Product
attribute as a bind parameter
The valid list of RequestType
s varies by Product
. So, to validate the RequestType
attribute, you use a List validator using the view accessor.
Now lets add a set of new service requests. For the first service request (row), the List validator binds the value of the Product
attribute to the view accessor and executes it. For each subsequent service request the List validator compares the new value of the Product
attribute to the currently bound value.
If the value of Product
matches, the current RowSet object is retained.
If the value of Product
has changed, the new value is bound and the view accessor re-executed.
Now consider the expected composition of input data. For example, the same products could appear in the input multiple times. If you simply validate the data in the order received, you might end up with the following:
Dryer (initial query)
Washing Machine (re-execute view accessor)
Dish Washer (re-execute view accessor)
Washing Machine (re-execute view accessor)
Dryer (re-execute view accessor)
In this case, the validator will execute 5 queries to get 3 distinct row sets. As an alternative, you can add an ORDER BY
clause to the RequestTypeVO
to sort it by Product
. In this case, the validator would execute the query only once each for Washing Machine and Dryer.
Dish Washer (initial query)
Dryer (re-execute view accessor)
Dryer
Washing Machine (re-execute view accessor)
Washing Machine
A small difference on a data set this size, but multiplied over larger data sets and many users this could easily become an issue. An ORDER BY
clause is not a solution to every issue, but this example illustrates how data composition can impact performance.
Groovy expressions are Java-like scripting code stored in the XML definition of an entity object. Because Groovy expressions are stored in XML, you can change the expression values even if you don't have access to the entity object's Java file. You can even change or specify values at runtime.
For more information about using Groovy script in your entity object business logic, see Section 3.6, "Overview of Groovy Support."
You can call methods on the current entity instance using the source
property of the current object. The source
property allows you to access to the entity instance being validated.
If the method is a non-boolean type and the method name is getXyzAbc()
with no arguments, then you access its value as if it were a property named XyzAbc
. For a boolean-valued property, the same holds true but the JavaBean naming pattern for the getter method changes to recognize isXyzAbc()
instead of getXyzAbc()
. If the method on your entity object does not match the JavaBean getter method naming pattern, or if it takes one or more arguments, then you must call it like a method using its complete name.
For example, say you have an entity object with the four methods shown in Example 7-11.
Example 7-11 Sample Entity Object Methods
public boolean isNewRow() { System.out.println("## isNewRow() accessed ##"); return true; } public boolean isNewRow(int n) { System.out.println("## isNewRow(int n) accessed ##"); return true; } public boolean testWhetherRowIsNew() { System.out.println("## testWhetherRowIsNew() accessed ##"); return true; } public boolean testWhetherRowIsNew(int n) { System.out.println("## testWhetherRowIsNew(int n) accessed ##"); return true; }
Then the following Groovy validation condition would trigger them all, one of them being triggered twice, as shown in Example 7-12.
Example 7-12 Groovy Script Calling Sample Methods
newRow && source.newRow && source.isNewRow(5) && source.testWhetherRowIsNew() && source.testWhetherRowIsNew(5)
By running this example and forcing entity validation to occur, you would see the diagnostic output shown in Example 7-13 in the log window:
Example 7-13 Output From Sample Groovy Script
## isNewRow() accessed ## ## isNewRow() accessed ## ## isNewRow(int n) accessed ## ## testWhetherRowIsNew() accessed ## ## testWhetherRowIsNew(int n) accessed ##
Notice the slightly different syntax for the reference to a method whose name matches the JavaBeans property getter method naming pattern. Both newRow
and source.newRow
work to access the boolean-valued, JavaBeans getter-style method that has no arguments. But because the testWhetherRowIsNew
method does not match the JavaBeans getter method naming pattern, and the second isRowNew
method takes an argument, then you must call them like methods using their complete name.
You can use a Groovy expression to return a true/false statement. The Script Expression validator requires that the expression either return true
or false
, or that it calls the adf.error.raise
/warn()
method. A common use of this feature would be to validate an attribute value, for example, to make sure that an account number is valid.
Note:
Using theadf.error.raise/warn()
method (rather than simply returning true
or false
) allows you to define the message text to show to the user, and to associate an entity-level validator with a specific attribute. For more information, see Section 7.7.3, "How to Conditionally Raise Error Messages Using Groovy."To validate using a true/false expression:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select where you want to add the validator.
To add an entity-level validator, select the Entity folder.
To add an attribute-level validator, expand the Attributes folder and select the appropriate attribute.
Click the Add icon.
In the Add Validation Rule dialog, in the Rule Type dropdown list, select Script Expression.
Enter a validation expression in the field provided.
You can optionally click the Validation Execution tab and enter criteria for the execution of the rule, such as dependent attributes and a precondition expression. For more information, see Section 7.6, "Triggering Validation Execution."
Click the Failure Handling tab and enter or select the error message that will be shown to the user if the validation rule fails. For more information, see Section 7.7, "Creating Validation Error Messages."
Click OK.
The sample code in Example 7-14 comes from the PaymentOptionEO
entity object. The code validates account numbers based on the Luhn algorithm, a checksum formula in widespread use.
Example 7-14 Validating an Account Number Using an Expression
<validation:ExpressionValidationBean Name="AccountNumber_Rule_0" OperandType="EXPR" Inverse="false"> <OnCondition> <![CDATA[PaymentTypeCode=='CC']]> </OnCondition> <MsgIds> <Item Value="PaymentOption_AccountNumber"/> </MsgIds> <TransientExpression> <![CDATA[ String acctnumber = newValue; sumofdigits = 0; digit = 0; addend = 0; timesTwo = false; range = acctnumber.length()-1..0 range.each {i -> digit = Integer.parseInt (acctnumber.substring (i, i + 1)); if (timesTwo) { addend = digit * 2; if (addend > 9) { addend -= 9; } } else { addend = digit; } sumofdigits += addend; timesTwo = !timesTwo; } modulus = sumofdigits % 10; return modulus == 0; ]]> </TransientExpression> </ExpressionValidationBean>
When you create a Groovy expression, it is saved in the entity object's XML component. Example 7-15 shows the RegisteredDate
attribute in the PersonEO.xml
file. The Groovy expression is wrapped by a <TransientExpression>
tag.
Example 7-15 XML Code for RegisteredDate Attribute on the PersonEO Entity Object
<Attribute
Name="RegisteredDate"
IsUpdateable="true"
ColumnName="REGISTERED_DATE"
Type="oracle.jbo.domain.Date"
ColumnType="DATE"
SQLType="DATE"
TableName="PERSONS">
<DesignTime>
<Attr Name="_DisplaySize" Value="7"/>
</DesignTime>
<validation:ExpressionValidationBean
Name="RegisteredDate_Rule_0"
OperandType="EXPR"
Inverse="false">
<MsgIds>
<Item
Value="RegisteredDate_Rule_0"/>
</MsgIds>
<TransientExpression>
<![CDATA[
newValue <= (new java.sql.Timestamp(System.currentTimeMillis()))
]]>
</TransientExpression>
</ExpressionValidationBean>
</Attribute>
This tag can take one of several forms. For some Groovy expressions, the <TransientExpression>
tag is wrapped by an <ExpressionValidationBean>
tag as well. Figure 7-6 shows the validation expression in the Edit Validation Rule dialog.
JDeveloper allows you to select the attributes that trigger validation, so that validation execution happens only when one of the triggering attributes is dirty. In previous releases of JDeveloper, an entity-level validator would fire on an attribute whenever the entity as a whole was dirty. This feature is described in Section 7.6.1, "How to Specify Which Attributes Fire Validation."
JDeveloper also allows you to specify a precondition for the execution of a validator (as described in Section 7.6.3, "How to Set Preconditions for Validation") and set transaction-level validation (described in Section 7.6.4, "How to Set Transaction-Level Validation").
When defining a validator at the entity level, you have the option of selecting one or more attributes of the entity object that, when changed, trigger execution of the validator.
Note:
When the validity of one attribute is dependent on the value in another attribute, the validation should be performed as entity validation, not attribute validation. You can set validation execution order on the entity level or attribute level.If you do not specify one or more dependent attributes, the validator will fire whenever the entity is dirty. Firing execution only when required makes your application more performant.
To specify which attributes fire validation:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select a validation rule and click the Edit icon.
In the Edit Validation Rule dialog, click the Validation Execution tab.
Select the attributes that will fire validation.
Click OK.
For example, in the StoreFront module of the Fusion Order Demo application, the OrderEO
entity object has an entity-level validator that constrains the length of the GiftwrapMessage
attribute. As shown in Figure 7-7, this validator is set to be executed on the entity object only when either the GiftwrapMessage
attribute or the GiftwrapFlag
attribute has been changed.
When you specify triggering attributes on the Validation Execution tab of the Edit Validation Rule dialog, JDeveloper adds an <OnAttributes>
tag to the validator definition in the entity object's XML file. Example 7-16 shows the XML code for the entity-level validator for the OrderEO
entity object in the StoreFront module of the Fusion Order Demo application.
Example 7-16 OnAttributes element in XML validation code
<LengthValidationBean xmlns="http://xmlns.oracle.com/adfm/validation" Name="OrderEO_Rule_0" OnAttribute="GiftwrapMessage" CompareType="GREATERTHANEQUALTO" DataType="CHARACTER" CompareLength="1" Inverse="false" ResId="GiftMessage_Required_Error_0"> <OnAttributes> <Item Value="GiftwrapMessage"/> <Item Value="GiftwrapFlag"/> </OnAttributes> <OnCondition> <![CDATA[GiftwrapFlag == 'Y']]> </OnCondition> </LengthValidationBean>
The Validation Execution tab (on the Add/Edit Validation Rule dialog) allows you to add a Groovy expression that serves as a precondition. If you enter an expression in the Conditional Execution Expression box, the validator is executed only if the condition evaluates True
.
Performing a validation during the transaction level (rather than entity level) means that the validation will be performed after all entity-level validation is performed. For this reason, it may be useful if you want to ensure that a validator is performed at the end of the process.
In addition, the Key Exists validator is more performant with bulk transactions if it is run as a transaction level validator since it will be run only once for all entities in the transaction (of the same type), rather than once per entity. This will result in improved performance if the validator has to go to the database.
Note:
Transaction-level validation is only applicable to Key Exists and Method entity validators.To specify entity-level or transaction-level validation:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select an entity-level validation rule and click the Edit icon.
In the Edit Validation Rule dialog, click the Validation Execution tab.
Select Execute at Entity Level or Defer Execution to Transaction Level.
Click OK.
You cannot control the order in which attributes are validated – they are always validated in the order they appear in the entity definition. You can order validations for a given attribute (or for the entity), but you cannot reorder the attributes themselves.
Validation error messages provide important information for the user: the message should convey what went wrong and how to fix it.
When you create or edit a validation rule, enter text to help the user determine what caused the error.
To create validation error messages:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page of the overview editor, select a validation rule and click the Edit icon.
In the Edit Validation Rule dialog, click the Failure Handling tab.
In the Message Text field, enter your error message.
You can also define error messages in a message bundle file. To select a previously defined error message or to define a new one in a message bundle file, click the Select Message icon.
Note:
The Script Expression validator allows you to enter more than one error message. This is useful if the validation script conditionally returns different error or warning messages. For more information, see Section 7.7.3, "How to Conditionally Raise Error Messages Using Groovy."You can optionally include message tokens in the body of the message, and define them in the Token Message Expressions list.
Figure 7-8 shows the failure message for a validation rule in the PaymentOptionEO
entity object that contains message tokens. For more information on this feature, see Section 7.7.4, "How to Embed a Groovy Expression in an Error Message."
Click OK.
The error message is a translatable string and is managed in the same way as translatable UI control hints in an entity object message bundle class. To view the error message for the defined rule in the message bundle class, locate the String
key in the message bundle that corresponds to the ResId
property in the XML component definition entry for the validator. For example, Example 7-17 shows a message bundle where the NAME_CANNOT_BEGIN_WITH_U
key appears with the error message for the default locale.
Example 7-17 Message Bundle Contains Validation Error Messages
package devguide.advanced.customerrors; import java.util.ListResourceBundle; public class CustomMessageBundle extends ListResourceBundle { private static final Object[][] sMessageStrings = new String[][] { // other strings here {"NAME_CANNOT_BEGIN_WITH_U", "The name cannot begin with the letter u!"}, // other strings here }; // etc. }
Resource bundles can be created for your applications as a list resource bundle (as shown in Example 7-17), as a properties bundle, or as an XLIFF resource bundle. For more information about using translatable strings in a resource bundle, see Section 4.7, "Working with Resource Bundles."
You can use the adf.error.raise()
and adf.error.warn()
methods to conditionally raise one error message or another depending upon branching in the Groovy expression. For example, if an attribute value is x, then validate as follows, and if the validation fails, raise error messageA; whereas if the attribute value is y, then instead validate a different way and if validation fails, raise error messageB.
If the expression returns false
(versus raising a specific error message using the raise()
method), the validator calls the first error message associated with the validator.
The syntax of the raise()
method takes one required parameter (the msgId
to use from the message bundle), and optionally can take the attrName
parameter. If you pass in the AttrName
, the error is associated with that attribute even if the validation is assigned to the entity.
You can use either adf.error.raise()
or adf.error.warn()
methods, depending on whether you want to throw an exception, or whether you want processing to continue, as described in Section 7.8, "Setting the Severity Level for Validation Exceptions."
A validator's error message can contain embedded expressions that are resolved by the server at runtime. To access this feature, simply enter a named token delimited by curly braces (for example, {2}
or {errorParam}
) in the error message text where you want the result of the Groovy expression to appear.
After entering the token into the text of the error message (on the Failure Handling tab of the Edit Validation Rule dialog), the Token Message Expressions table at the bottom of the dialog displays a row that allows you to enter a Groovy expression for the token. Figure 7-8 shows the failure message for a validation rule in the PaymentOptionEO
entity object that contains message tokens.
The expressions shown in Figure 7-8 are Groovy expressions that return the labels of the specified fields. You can also use Groovy expressions to access attribute values and other business components objects. You can use the Groovy expression newValue
to return the entered value, as shown in the Rule validator for the RoutingIdentifier
attribute of the PaymentOptionEO
entity object in the StoreFront module of the Fusion Order Demo application.
The Groovy syntax to retrieve a value from a view accessor is accessorName
.currentRow.
AttributeName
. For example, the Groovy expression MyEmpAccessor.currentRow.Job
returns the value of the Job
attribute in the current row of the MyEmpAccessor
view accessor.
The Groovy expression can also be more complex, as in Example 7-18, which shows an expression in the error message for the List validation rule for the OwnerTypeCode
attribute in the AddressUsageEO
entity object.
Example 7-18 Groovy Script in the OwnerTypeCode Validation Error Message
def ownertypevalue = [] while ( AddressOwnerTypesVA.hasNext() ) { AddressOwnerTypesVA.next() ownertypevalue.add(AddressOwnerTypesVA.currentRow.Value) } return ownertypevalue
For more information about accessing business components objects using Groovy, see Section 3.6, "Overview of Groovy Support."
You can set the severity level for validation exceptions to two levels, Informational Warning and Error. If you set the severity level to Informational Warning, an error message will display, but processing will continue. If you set the validation level to Error, the user will not be able to proceed until you have fixed the error.
Under most circumstances you will use the Error level for validation exceptions, so this is the default setting. However, you might want to implement a Informational Warning message if the user has a certain security clearance. For example, a store manager may want to be able to make changes that would surface as an error if a clerk tried to do the same thing.
To set the severity level for validation exceptions, use the Failure Handling tab of the Add Validation Rule dialog.
To set the severity level of a validation exception:
In the Application Navigator, double-click the desired entity object.
On the Business Rules page, select an existing validation rule and click the Edit icon, or click the Add icon to create a new rule.
In the Edit/Add Validation Rule dialog, click the Failure Handling tab and select the option for either Error or Informational Warning.
Click OK.
To improve the performance of batch-load applications, such as data synchronization programs, the ADF framework employs bulk validation for primary keys (including alternate keys) and foreign keys.
When the Key Exists validator is configured to defer validation until the transaction commits, or when the rows are being updated or inserted through the process
XXX
methods of the ADF business components service layer, the validation cache is preloaded. This behavior uses the normal row-by-row derivation and validation logic, but uses validation logic that checks a memory cache before making queries to the database. Performance is improved by preloading the memory cache using bulk SQL operations based on the inbound data.