Skip Headers
Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers
10g (10.1.3.1.0)

Part Number B25947-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

5.11 Generating Custom Java Classes for a View Object

As you've seen, all of the basic querying functionality of a view object can be achieved without using custom Java code. Clients can retrieve and iterate through the data of any SQL query without resorting to any custom code on the view object developer's part. In short, for many read-only view objects, once you've defined the SQL statement, you're done. However, it's important to understand how to enable custom Java generation for a view object when your needs might require it. Appendix D, "Most Commonly Used ADF Business Components Methods" provides a quick reference to the most common code that you will typically write, use, and override in your custom view object and view row classes. Later chapters discuss specific examples of how the SRDemo application uses custom code in these classes as well.

5.11.1 How To Generate Custom Classes

To enable the generation of custom Java classes for a view object, use the Java page of the View Object Editor. As shown in Figure 5-30, there are three optional Java classes that can be related to a view object. The first two in the list are the most commonly used:

  • View object class, which represents the component that performs the query

  • View row class, which represents each row in the query result

Figure 5-30 View Object Custom Java Generation Options

Image of Java Generation Options page

5.11.1.1 Generating Bind Variable Accessors

When you enable the generation of a custom view object class, if you also select the Bind Variable Accessors checkbox, then JDeveloper generates getter and setter methods in your view object class. Since the Users view object had three named bind variables (TheName, LowUserId, and HighUserId), the custom UsersImpl.java view object class would have corresponding methods like this:

public Number getLowUserId() {...}
public void setLowUserId(Number value) {...}
public Number getHighUserId(){...}
public void setHighUserId(Number value) {...}
public String getTheName() {...}
public void setTheName(String value){...}

These methods allow you to set a bind variable with compile-time type-checking to ensure you are setting a value of the appropriate type. That is, instead of writing a line like this to set the value of the LowUserId:

vo.setNamedWhereClauseParam("LowUserId",new Number(150));

You can write the code like:

vo.setLowUserId(new Number(150));

You can see that with the latter approach, the Java compiler would catch a typographical error had you accidentally typed setLowUserName instead of setLowUserId:

// spelling name wrong gives compile error
vo.setLowUserName(new Number(150));

Or if you were to incorrectly pass a value of the wrong data type, like "ABC" instead of Number value:

// passing String where number expected gives compile error
vo.setLowUserId("ABC");

Without the generated bind variable accessors, an incorrect line of code like the following cannot be caught by the compiler:

// Both variable name and value wrong, but compiler cannot catch it
vo.setNamedWhereClauseParam("LowUserName","ABC");

It contains both an incorrectly spelled bind variable name, as well as a bind variable value of the wrong datatype. If you use the generic APIs on the ViewObject interface, errors of this sort will raise exceptions at runtime instead of being caught at compile time.

5.11.1.2 Generating View Row Attribute Accessors

When you enable the generation of a custom view row class, if you also select the Accessors checkbox, then JDeveloper generates getter and setter methods for each attribute in the view row. For the Users view object, the corresponding custom UsersRowImpl.java class would have methods like this generated in it:

public Number getUserId() {...}
public void setUserId(Number value) {...}
public String getEmail() {...}
public void setEmail(String value) {...}
public String getFirstName() {...}
public void setFirstName(String value) {...}
public String getLastName() {...}
public void setLastName(String value) {...}
public String getUserRole() {...}
public void setUserRole(String value) {...}

These methods allow you to work with the row data with compile-time checking of the correct datatype usage. That is, instead of writing a line like this to get the value of the UserId attribute:

Number userId = (Number)row.getAttribute("UserId");

you can write the code like:

Number userId = row.getUserId();

You can see that with the latter approach, the Java compiler would catch a typographical error had you accidentally typed UserIdentifier instead of UserId:

// spelling name wrong gives compile error
Number userId = row.getUserIdentifier();

Without the generated view row accessor methods, an incorrect line of code like the following cannot be caught by the compiler:

// Both attribute name and type cast are wrong, but compiler cannot catch it
String userId = (String)row.getAttribute("UserIdentifier");

It contains both an incorrectly spelled attribute name, as well as an incorrectly-typed cast of the getAttribute() return value. Using the generic APIs on the Row interface, errors of this kind will raise exceptions at runtime instead of being caught at compile time.

