An existing SQL database such as Oracle will have a full range of table constraints, including primary keys, unique keys, foreign keys, not nulls and check constraints. JDeveloper's ADF Business Components (
The following post was raised on request for a client to explain the ADF BC facility provided. The discussion assumes you are familiar with Oracle's default schema "HR" and its associated table employees, and the constraints implemented in the employees table:
CREATE TABLE employees
(employee_id NUMBER(6,0)
,first_name VARCHAR2(20)
,last_name VARCHAR2(25) CONSTRAINT emp_last_name_nn NOT NULL
,email VARCHAR2(25) CONSTRAINT emp_email_nn NOT NULL
,phone_number VARCHAR2(20)
,hire_date DATE CONSTRAINT emp_hire_date_nn NOT NULL
,job_id VARCHAR2(10) CONSTRAINT emp_job_nn NOT NULL
,salary NUMBER(8,2)
,commission_pct NUMBER(2,2)
,manager_id NUMBER(6,0)
,department_id NUMBER(4,0)
,CONSTRAINT emp_salary_min CHECK (salary > 0)
,CONSTRAINT emp_email_uk UNIQUE (email)
,CONSTRAINT emp_emp_id_pk PRIMARY KEY (employee_id)
,CONSTRAINT emp_dept_fk FOREIGN KEY (department_id)
REFERENCES departments (department_id)
,CONSTRAINT emp_job_fk FOREIGN KEY (job_id)
REFERENCES jobs (job_id)
,CONSTRAINT emp_manager_fk FOREIGN KEY (manager_id)
REFERENCES employees (employee_id));
Overriding the database constraint error messages
The Oracle database isn't known for displaying particularly useful or friendly error messages when a table constraint is violated. For example if we create a new HR.employees record with an unknown DepartmentId value, the database throws the following error:
ORA-02291: integrity constraint (HR.EMP_DEPT_FK) violated - parent key not found
This message is not particular useful to the average user as they wont know what EMP_DEPT_FK is, what an integrity constraint is, or why the parent key is not found.
Via
Section "25.8.3 How to Customize Error Messages for Database Constraint Violations" of the "Application Development Framework Developer's Guide for Forms/4GL Developers" manual outlines that a standard ADF BC project can be extended with a custom message bundle to customise the database's constraint error messages when caught by the midtier. Note to implement this, as noted in section 25.8.3, you must follow steps 1 to 5 of section 25.8.1. Take care to follow the special Note in step 3 if you're using JDev 10.1.3+, though it appears unnecessary in JDev 11g.
If we consider the HR.employees tables named constraints, of which there are 4 not null constraints, 1
public class MessageBundle1 extends ListResourceBundle {
private static final Object[][] sMessageStrings =
{ "EMP_LAST_NAME_NN" ,"A not null error message0" }
,{ "EMP_EMAIL_NN" ,"A not null error message1" }
,{ "EMP_HIRE_DATE_NN" ,"A not null error message2" }
,{ "EMP_JOB_NN" ,"A not null error message3" }
,{ "EMP_SALARY_MIN" ,"A check error message4" }
,{ "EMP_EMAIL_UK" ,"A unique key error message5" }
,{ "EMP_EMP_ID_PK" ,"A primary key error message6" }
,{ "EMP_DEPT_FK" ,"A foreign key error message7" }
,{ "EMP_JOB_FK" ,"A foreign key error message8" }
,{ "EMP_MANAGER_FK" ,"A foreign key error message9" }};
return sMessageStrings;
}
}
The constraint names in the message bundle class must be in uppercase to match the case-specific constraint name raised by the database.
The HR schema has named the not null constraints within its tables explicitly. In your schema if you choose not to name the not null constraints, Oracle will generate a constraint name of the format SYS_C00000n where n is a counter incremented for each auto named constraint created by the database. It's fine to refer to these constraint names instead in your custom message bundle. However be aware if you recreate your schema and Oracle chooses to name the constraints starting at a different n value, obviously the ADF BC custom message bundle approach above will not work.
If you've setup your
The EO pk routine checks any created/updated pk value against all pk values for the existing EO that have been cached in the midtier at submit time. If it finds a matching value (or in other words that the values aren't unique) it throws the following JBO (JDeveloper) exception where X is the primary key value(s):
JBO-25013: Too many objects match the primary key oracle.jbo.Key[X ].
In this case you will not see your message, but the JBO error above.
EOs and their associated View Objects (VOs) are by default configured to lazy fetch records from the database into the midtier in ranges (record sets of say 10 records) based on user requests. This lazy loading feature is designed to make it unnecessary to load all the records from a table into the midtier for speed and memory efficiencies while taking advantage of the fact the user probably doesn't want to view all 1000 employee records at once, but will be happy viewing a sub set at a time.
As noted above, if the record has been fetched into the midtier's
To my knowledge JDeveloper 10.1.3+ has no facility with the EOs to validate
Beating the database constraints at their own game in
In my opinion however, good user interface (UI) design dictates that it would be better to stop the database errors occurring in the first place to provide a relatively more satisfying experience for the user. How do you do this? Well you stop the user from ever entering data that violates the constraints in the first place.
The first set of suggestions here are obvious but need to be stated:
Using and generating surrogate read-only primary keys - if you're allowing the user to create or edit records, and the table has a surrogate primary key generated via a sequence in the database that guarantees uniqueness, make the field read-only (or hidden). Then within the ADF BC EO for the associated table, populate the pk programmatically on creating a new record via the sequence. In this fashion the user can never enter a non unique pk for the primary key value.
Use poplists and list of values (LOVs) for foreign keys - if the user in creating or editing a record needs to enter a foreign key value, don't allow them to manually type potentially invalid foreign key values. Instead provide poplists and LOVs for the user to select a valid value from.
Make mandatory database fields mandatory in
Beyond the obvious ideas, JDeveloper also provides a number of validators at the EO level that enforce data integrity checks and supply custom error messages if a problem is found before they hit the database. For the brevity of this post they will not be considered here. I note that Jan Kettenis under the recent post UML Rules is currently writing a whitepaper on implementing business rules in ADF BC which hopefully will cover the majority of the validators and a useful source of information beyond Section "6.7 Using Declarative Validation Rules" in Oracle's developer's guide.
14 comments:
Chris - nice post. This question comes up on a regular basis, and it's nice not to have to say "go find the not-yet-or-ever-to-be-finished JSF Toy Store demo."
Thanks John. Thought of it while I was "at the pub" ;)
I would like save error codes and corresponding custom error messages not in the Java code, but in a delegated database table. So my question is, how would you advice to read database table content and create a two-dimensional array of it?
Anonymous (it would be better if you posted with a real name), I've been meaning to write up such a post for some time but haven't the time just yet.
Possibly have a look at Jan Kettenis's blog entry How to Pimp ADF BC Exception Handling. This may give you the seeds of where the choke point is in ADF BC for overriding the default behaviour to retrieve from the message bundles, and instead write code to retrieve from the database.
Hope this helps.
CM.
when we allow users to enter data via the front end, that we should never allow the user to eneter the primary key, and
instead should use db sequences as primary keys?
is there any restriction in adf table can't allow enter data via front ends for primary key fields
Dil, in answer to your 2 questions:
1) If your PK column is based on a sequence it makes sense for ADF BC to fetch the seq number for the user and the user doesn't enter a value.
2) Not sure what you mean by "restrictions". If you mean can we stop the user overwriting the fetched seq number, yes you can by selecting the EO or VO attribute in the appropriate design time editor, then setting it's Update on Insert option to false.
For your reference both questions would have been better posted on the OTN JDeveloper forums.
Regards,
CM.
Hi Chris,
I have a question regarding this topic, see my OTN Forum post at http://forums.oracle.com/forums/thread.jspa?messageID=3736085#3736085
Thanks,
Patrik
Hi Patrik
Yes, I can see your forum post. As somebody else has answered I was waiting for you to give them reply, a courtesy as they've taken time out to post for you.
Regards,
CM.
Chris,
sorry but I don't get your point as somebody else has answered to somebody else (not me), so I don't think I got any replies.
Btw, I was posting to that specific forum because it seemed the most appropriate place for this question (and not your blog's comment area), especially after your post there.
Thanks again,
Patrik
See my forum post.
Nice blog, cant seem to find the reference to 25.8.1 steps 1-5, can you direct me?
Assuming you specifically want the 10.1.3 documentation, the guide can be found here:
http://download.oracle.com/docs/cd/B32110_01/web.1013/b25947/toc.htm
...and section 25.8.1 here:
http://download.oracle.com/docs/cd/B32110_01/web.1013/b25947/bcadvgen.htm#sthref2387
CM.
That is a great article about how to make custom error messages, but there is just one important step missing for the custom messages to appear, is that u have to change the properties of the model project to see the custom messages class
Quoting from chapter 25 section 25.8.1
"
Open the Business Components > Options page in the Project Properties dialog for the project containing your business components.
Notice the Custom Message Bundles to use in this Project list at the bottom of the dialog.
Click New.
Enter a name and package for the custom message bundle in the Create MessageBundle class dialog and click OK.
Note:
If the fully-qualified name of your custom message bundle file does not appear in the Custom Message Bundles to use in this Project list, click the Remove button, then click the Add button to add the new message bundle file created. When the custom message bundle file is correctly registered, it's fully-qualified class name should appear in the list.
Click OK to dismiss the Project Properties dialog and open the new custom message bundle class in the source editor.
"
Post a Comment