JDeveloper
JDeveloper was created to help you design, develop, debug, and deploy Java code of different types and build an object layer that accesses the database. It offers a rich set of features for designing, developing, debugging, and deploying Java and other related files that are part of the Java 2 Platform, Enterprise Edition (J2EE). JDeveloper 10g contains many wizards and code generators that make it easier to implement complex functionality in Java, enabling you to concentrate on solving business problems when building web applications. It also offers strong code organization and configuration management. However, if you are just getting started with this tool, there is a lot to learn. This paper will offer some helpful tips and techniques that the author has gleaned from building real systems with this product that will hopefully be helpful to others.
I. Beginner Level Tips
The following tips can help you get started with your first few JDeveloper experiences.
A. Use the Internet
There are many helpful sources of information available over the
Internet:
· Use Google a lot!
·
Use OTN:
(i.e. how to get a check box to work in a JSP see link)
http://www.oracle.com/technology/products/jdev/tips/mills/adf_data_bound_checkbox.html
B. Use Naming Conventions
Consistently applied naming conventions are critical for the success of any system. Given the enormous number of elements available in the Java and JDeveloper environments, it is even more important to have a clearly defined set of conventions to follow. Creating naming conventions for your Java applications has several benefits:
1.
When you review your own or other people’s code, you
can quickly grasp the meaning of a particular element simply by seeing its
name.
2.
By knowing how elements are named, you can locate
specific elements more efficiently, making your applications easier to
maintain.
3.
Another benefit of using a naming convention is that it
frees you from having to re-create ways to name elements. By having a naming
framework, you
4.
Consistently named elements applied throughout an
organization also make it easier for developers to work on each other’s code.
The following are some naming conventions that the author has found useful in JDeveloper. Note that the type of object is often pre-pended on the instance name. This can help you locate things like buttons on a page, when you do not remember their exact names. (i.e. jButton_Save).
·
jPanel_main
·
borderlayout_contentPane
·
jTextField_firstName
·
jTextArea_description
New Java classes are always named with an initial capital letter and initial capitals on merged word transitions as shown in this example:
MyClass(String firstParameter_In, Date secondParameter_In)
Methods within a class are handled with an initial lower case letter, and the uppercase letter is used to indicate additional words as shown here:
· myMethod()
When naming Associations and Links in ADF Business Components, start with the parent-object, then add the word “To”, followed by the child object as shown here:
· (ParentToChild)
· DeptToEmpAssoc (this association would validate that Emp.dept_Id is a valid value in the Departments table)
· EmpToAddressLink (this link would provide a set address for current employee)
C. Generate “create” Methods for Entities
There are several ways to handle the creation of new rows in the database. However, unless you obtain an appropriate primary key at the time of creation in the middle tier, a number of subtle bugs may show up in your application. Therefore, it is recommended that you always check the box next to “Create Method” in the Entity Object Editor shown in Figure 2.
Figure 2. Entity Object Editor - Create Method checkbox
Find the “create” method in the “yourEntityImpl.java” class in the Structure pane and add the following code to get a sequence counter from the database to populate the primary key.
protected void create(AttributeList attributeList)
{
super.create(attributeList);
oracle.jbo.server.SequenceImpl s = new
oracle.jbo.server.SequenceImpl("myTable_seq",
getDBTransaction());
this.setMyTableId(s.getSequenceNumber());
}
D. Coding Shortcuts
There are many handy coding shortcuts that can speed up your code
writing in JDeveloper. For a complete listing see:
Tools | Preferences | Code Editor | Code
Templates | Shortcut (column)
1. Type “sop” and then press “Ctrl-Enter” to give you:
System.out.println();
2. To create a basic FOR LOOP, type “fori” and press “Ctrl-Enter to get:
for(int i=0; i< ; i++)
{
}
3. Use “Surround with” to add commonly used code snippets to any block of highlighted text. Right-click the highlighted block and then click “Surround with” to add snippets such as: try-catch, if, while, for, if-else, etc. around the selected block. Figure 1 shows some of the options for the Surround With menu choice.
Figure 1. Surround With
Making the selection shown in Figure 1 inserts the following code:
try
{
String x ="";
}
catch (Exception e)
{
}
4. Indent/shift blocks of code by highlighting the code and then pressing “Tab” or “Shift-Tab” to move the entire block.
E. Backup your entire workspace
Backup
your workspace, or be prepared to suffer the loss. Use any zipping tool to make
regular backups of your entire workspace.
Even with the best source control system you may implement, it is still
necessary to do this often. Typical
applications can run into hundreds of pages of generated and hand-written code,
which have large interdependencies. The
ability to step forward or back in time can be a life saver when things go
wrong.
II. Intermediate Level Tips
After you have had some practice building simple applications with JDeveloper, these tips and techniques may come in handy.
A. Use Views with INSTEAD OF Triggers
Although you may be tempted to build a middle-tier similar to the database with entities for each table of interest, this approach may not be very scaleable. Many BC4J projects have had to be rebuilt because they performed very poorly when there was significant data in the middle tier. An alternative approach uses ADF BC to build one or two objects per page against database views that handle the complex table joins for a given user application page. Inserts, updates and deletes can then be handled with INSTEAD OF Triggers on the database view object. Better code maintenance and middle tier performance are quickly achieved, and the database and client code can be developed and debugged independently by maintaining a single common point of interaction in database views representing the pages in the user application. Limit the use of entities to data that needs to be written back to the database. Entities require additional overhead and data caching, so it is wasteful to use them for static content.
Use a view object that directly points to the database for read-only data. You can implement almost any complex query in an ADF view object, but let the database store and execute your complex queries. Keep the ADF view objects simple. Focus on using one or two views per page, in the same way as described previously for entities. For example, using the emp_details_view to update manager_id for an employee from the HR Schema: (emp_details_view - joins five tables), you could create the following INSTEAD OF trigger:
CREATE OR REPLACE TRIGGER t_iu_emp_details_view
INSTEAD OF
UPDATE
ON emp_details_view
Referencing new as new and old as old
Begin
if updating ('manager_id') then
update employees set manager_id = :new.manager_id
where employee_id = :old.employee_id;
end if;
end;
B. Clearing the ADF BC Cache
It is important to keep the view and entity caches synchronized with the database. Before deleting a parent row, it is necessary to clear the ADF BC cache for child rows using the Remove method in the parent class. To access the remove method, check the box next to “Remove Method” in the Entity Object Editor. Then add custom code to spin through and remove any child rows, in the yourEntityImpl.java class.
Figure 3. Entity Object Editor - Remove Method checkbox
The following is an example of the code to remove the child rows:
public void remove()
{
getDBTransaction().executeCommand(
"Begin ste_rnd.p_DisconnectDigrmHookedClasses("
+ this.getDigrmId() +
" ); End;" );
//remove child rows
when deleting a Parent row
oracle.jbo.Row r;
this.getRmDDURClass().reset();
while
(this.getRmDDURClass().hasNext()){
r =
this.getRmDDURClass().next();
r.remove();
this.getDBTransaction().commit();
}
super.remove();
this.getDBTransaction().commit();
}//End remove()
C. Use JClient Layout classes
JClient Layout classes are designed to improve portability across operating systems and hardware variations such as monitor resolutions. Traditional client applications have been designed with rigid XYLayout values that were geared to a single pre-defined size/resolution on the client’s monitor. Each item would be set to a specific location, and given an exact size.
With the need for more portable solutions, JClient applications have a wide variety of Layout editors to dynamically accommodate wide differences in end-user preferences. Objects used to display information in JClient applications can be made to resize and reposition themselves as available screen real estate changes. Objects can “flow” around on the screen as widows are resized, or as data changes. For example, you can use the GridBagLayout to place information into cells in a grid or use the FlowLayout to arrange components into rows similar to a word-wrap feature in a word processor. Failure to take advantage of these new Layout classes will leave your client applications with an out-of-date look and feel.
D. Tips for Building ADF BC – JSP/Struts-Based Web Applications
Use the App-Module_Impl class to hold custom Java code. Expose custom methods to the client, using the Client Interface node in the Edit Entity wizard. (Do not put Java directly into the JSPs). The following code illustrates a custom method called getBrimErrorMessage added to the AppModuleImpl.java class to be used in a Struts data action.
public class AppModuleImpl extends ApplicationModuleImpl implements model.common.AppModule
{
// *********Add custom code here ********
public String getBrimErrorMessage()
{
//send data down to the database
this.getTransaction().postChanges();
//check for errors
this.getBrimErrorView().executeQuery();
if( this.getBrimErrorView().first()!=null &&
((BrimErrorViewRowImpl)this.getBrimErrorView().first()).getMessage()!=null &&
(((BrimErrorViewRowImpl)this.getBrimErrorView().first()).getMessage()).length()>0)
{
//refresh clean data from database on rule failure
int currentRowIndex = this.getPageOneEmpDetailsViewView().getCurrentRowIndex();
this.getPageOneEmpDetailsViewView().executeQuery();
this.getPageOneEmpDetailsViewView().setCurrentRowAtRangeIndex(currentRowIndex);
this.getTransaction().commit();
return "isError";
}//else no problems found - just return “success”
return "success";
}
Next, expose the code to the client by moving the method names to the Selected pane in the Application Module Editor shown in Figure 4.
Figure 4. Application Module Editor
Keep the code in the JSPs limited to presentation-only objects. Conditional logic belongs in Struts, and complex Java code should be placed in the AppModule or database.
E. Leverage “event_MyCustomCode” Forwarding in Struts
Leverage the “event_MyCustomCode” forwarding architecture that
Oracle provides in the enhanced Struts framework. Navigation for Events/Actions is automatically
handled for you when you name buttons and actions per Oracle’s new Struts
standards. As shown in Figure 5, this
snippet of Struts code shows how one of these (event_Submit) is used to navigate
to the forward named “Submit.” The DataPage in the upper left corner has a
Submit button named “event_Submit”. When
the button is pressed, data from the form is returned to
Figure 5. Example of Struts forward architecture
When the Struts controller sees /getBrimError, several things
happen. First, some custom code in the
appModule checks for any custom error messages that the database may have
returned. Next a string will be returned
via “methodResult” with the name of the appropriate forward. Thus the controller will be redirected back
to pageOneDP when “isError” is returned, or to the successPage when “success”
is returned. The code shown below overrides
the findForward method for /getBrimError to receive the transition passed back
in methodResult and use that to forward to the next page or action.
public class GetBrimErrorAction extends DataAction
{
protected void
findForward(DataActionContext actionContext) throws Exception
{
HttpServletRequest
request = actionContext.getHttpServletRequest();
if(request.getAttribute("methodResult")!=null
&&
((String)request.getAttribute("methodResult")).length()>0)
{
actionContext.setActionForward(actionContext.getActionMapping().findForward(
(String)request.getAttribute("methodResult")));
}
super.findForward(actionContext);
}
}
F. Clean up Generated Data Bindings after Deleting Objects from a Data Page
Remember to clean up any data bindings generated in Java classes named like “pageNameUIModel.xml” any time you delete data-bound-objects from the corresponding user interface pages. For example, when you drag-and-drop data bound objects onto a JSP page and later decide to remove them, you must do additional clean up outside of the JSP. Each data object that is added to a page has its model bindings generated in an XML file. If you add, delete, and re-add ManagerId to pageOne you will see multiple entries that start with “ManagerId”. To clean this up, right-click each of the “ManagerId”entries in the Structure Pane shown in Figure 6. Then click Delete. Finally, remove the object bound to ManagerId in the JSP and re-drag-and-drop to establish the correct binding.
Figure 6. Cleaning up the Data Bindings
G. Build Smart Objects in the Client User Interface
Build smart-objects in your client user interfaces that can be programmatically updated from the database (i.e. having a button’s displayed name come from the database.)
If you are stringing data actions to perform some task and you want to be able to access the view in a subsequent data action, make certain that you are using the same user interface model. Notice in the following struts-config.xml file that for each action, the property modelReference has the same value of addmgrrequestUIModel.
<action path="/savemgrrequest" className="oracle.adf.controller.struts.actions.DataActionMapping" type="oracle.adf.controller.struts.actions.DataAction" name="DataForm">
<set-property property="modelReference" value="addmgrrequestUIModel"/>
<set-property property="methodName" value="savemgrrequestUIModel.startToCreateMgrRequest"/>
<set-property property="resultLocation" value="${requestScope.methodResult}"/>
<set-property property="numParams" value="0"/>
<forward name="success" path="/loadimagefile.do"/>
</action>
<action path="/loadimagefile" className="oracle.adf.controller.struts.actions.DataActionMapping" type="oracle.adf.controller.struts.actions.DataAction" name="DataForm">
<set-property property="modelReference" value="addmgrrequestUIModel"/>
<set-property property="methodName" value="mgrrequestsUIModel.SetImageFile"/>
<set-property property="resultLocation" value="${requestScope.methodResult}"/>
<set-property property="numParams" value="2"/>
<set-property property="paramNames[0]" value="${bindings.RequestOid}"/>
<set-property property="paramNames[1]" value="${param.file1}"/>
<forward name="success" path="/mgrrequests.do"/>
</action>
III. Advanced Level Tips
As you become more familiar with the depth and features of
JDeveloper and have built more applications, the following suggestions may be
useful:
1. Communication between multiple Java processes is harder than between threads. A useful workaround is to utilize the file-system as a scratch pad to communicate between processes. Each process is set up to read and write to a given file. Synchronization occurs using a polling-type routine within each process. The shared file is tested at a given interval by each process. Thus, information can be developed and shared by two independent processes which normally could not communicate with each other by simply reading or writing the shared file.
2.
Use XML as a persistent-data store (using the Java
Architecture of XML Binding (jaxb) technology to read/write XML). You can automatically generate all of the
necessary Java classes to read and write data to well-formed XML files using
the XML Binding Compiler (xjc). The
generated classes will read, parse and write the data using simple object.get
and object.set methods: - i.e. PATIENT.setLASTNAME(“Smith”).
<?xml version="1.0" encoding="UTF-8"
standalone="yes" ?>
<PATIENTLIST>
<PATIENT>
<IDNUMBER>10000</IDNUMBER>
<LASTNAME>Smith</LASTNAME>
<FIRSTNAME>Bob</FIRSTNAME>
<DESCRIPTIONS>
<DESC
ROW="">hand trouble</DESC>
<DESC
<DESC
ROW="Group As">Hand "Type 2" trouble...</DESC>
This code represents the XML persistent data store, which is simply an XML file stored on your hard drive and it can be used with or without a database.
Conclusions
JDeveloper has a fairly steep learning curve. The many wizards
and editors in the tool can greatly expedite your application development.
However, it is important to use many of the other resources available
(Internet, OTN, Help files, Tutorials, books, etc.) to benefit from the
experiences of others. When possible, get a mentor who has been using the
product to build real systems to help guide your first few efforts.
About the Author
Dr.
Paul is the co-author of seven Oracle Press books that have been translated into nine languages:
·
Oracle
JDeveloper 10g Handbook
·
Oracle9i
JDeveloper Handbook
·
Oracle
JDeveloper 3 Handbook
·
Oracle
Designer Handbook (2 editions)
·
Oracle
Developer: Advanced Forms and Reports
·
Oracle8
Design Using UML Object Modeling – (the first book about UML data modeling
– published 1999)
Paul won the ODTUG 2003 Volunteer of the Year award, the Chris
Woolridge Outstanding Volunteer Award at