5.11.1.3 Exposing View Row Accessors to Clients

When enabling the generation of a custom view row class, if you choose to generate the view row attribute accessor, you can also optionally select the Expose Accessor to the Client checkbox. This causes an additional custom row interface to be generated which application clients can use to access custom methods on the row without depending directly on the implementation class. As you learned in Chapter 4, "Overview of ADF Business Components", having client code work with business service tier interfaces instead of concrete classes is a best practice which ensures that client code does not need to change when your server-side implementation does.

In the case of the Users view object, exposing the accessors to the client will generate a custom row interface named UsersRow. This interface is created in the common subpackage of the package in which the view object resides. Having the row interface allows clients to write code that accesses the attributes of query results in a strongly typed manner. Example 5-16 shows a TestClient3 sample client program that casts the results of the next() method to the UsersRow interface so that it can call getUserId() and getEmail().

Example 5-16 Simple Example of Using Client Row Interface with Accessors

package devguide.examples.client;
import devguide.examples.common.UsersRow;
import oracle.jbo.ApplicationModule;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.Number;
public class TestClient3 {
  public static void main(String[] args) {
    String amDef = "devguide.examples.UserService";
    String config = "UserServiceLocal";
    ApplicationModule am =
      Configuration.createRootApplicationModule(amDef, config);
    ViewObject vo = am.findViewObject("UserList");
    vo.executeQuery();
    while (vo.hasNext()) {
     // Cast next() to a strongly-typed UsersRow interface 
      UsersRow curUser = (UsersRow)vo.next();
      Number userId = curUser.getUserId();
      String email  = curUser.getEmail();
      System.out.println(userId+ " " + email);
    }
    Configuration.releaseRootApplicationModule(am, true);
  }
}

5.11.1.4 Configuring Default Java Generation Preferences

You've seen how to generate custom Java classes for your view objects when you need to customize their runtime behavior, or if you simply prefer to have strongly typed access to bind variables or view row attributes.

To configure the default settings for ADF Business Components custom Java generation, choose Tools | Preferences and open the Business Components page to set your preferences to be used for business components created in the future. Oracle recommends that developers getting started with ADF Business Components set their preference to generate no custom Java classes by default. As you run into specific needs, you can enable just the bit of custom Java you need for that one component. Over time, you'll discover which set of defaults works best for you.

5.11.2 What Happens When You Generate Custom Classes

When you choose to generate one or more custom Java classes, JDeveloper creates the Java file(s) you've indicated. For a view object named devguide.examples.Users, the default names for its custom Java files will be UsersImpl.java for the view object class and UsersRowImpl.java for the view row class. Both files get created in the same ./devguide/examples directory as the component's XML component definition file.

The Java generation options for the view object are continue to be reflected on the Java page on subsequent visits to the View Object Editor. Just as with the XML definition file, JDeveloper keeps the generated code in your custom java classes up to date with any changes you make in the editor. If later you decide you didn't require a custom Java file for any reason, unchecking the relevant options in the Java page causes the custom Java files to be removed.

5.11.2.1 Seeing and Navigating to Custom Java Files

As with all ADF components, when you select a view object in the Application Navigator, the Structure window displays all of the implementation files that comprise it. The only required file is the XML component definition file. You saw above that when translatable UI control hints are defined for a component, it will have a component message bundle file as well. As shown in Figure 5-31, when you've enabled generation of custom Java classes, they also appear under the Sources folder for the view object. When you need to see or work with the source code for a custom Java file, there are two ways to open the file in the source editor:

  • Choose the relevant Go to option in the context menu as shown in Figure 5-31

  • Double-click on a file in the Sources folder in the Structure window

Figure 5-31 Seeing and Navigating to Custom Java Classes for a View Object

Image of Structure window and context menu items

5.11.3 What You May Need to Know About Custom Classes

See the following sections for additional information to help you use custom Java classes.

5.11.3.1 About the Framework Base Classes for a View Object

When you use an "XML-only" view object, at runtime its functionality is provided by the default ADF Business Components implementation classes. Each custom Java class that gets generated will automatically extend the appropriate ADF Business Components base class so that your code inherits the default behavior and can easily add or customize it. A view object class will extend ViewObjectImpl, while the view row class will extend ViewRowImpl (both in the oracle.jbo.server package).

