Beruflich Dokumente
Kultur Dokumente
For technical information about J2EE Development on Oracle, visit the Java Developer
Center (http://www.oracle.com/technology/java) on Oracle Technology Network.
A DATABASE-CENTRIC APPROACH TO J2EE
APPLICATION DEVELOPMENT
—Toon Koppelaars
Centraal Boekhuis
Introduction
Building a J2EE/Java application may seem a daunting task at first. Java, object-oriented programming itself, is truly
different, and cannot be compared with PL/SQL (which can be classified as an easy to learn programming language).
There are many new technologies one has to explore and learn: for example, various frameworks to enable the Model-
View-Controller (MVC) design pattern; HTTP, the stateless-protocol; HTML in the browser, including JavaScript;
JDeveloper, Oracle’s strategic Integrated Development Environment (IDE). Will you ever be able to grasp all this new
technology? There are ways to ease this daunting task, in particular for traditional Oracle client/server shops. The most
successful way is to adopt a database-centric approach. The Oracle server has come a long way and various features such
as Virtual Private Database (VPD), instead-of triggers (IOT), (updateable) views, ref cursors, pipelined table functions,
and packaged procedures today enable you to implement the majority of all involved application logic into the well-
known database server. Strongly preferring stored PL/SQL to Java when deciding on where code should go, will enable a
very thin J2EE/Java layer. By never writing a line of Java code that could have been a line of PL/SQL code, you will
considerably reduce the risk in your J2EE project.
In order to deal in a structured way with the question in the title above, we will first present a model of the J2EE
technology stack from the viewpoint of “code execution environments.” This will enable us to talk about the where of
code execution. Next we will develop a generic classification scheme for code (irrespective of where the code ends up
running). This will enable us to talk about what code. We can then map code onto the technology stack. This article will
discuss how traditional Oracle client/server shops can approach the J2EE world using a database-centric mapping. We
explain in more detail how we successfully apply this strategy in all of our J2EE application development efforts.
1
Applets should not be confused with JavaScript. JavaScript is Java-like code that is embedded in HTML source. JavaScript is used to
perform simple checks in the client tier (e.g., domain checks) or to enhance the look/feel of HTMLin the browser.
source. Part of the applet tag is the applet'
s Java class file that needs to be fetched from the Web server. Once the browser
has downloaded this file from the Web server, it invokes a start-method which starts applet execution. Applets too can
use the popular Swing and AWT libraries to provide a rich (i.e., not HTML- or XML- based) user interface to the user.
As such they too can be seen as “fat” client programs, only these run inside your browser.
Servlets
These are Java programs that execute inside a Servlet Container on your Web server. This container is provided by your
vendor: Oracle provides OC4J (Oracle Containers for J2EE) as the J2EE Servlet Container. Note that a Servlet Container
is itself a Java program, so it in turn runs inside the JVM provided by the operating system of your Web server.
Execution of servlets is typically started by an incoming HTTP request (post or get). When handed the request, OC4J
instantiates the servlet object by loading the Java class file that holds the servlet'
s byte code from the file system and then
invokes the doPost or doGet method on the servlet object. Servlets handle the HTTP request and eventually create the
next (HTML or XML) page to be sent back to the browser that initiated the request. By doing so, servlets can be viewed
as Java' s answer to existing solutions such as Common Gateway Interface (CGI), PHP Hypertext Preprocessor (PHP),
and Active Server pages (ASP).
JavaServer Pages (JSP)
These are HTML pages mixed with special tags that hold embedded Java. This Java will be executed prior to sending the
HTML to the browser and is able to generate dynamic content. JSPs eventually execute in the same container as servlets
do. Whenever OC4J detects that a JSP needs to be executed, it reads the JSP file from the file system and converts the
JSP into a servlet by generating a class file for it. It then instantiates this servlet class and invokes a method to start
execution of this “servletised” JSP. It's often said that “a JSP is a Servlet inside out” (HTML that executes embedded
Java), and vice-versa “a Servlet is a JSP inside out” (Java that outputs HTML).
Enterprise JavaBeans (EJB)
These are Java programs that execute inside an EJB Container on your application server (not necessarily the same as
your Web server). With Oracle, OC4J also acts as the EJB Container. EJBs are used to implement reusable business
functions. As such, EJBs typically act as “back-end” application services for the other component types. They are
considered to be fat, meaning that they hold the business and data logic code for the application. EJBs sit between
servlets/JSPs and the data tier (servlets/JSPs can call EJBs). EJBs can also call other EJBs, potentially on other
application servers. EJBs will communicate with the persistent data store (database) on the data tier.
Servlets, JSPs, and EJBs are enabled to access the relational database running at the data tier by means of Java DataBase
Connectivity (JDBC) . JDBC is J2EE' s standard database access application-programming language (API), that replaces
the well-known SQL*Net layer in client/server applications. Figure 1 summarizes the multitier, distributed object
architecture.
EJB
EJB
EJB
Java
r m i /i i o p
Se r v l e t J
A p p let Se r v l e t D
h t t p (s)
JSP B
H t m l /X m l
JSP C RD BM S
2
As we will see later on, a pure J2EE application will never produce complex SQL statements. Only single table selects, primary key-
based (i.e., single row) updates and deletes, and single row inserts are produced by such an application.
3
By a poor UI we mean a UI that does not respond directly to user-initiated events. A block-mode device, which is what a browser
displaying HTML can be considered, is an example of a device offering a poor UI. No processing takes place within the browser (to
respond directly). Only when the user submits the HTML form to the server will processing start (at the server). This browser
behaviour can be enhanced by using JavaScript enabling a somewhat more responsive UI. JavaScript is code that runs within the
browser and is triggered by UI events initiated by the user.
4
This alternative is actually comparable with the way we did client/server applications in the early days, when the database tier was
not yet capable of running business or data logic code (pre-Oracle7 era).
5
I often refer to this alternative as the Run-Run-Run (as fast as you can) alternative. You will end up with this architecture if you
decide to put a PL/SQL wizard, an EJB believer, and a Swing hacker together in your development team.
6
UI code will typically call query composing/executing code to embed data in the user interface. UI code will typically call transaction
composing code in response to user interface events.
7
DL code for checking simple rules such as “status-code must be in (A, B, C),” or “finish date must be after start date” is often
embedded in UI code.
8
The only way to fully separate DL code from BL code is to adopt an architecture where all DL code sits in database triggers.
9
See: http://www.rulegen.com
• DML statements
- Are always based on a view
- Always manipulate one-row
This gives us the templates in Listing 1 for DMLstatements that are embedded in UI code:
UPDATE <view> SET <column>=<value>, <column>=<value>, …
WHERE <primary key>=<value>;
DELETE FROM <view>
WHERE <primary key>=<value>;
INSERT INTO <view> VALUES(<value>,<value>,…);
Listing 1. Templates for DML Statements
• Instead-of triggers defined on these views are then able to interpret the single view-row DML statement and
translate this into (potentially complex) DML statements against underlying database tables.
Other than the calls to these API BL code objects, we do not allow BL code in the Java container or inside the Browser
(Javascript).
V 4(p1)
In st e a d - o f
V PD t rig g er
Co n t e x t
Pa g e
Co n t e x t
Pa g e Co n t e x t
Co n t e x t Co n t e x t
Co n t e x t Co n t e x t Pa g e
Pa g e
Pa g e Co n t e x t
Co n t e x t V 5 (p 1 ,p 2 ,p 3
)
V PD
Pa g e
V 1 (p 1 ) V 3 ( p 1 ,p 2 )
V 2 ( p 1 ,p 2 )
V PD V PD
V PD In st e a d - o f
t r ig g er
10
VPD can even be tweaked such that it will return rows in a specified order too (see next section).
Policy functions can access system variables such as USER, SYSDATE, etc, or functions like USERENV(), to
dynamically determine what the predicate to be returned should be. To further enable a policy function to dynamically
determine this return value, one can use another feature called “application context.” Application context offers a generic
way to register knowledge on what the context within the (client side) application currently is within the database
session. This is done by setting <attribute,value> pairs within a named context of the current database session. The
<attribute,value> pairs are stored inside v$context, of which each database session has its own copy. Policy functions can
read these attribute values and use them to compute the correct predicate. A (named) context must first be created using
the “create context” command. Oracle offers secure contexts by making sure <attribute,value> pairs can only be set
within a context by a given stored package (which is supplied at create context time). Note that contexts exist at the
database level and not within a specific schema. To create a context the create context system privilege is required.
Listing 4 shows the code that creates a context (specifies the package that can write the context), and the package source
itself.
create context EMP_APPLICATION using EMP_SEC;
package EMP_SEC is
procedure set_empno(p_empno in number);
end;
package body EMP_SEC is
procedure set_empno(p_empno in number) is
begin dbms_session.set_context('EMP_APPLICATION','CURRENT_EMPNO',to_char(p_empno));
end;
end;
Listing 4. Create Context
A client application can now register a current employee in the context by calling the EMP_SEC.set_empno packaged
procedure. Policy functions can read context contents in two ways: by directly selecting from v$context, or by calling the
sys_context PL/SQL built-in function.
select value into l_current_empno
from v$context
where namespace = 'EMP_APPLICATION’ and attribute = 'CURRENT_EMPNO';
Or,
l_current_empno := sys_context('EMP_APPLICATION','CURRENT_EMPNO');
VPD and application context made it very simple for our Java programmers to retrieve the data that had to be shown on a
certain UIX page. First they call a “set-context” procedure telling the database what the current application context is,
then they perform a full access of a BC4J view object (which would translate into a “select * from <view>” statement).
The policy function then makes sure only the necessary rows are returned. Figure 11 depicts these steps taking place.
O r acl e 8i
<view >
Se t _Ct x
W h e r e -cl a u se
2
4
6
v $ co n t e x t V PD
A1 v1 Po l i cy-Fu n ct i o n
A2 v2 5
A3 v3
It is therefore essential that when a page view is developed for a UIX page that supports updates by the user, this view is
explicitly checked to optimally execute its query given the where-clause BC4J adds (and not only given the where-
clauses added by the VPD policy, as mentioned earlier). To prevent mixing of these (BC4J) where-clauses with our own
policy where-clauses, we standardized that VPD only adds a where-clause when SELECTing from page views. It was up
to the instead-of trigger code (before even starting with the execution of DL code or wBL code), to always first check the
row level security: is, given the application context, updating this row at all allowed or not? Figure 12 depicts the steps
taking place when BC4J updates a view with an instead-of trigger:
1) BC4J generates update statement.
2) View will retrieve affected rows (perform a select, but not update).
3) Update responsibility is handed over to instead-of-trigger. Trigger is supplied with retrieved
record(-variables).
4) Trigger will check row-level-security and/or other application-context dependant issues.
5) Trigger determines which tables to update, and does so.
O r acl e8i
2 V
T
T 3
T
v$ co n t ext
A1 v 1 5
A2 v 2 In st e ad -o f
A3 v 3
U p d at e -t r i g g e r
4
Lessons Learned
Here are some of the lessons we have learned during the first few J2EE projects that we have done, using the database-
centric approach that we have described.
Do Not Trust Java Geeks
In our database-centric approach we explicitly create API views in conjunction with set-context stored procedures that
are to be used by the UI code for executing the data retrieval and manipulation (including BL+DL Code as described
earlier). Once you have set these guidelines in terms of what BL code API objects are to be used from within the UI code
(Java view+control frameworks), make sure that the Java programmers cannot access any other database objects than
these. For if you supply them with a database connection that gives them access to all other BL code objects, or all
underlying tables of the API views, you can be sure of one thing: they will start using those too. Part of our approach
now is that we create a separate database schema that holds the BL code API objects only. The Java model framework
connects to this schema, and cannot see or access any other application objects inside the database (they sit in a different
schema). We create separate development teams too: one team that develops the view+control (UI code) for the
application, and one team that develops the model (BL+DL code) for the application. The first team uses JDeveloper.
The second team still uses the Designer product. For each page of the application these two teams are forced to sit
together first and discuss the application context dimensions for that page, and the semantics of the database view that
services the data retrieval and/or manipulation for that page. Once this has been agreed upon both teams can start
development for that page independent from each other.
VPD: Hard Parses Versus Soft Parses
Consider a “display-book-details” page of an Order-Book application. The application context of this page consists of
only one attribute, say Book_Id. The API database view selects all columns of the Books table. The VPD policy function
is supposed to add a where-clause such as “WHERE Book_Id = <current book_id of the application context>.” The
policy function in Listing 7 would do this (assuming a BOOK_DETAILS context with a BOOK_ID attribute that has
been set by the UI code):
function Book_Details_Policy(p_schema in varchar2, p_object in varchar2)
return varchar2 as
l_book_id number;
begin
l_book_id = sys_context(‘BOOK_DETAILS’,’BOOK_ID’);
Filling the temporary table is CPU-intensive in this case. Once it is done, fetching the rows from the view (by UI code)
requires little CPU. This worked fine for us in Oracle8i. However the introduction of Oracle9i has changed the execution
model of VPD policy functions. A policy function now fires twice for every query that is submitted: once during the
parse phase (irrespective of whether this is a hard or a soft parse), and once during the execute phase. Given the CPU-
intensive policy function this has meant that the migration to Oracle9i has caused a near doubling of the CPU usage for
these pages. We have opened a TAR for this on Metalink but to no avail: this is documented behavior (in fact Oracle
states that the once-only firing of the policy function in Oracle8i was a bug). We are in the process of rewriting these
CPU-intensive policy functions so that they detect whether the contents of the temporary table are already OK, and if so
do not execute the procedural PL/SQL code a second time.
Sorting Via VPD: A Bridge Too Far
The double sub-query trick we explained earlier to mandate a certain order in the rows retrieved by the API view works
fine as long as the view involved is simple. Getting this trick to work for more complex views turned out to be rather
difficult. Currently we do not use this trick anymore. Using BC4J, the order-by clause is added to the “select * from
<API view>” query. This can easily be coded as part of the embedded BL code call inside the UI code as follows in
Listing 9:
ViewObject vo = am.findViewObject("VBOOKHITS");
Vo.setorderbyclause(‘author’);
RowSet rowSet = vo.getRowSet();
Row r = null;
while (rowSet.hasNext()) {
r = rowSet.next();
// processing here…}
Listing 9.Order by Author Query
Instead of sending a “select * from <view>” statement to the RDBMS, this code will send a “select * from <view> order
by author” query to the RDBMS. VPD will still insert the necessary where-clause.
Conclusion
This article assumes you are an Oracle PL/SQL shop. If you are, then coding all Data Logic and Business Logic Code in
PL/SQL will enable you to considerably reduce the risk in your (first) J2EE project. We are not saying that it will be easy
though. On the contrary, there is still a whole new world for you to discover and adopt. Approaching this in a database-
centric way however, will enable you to not make it more complex than it needs to be.
For us this approach has meant that only few developers needed to learn the new IDE including all the technology
aspects surrounding it. Since the new Java IDE is only used to build UI code, we were able to specify a finite number of
Bibliography –L1
[Kopp-2003] Business Rules: Theory and Implementation (What and How). Proceedings of the Business Rules
Symposium ODTUG-2003, Miami (also available at http://web.inter.nl.net/users/T.Koppelaars).
! "
# $%& ' ( ') *
' + # , , or
, .
11
On average two-thirds of the build effort is done in the familiar PL/SQL and SQL environments and one-third in JDeveloper/Java.
After go-live, the run and maintenance effort shows a distribution that shifts even more in favor of a PL/SQL and SQL programming
effort.