Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g (10.1.3.1.0) Part Number B25947-01 |
|
|
View PDF |
When you find yourself repeating the same sanity-checking validations on the values of similar attributes across multiple entity objects, you can save yourself time and effort by creating your own data types that encapsulate this validation. For example, imagine that across your business domain layer there are numerous entity object attributes that store strings that represent email addresses. One technique you could use to ensure that end-users always enter a valid email address everywhere one appears in your business domain layer is to:
Use a basic String
data type for each of these attributes
Add an attribute-level method validator with Java code that ensures that the String value has the format of a valid email address for each attribute
However, these approaches could get tedious quickly in a large application. Luckily, ADF Business Components offers an alternative that allows you to create your own EmailAddress
data type that represents email addresses. After centralizing all of the sanity-checking regarding email address values into this new custom data type, you can use the EmailAddress
as the type of every attribute in your application that represents an email address. By doing this, you make the intention of the attribute values more clear to other developers and simplify application maintenance by putting the validation in a single place. ADF Business Components calls these developer-created data types domains.
Note: The examples in this section refer to theSimpleDomains project in the AdvancedEntityExamples workspace. See the note at the beginning of this chapter for download instructions. Run the CreateObjectType.sql script in the Resources folder against the SRDemo connection to set up the additional database objects required for the project. |
Domains are Java classes that extend the basic data types like String
, Number
, and Date
to add constructor-time validation to insure the candidate value passes relevant sanity checks. They offer you a way to define custom data types with cross-cutting behavior such as basic data type validation, formatting, and custom metadata properties in a way that are inherited by any entity objects or view objects that use the domain as the Java type of any of their attributes.
To create a domain, use the Create Domain wizard. This is available in the New Gallery in the ADF Business Components category.
In step 1, on the Name panel specify a name for the domain and a package in which it will reside. To create a domain based on a simple Java type, leave the Domain for an Oracle Object Type unchecked.
In step 2, on the Settings panel, indicate the base type for the domain and the database column type to which it will map. For example, if you were creating a domain called ShortEmailAddress
to hold eight-character short email addresses, you would set the base type to String
and the Database Column Type to VARCHAR2(8)
. You can set other common attribute settings on this panel as well.
Then, click Finish to create your domain.
When you create a domain, JDeveloper creates its XML component definition in the subdirectory of your project's source path that corresponds to the package name you chose. For example, if you created the ShortEmailAddress
domain in the devguide.advanced.domains
package, JDeveloper would create the ShortEmailAddress.xml
file in the ./devguide/advanced/domains
subdirectory. A domain always has a corresponding Java class, which JDeveloper creates in the common
subpackage of the package where the domain resides. This means it would create the ShortEmailAddress.java
class in the devguide.advanced.domains.common
package. The domain's Java class is automatically generated with the appropriate code to behave in a way that is identical to one of the built-in data types.
{para}?>
Once you've created a domain in a project, it automatically appears among the list of available data types in the Attribute Type dropdown list in the entity object and view object wizards and editors as shown in Figure 26-1. To use the domain as the type of a given attribute, just pick it from the list.
Note: The entity-mapped attributes in an entity-based view object inherit their data type from their corresponding underlying entity object attribute, so if the entity attribute uses a domain type, so will the matching view object attribute. For transient or SQL-derived view object attributes, you can directly set the type to use a domain since it is not inherited from any underlying entity. |
Typically, the only coding task you need to do for a domain is to write custom code inside the generated validate()
method. Your implementation of the validate()
method should perform your sanity checks on the candidate value being constructed, and throw a DataCreationException
in the oracle.jbo
package if the validation fails.
In order to throw an exception message that is translatable, you can create a message bundle class similar to the one shown in Example 26-1. Create it in the same common
package as your domain classes themselves. The message bundle returns an array of {
MessageKeyString
,
TranslatableMessageString
}
pairs.
Example 26-1 Custom Message Bundle Class For Domain Exception Messages
package devguide.advanced.domains.common; import java.util.ListResourceBundle; public class ErrorMessages extends ListResourceBundle { public static final String INVALID_SHORTEMAIL = "30002"; public static final String INVALID_EVENNUMBER = "30003"; private static final Object[][] sMessageStrings = new String[][] { { INVALID_SHORTEMAIL, "A valid short email address has no @-sign or dot."}, { INVALID_EVENNUMBER, "Number must be even."} }; /** * Return String Identifiers and corresponding Messages * in a two-dimensional array. */ protected Object[][] getContents() { return sMessageStrings; } }
Since String
is a base JDK type, a domain based on a String
aggregates a private mData String
member field to hold the value that the domain represents. Then, the class implements the DomainInterface
expected by the ADF runtime, as well as the Serializable
interface, so the domain can be used in method arguments or returns types of ADF components custom client interfaces.
Example 26-2 shows the validate()
method for a simple ShortEmailAddress
domain class. It tests to make sure that the mData
value does not contains an at-sign or a dot, and if it does, then the method throws DataCreationException
referencing an appropriate message bundle and message key for the translatable error message.
Example 26-2 Simple ShortEmailAddress String-Based Domain Type with Custom Validation
public class ShortEmailAddress implements DomainInterface, Serializable { private String mData; // etc. /**Implements domain validation logic and throws a JboException on error. */ protected void validate() { int atpos = mData.indexOf('@'); int dotpos = mData.lastIndexOf('.'); if (atpos > -1 || dotpos > -1) { throw new DataCreationException(ErrorMessages.class, ErrorMessages.INVALID_SHORTEMAIL,null,null); } } // etc. }
Other simple domains based on a built-in type in the oracle.jbo.domain
package extend the base type as shown in Example 26-3. It illustrates the validate()
method for a simple Number-based domain called EvenNumber
that represents even numbers.
Example 26-3 Simple EvenNumber Number-Based Domain Type with Custom Validation
public class EvenNumber extends Number { // etc. /** * Validates that value is an even number, or else * throws a DataCreationException with a custom * error message. */ protected void validate() { if (getValue() % 2 == 1) { throw new DataCreationException(ErrorMessages.class, ErrorMessages.INVALID_EVENNUMBER,null,null); } } // etc. }
When you create a simple domain based on one of the basic data types, it is an immutable class. This just means that once you've constructed a new instance of it like this:
ShortEmailAddress email = new ShortEmailAddress("smuench");
You cannot change its value. If you want to reference a different short email address, you just construct another one:
ShortEmailAddress email = new ShortEmailAddress("bribet");
This is not a new concept since it's the same way that String
, Number
, and Date
classes behave, among others.
The Oracle database supports the ability to create user-defined types in the database. For example, you could create a type called POINT_TYPE
using the following DDL statement:
create type point_type as object ( x_coord number, y_coord number );
If you use user-defined types like POINT_TYPE
, you can create domains base on them, or you can reverse-engineer tables containing columns of object type to have JDeveloper create the domain for you.
To create a domain yourself, do the following in the Create Domain wizard:
In step 1 of the Create Domain wizard on the Name panel, check the Domain for an Oracle Object Type checkbox, then select the object type for which you want to create a domain from the Available Types list.
In step 2 on the Settings panel, use the Attribute dropdown list to switch between the multiple domain properties to adjust the settings as appropriate.
Click Finish
In addition to manually creating object type domains, when you use the Business Components from Tables wizard and select a table containing columns of an Oracle object type, JDeveloper automatically creates domains for those object types as part of the reverse-engineering process. For example, imagine you created a table like this with a column of type POINT_TYPE
:
create table interesting_points( id number primary key, coordinates point_type, description varchar2(20) );
If you create an entity object for the INTERESTING_POINTS
table in the Business Components from Tables wizard, then you will get both an InterestingPoints
entity object and a PointType domain. The latter will have been automatically created, based on the POINT_TYPE
object type, since it was required as the data type of the Coordinates
attribute of the InterestingPoints
entity object.
Unlike simple domains, object type domains are mutable. JDeveloper generates getter and setter methods into the domain class for each of the elements in the object type's structure. After changing any domain properties, when you set that domain as the value of a view object or entity object attribute, it is treated as a single unit. ADF does not track which domain properties have changed, only that a domain-valued attribute value has changed.
Note: Domains based on Oracle object types are useful for working programmatically with data whose underlying type is an oracle object type. They also can simplify passing and receiving structure information to stored procedures. However, support for working with object type domains in the ADF binding layer is complete, so it's not straightforward to use object domain-valued attributes in declaratively-databound user interfaces. |
After selecting a domain in the Application Navigator, you can quickly navigate to its implementation class by:
Choosing Go to Domain Class on the right-mouse context menu, or
Double-clicking on the domain class in the Structure Window
When you create a business components archive, as described in Section 25.7, "Working with Libraries of Reusable Business Components", the domain classes and message bundle files in the *.common subdirectories of your project's source path get packaged into the *CSCommon.jar
. They are classes that are common to both the middle-tier application server and to an eventual remote-client you might need to support.
You can define custom metadata properties on a domain. Any entity object or view object attribute based on that domain inherits those custom properties as if they had been defined on the attribute itself. If the entity object or view object attribute defines the same custom property, its setting takes precedence over the value inherited from the domain.
JDeveloper will enforce declarative settings you impose at the domain definition level cannot be made less restrictive in the Entity Object editor or View Object editor for an attribute based on the domain type. For example, if you define a domain to have its Updatable property set to While New, then when you use your domain as the Java type of an entity object attribute, you can set Updatable to be Never (more restrictive) but you cannot set it to be Always. Similarly, if you define a domain to be Persistent, you cannot make it transient later. When sensible for your application, set declarative properties for a domain to be as lenient as possible so you can later make them more restrictive as needed.