5.11.3.2 You Can Safely Add Code to the Custom Component File

Based perhaps on previous negative experiences, some developers are hesitant to add their own code to generated Java source files. Each custom Java source code file that JDeveloper creates and maintains for you includes the following comment at the top of the file to clarify that it is safe to add your own custom code to this file:

// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------

JDeveloper does not blindly regenerate the file when you click the OK or Apply button in the component editor. Instead, it performs a smart update to the methods that it needs to maintain, leaving your own custom code intact.

5.11.3.3 Attribute Indexes and InvokeAccessor Generated Code

As you've seen, the view object is designed to function either in an XML-only mode or using a combination of an XML component definition and a custom Java class. Since attribute values are not stored in private member fields of a view row class, such a class is not present in the XML-only situation. Instead, in addition to a name, attributes are also assigned a numerical index in the view object's XML component definition, on a zero-based, sequential order of the ViewAttribute and association-related ViewLinkAccessor tags in that file. At runtime, the attribute values in an view row are stored in a structure that is managed by the base ViewRowImpl class, indexed by the attribute's numerical position in the view object's attribute list.

For the most part this private implementation detail is unimportant. However, when you enable a custom Java class for your view row, this implementation detail is related to some of the generated code that JDeveloper automatically maintains in your view row class, and you may want to understand what that code is used for. For example, in the custom Java class for the Users view row, Example 5-17 shows that each attribute or view link accessor attribute has a corresponding generated integer constant. JDeveloper ensures that the values of these constants correctly reflect the ordering of the attributes in the XML component definition.

Example 5-17 Attribute Constants Are Automatically Maintained in the Custom View Row Java Class

public class UsersRowImpl extends ViewRowImpl implements UsersRow {
  public static final int USERID = 0;
  public static final int EMAIL = 1;
  public static final int FIRSTNAME = 2;
  public static final int LASTNAME = 3;
  public static final int USERROLE = 4;
  public static final int ASSIGNEDREQUESTS = 5;
  // etc.

You'll also notice that the automatically maintained, strongly typed getter and setter methods in the view row class use these attribute constants like this:

// In devguide.examples.UsersRowImpl class
public String getEmail() {
  return (String) getAttributeInternal(EMAIL); // <-- Attribute constant
}
public void setEmail(String value) {
  setAttributeInternal(EMAIL, value);// <-- Attribute constant
}

The last two aspects of the automatically maintained code related to view row attribute constants are the getAttrInvokeAccessor() and setAttrInvokeAccessor() methods. These methods optimize the performance of attribute access by numerical index, which is how generic code in the ViewRowImpl base class typically accesses attribute values. An example of the getAttrInvokeAccessor() method looks like the following from the ServiceRequestImpl.java class. The companion setAttrInvokeAccessor() method looks similar.

// In devguide.examples.UsersRowImpl class
protected Object getAttrInvokeAccessor(int index,AttributeDefImpl attrDef)
throws Exception {
  switch (index) {
  case USERID:           return getUserId();
  case EMAIL:            return getEmail();
  case FIRSTNAME:        return getFirstName();
  case LASTNAME:         return getLastName();
  case USERROLE:         return getUserRole();
  case ASSIGNEDREQUESTS: return getAssignedRequests();
  default:
    return super.getAttrInvokeAccessor(index, attrDef);
  }
}

The rules of thumb to remember about this generated attribute-index related code are the following.

The Do's
  • Add custom code if needed inside the strongly typed attribute getter and setter methods

  • Use the View Object Editor to change the order or type of view object attributes

    JDeveloper will change the Java signature of getter and setter methods, as well as the related XML component definition for you.

The Don'ts
  • Don't modify the getAttrInvokeAccessor() and setAttrInvokeAccessor() methods

  • Don't change the values of the attribute index numbers by hand


Note:

If you need to manually edit the generated attribute constants, perhaps due to source control merge conflicts, you must ensure that the zero-based ordering reflects the sequential ordering of the ViewAttribute and ViewLinkAccessor tags in the corresponding view object XML component definition.