2. Table
Of
Content
q I.
Test-‐Driven
PL/SQL
Development
v I.1.
Front-‐end
&
Back-‐end
v I.2.
Development
WorkKlow
q II.
Exception
Handling
Architecture
q III.
Optimize
SQL
in
PL/SQL
q IV.
Java
&
PL/SQL
v IV.1.
Client
Server
Java/PLSQL
Architecture
v IV.2.
Java
-‐
PL/SQL:
Ad-‐hoc
Call
v IV.3.
Java
-‐
PL/SQL:
Function/Procedure
Call
10/8/12
www.smartbiz.vn
2
3. I.1.
Front-‐end
&
Back-‐end
§ We
usually
just
talk
Browser
Java
about
front-‐end
and
Frontend
Oracle
back-‐end,
but
there's
Developer
more
to
it
than
that.
"Assign
calls"
§ Developers
spend
Application
Logic
app_call_mgr
most
of
their
time
in
B "Is
call
open?"
a Business
Rules
cm_calls_rp.is_open
the
top
two
layers.
"Log
error"
c Backend
Infrastructure
– But
they
often
em_errors
k (in
the
"Parse
string"
don't
have
clearly
Generic
Utilities
e Oracle
Database)
tb_string_utils
deKined
"Add
new
call"
boundaries.
n Data
Access
cm_calls_cp.ins
d "Call
Table"
Data
(tables)
cm_calls
10/8/12
www.smartbiz.vn
3
4. I.2.
Development
WorkLlow
Application
Preparation
1 Coding
7 The
Build
Test
and
Cycle
Conventions
Review
SQL
To
QA
/
Access
Debug
Production
Error
Build
/
Mgt
6 Fix
Code
Single
Unit
2 3 4 5
Preparation
DeKine
Construct
DeKine
Build
Req’ments
Header
Tests
Test
Code
Post-‐
Post-‐
Production
Production
Bug
Enhance.
Report
Request
10/8/12
www.smartbiz.vn
4
5. I.3.
Testing
q Functional/System
Tests
v Performed
by
QA
teams
and
users,
tests
entire
application.
q Stress
Tests
v Program
works
for
1
user,
how
about
10,000?
Usually
done
by
DBAs
and
system
admins.
v Quest's
Benchmark
Factory
automates
this
process.
q Unit
Tests,
aka
”Programmer
Tests"
v The
test
of
a
single
unit
of
code.
v These
are
the
responsibility
of
developers,
of
the
people
who
wrote
the
program.
q All
testing
is
important,
but
unit
tests
are
the
most
fundamental
kind
of
testing.
10/8/12
www.smartbiz.vn
5
6. II.1.
Error
Management
q Manage
errors
effectively
and
consistently:
v Errors
are
raised,
handled,
logged
and
communicated
in
a
consistent,
robust
manner.
q Some
special
issues
for
PL/SQL
developers
v The
EXCEPTION
datatype
v How
to
Kind
the
line
on
which
the
error
is
raised?
v Communication
with
non-‐PL/SQL
host
environments
q Achieving
ideal
error
management
v DeKine
your
requirements
clearly
v PL/SQL
error
management
&
best
practices
10/8/12
www.smartbiz.vn
6
7. II.2.
PL/SQL
Error
Management
q DeKining
exceptions
v Give
name
to
Error
with
EXCEPTION_INIT
PRAGMA
q Raising
exceptions
v RAISE_APPLICATION_ERROR:
communicates
speciKic
error
back
to
a
non-‐PL/SQL
host
environment
q DML
aren’t
rolled
back
when
exception
is
raised
v Rollback
Handling
v Log
information
is
committed,
while
leaving
the
business
transaction
unresolved
q Handing
exceptions
10/8/12
www.smartbiz.vn
7
8. II.3.
Handing
Exceptions
q The
EXCEPTION
section
consolidates
all
error
handling
logic
in
a
block.
v But
only
traps
errors
raised
in
executable
section
of
block.
q Several
useful
functions
usually
come
into
play:
v SQLCODE
and
SQLERRM
v DBMS_UTILITY.FORMAT_ERROR_STACK
v DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
q The
DBMS_ERRLOG
package
v Quick
and
easy
logging
of
DML
errors.
q The
AFTER
SERVERERROR
trigger
v Instance-‐wide
error
handling:
most
useful
for
non-‐PL/SQL
front
ends
executing
SQL
statements
directly.
10/8/12
www.smartbiz.vn
8
9. II.4.
Best
Practices
for
Error
Management
q Some
general
guidelines
-‐
standards
for
logging
mechanisms,
error
message
text;
must
consistent
v Avoid
hard-‐coding
of
Error
Numbers
and
Messages.
v Build
and
use
reusable
components
for
Raising,
Handling
and
Logging
Errors.
q Application-‐level
code
should
not
contain:
v RAISE_APPLICATION_ERROR:
don't
leave
it
to
the
developer
to
decide
how
to
raise.
v PRAGMA
EXCEPTION_INIT:
avoid
duplication
of
error
deKinitions.
q Object-‐like
representation
of
an
exception
(DB)
10/8/12
www.smartbiz.vn
9
10. II.4.1.
Prototype
Exception
Manager
² The
rule:
developers
should
only
call
a
pre-‐
deLined
handler
inside
an
exception
section
v Easier
for
developers
to
write
consistent,
high-‐quality
code
v They
don't
have
to
make
decisions
about
form
of
log
&
how
the
process
should
be
stopped
PACKAGE
errpkg
IS
Generic
PROCEDURE
raise
(err_in
IN
PLS_INTEGER);
EXCEPTION
PROCEDURE
raise
(err_in
in
VARCHAR2);
Raises
WHEN
NO_DATA_FOUND
THEN
PROCEDURE
record_and_stop
(
errpkg.record_and_continue
(
err_in
IN
PLS_INTEGER
:=
SQLCODE
SQLCODE,
Record
,msg_in
IN
VARCHAR2
:=
NULL);
'
Not
Exist
'
||
TO_CHAR
(v_id));
and
Stop
PROCEDURE
record_and_continue
(
WHEN
OTHERS
err_in
IN
PLS_INTEGER
:=
SQLCODE
THEN
Record
,msg_in
IN
VARCHAR2
:=
NULL);
errpkg.record_and_stop;
&
Continue
END;
END
errpkg;
10/8/12
www.smartbiz.vn
10
11. III.
Optimize
SQL
in
PL/SQL
q Advantage
of
PL/SQL
enhancements
for
SQL.
v III.1.
BULK
COLLECT
Ø Use
with
implicit
and
explicit
queries.
Ø Move
data
from
tables
into
collections.
v III.2.
FORALL
Ø Use
with
inserts,
updates
and
deletes.
Ø Move
data
from
collections
to
tables.
v III.3.
Table
functions
q III.4.
Top
Tip:
Stop
Writing
So
Much
SQL
v A
key
objective
of
this
presentation
is
to
have
you
stop
taking
SQL
statements
for
granted
inside
your
PL/SQL
code.
v Instead,
you
should
think
hard
about
when,
where
and
how
SQL
statements
should
be
written
in
your
code.
10/8/12
www.smartbiz.vn
11
12. III.1.
SQL
and
PL/SQL
Oracle
Server
PL/SQL
Runtime
Engine
SQL
Engine
PL/SQL
block
Procedural
statement
FOR
rec
IN
emp_cur
LOOP
executor
UPDATE
employee
SQL
SET
salary
=
...
statement
WHERE
employee_id
=
executor
rec.employee_id;
END
LOOP;
Performance
penalty
for
many
“context
switches”
12
13. III.1.
Different
process
with
FORALL
Oracle
server
PL/SQL
Runtime
Engine
SQL
Engine
PL/SQL
block
Procedural
statement
FORALL
indx
IN
executor
list_of_emps.FIRST..
SQL
list_of_emps.LAST
statement
UPDATE
employee
executor
SET
salary
=
...
WHERE
employee_id
=
list_of_emps(indx);
Much
less
overhead
for
context
switching
13
14. III.1.
Use
BULK
COLLECT
INTO
for
Queries
DECLARE
Declare
a
TYPE
employees_aat
IS
TABLE
OF
employees%ROWTYPE
collection
of
INDEX
BY
BINARY_INTEGER;
records
to
hold
l_employees
employees_aat;
the
queried
data.
BEGIN
SELECT
*
BULK
COLLECT
INTO
l_employees
Use
BULK
FROM
employees;
COLLECT
to
FOR
indx
IN
1
..
l_employees.COUNT
retrieve
all
rows.
LOOP
process_employee
(l_employees(indx));
END
LOOP;
END;
Iterate
through
the
collection
contents
with
a
WARNING!
BULK
COLLECT
will
not
raise
loop.
NO_DATA_FOUND
if
no
rows
are
found.
Always
check
contents
of
collection
to
conLirm
that
something
was
retrieved.
14
15. III.2.
FORALL
Bulk
Bind
Statement
q Instead
of
executing
repetitive,
individual
DML
statements,
you
can
write
your
code
like
this:
PROCEDURE
upd_for_dept
(...)
IS
BEGIN
FORALL
indx
IN
list_of_emps.FIRST
..
list_of_emps.LAST
UPDATE
employee
SET
salary
=
newsal_in
WHERE
employee_id
=
list_of_emps
(indx);
END;
q Things
to
be
aware
of:
v You
MUST
know
how
to
use
collections
to
use
this
feature!
v Only
a
single
DML
statement
is
allowed
per
FORALL.
v New
cursor
attributes:
SQL%BULK_ROWCOUNT
returns
number
of
rows
affected
by
each
row
in
array.
SQL%BULK_EXCEPTIONS...
v Use
SAVE
EXCEPTIONS
to
continue
past
errors.
q When
Cursor
FOR
Loop
vs.
BULK
COLLECT?
v If
you
want
to
do
complex
processing
on
each
row
as
it
is
queried
–
and
possibly
halt
further
fetching.
15
16. III.2.
Tips
and
Fine
Points
q BULK
COLLECT
v Collection
is
always
Lilled
sequentially,
starting
at
row
1.
v Production-‐quality
code
should
generally
use
the
LIMIT
clause
to
avoid
excessive
memory
usage.
v Note:
Oracle
will
automatically
optimize
cursor
FOR
loops
to
BULK
COLLECT
performance
levels.
q FORALL
v Use
whenever
executing
multiple
single-‐row
DML
statements.
v Used
with
Collection;
Collection
subscripts
cannot
be
expressions.
v Cannot
reference
Lields
of
collection-‐based
records
inside
FORALL.
Ø But
you
can
use
FORALL
to
insert
&
update
entire
records.
v Use
the
INDICES
OF
clause
to
use
only
the
row
numbers
deLined
in
another
array.
v Use
VALUES
OF
clause
to
use
only
values
deLined
in
another
array.
10/8/12
www.smartbiz.vn
16
17. III.3.
Table
Function
q The
Wonder
Of
Table
Functions
v Table
function
allow
to
perform
arbitrarily
complex
transformations
of
data
and
then
make
that
data
available
through
a
query.
Ø Not
everything
can
be
done
in
SQL.
v Combined
with
REF
CURSORs,
you
can
now
more
easily
transfer
data
from
within
PL/SQL
to
host
environments.
Ø Java,
works
very
smoothly
with
cursor
variables
q Building
a
table
function
v Return
nested
table
or
varray
based
on
schema-‐deLined
type.
Ø Types
deLined
–
only
used
with
pipelined
table
functions.
v The
function
header
and
the
way
it
is
called
must
be
SQL-‐compatible:
all
parameters
use
SQL
types;
no
named
notation.
Ø In
some
cases
(streaming
and
pipelined
functions),
the
IN
parameter
must
be
a
cursor
variable
-‐-‐
a
query
result
set.
10/8/12
www.smartbiz.vn
17
18. III.3.
Streaming
Data
with
Table
Functions
q "stream"
data
through
several
stages
within
a
single
SQL
statement.
v Example:
transform
one
row
in
the
stocktable
to
two
rows
in
the
tickertable.
CREATE OR REPLACE PACKAGE refcur_pkg
IS
TYPE refcur_t IS REF CURSOR
RETURN stocktable%ROWTYPE;
END refcur_pkg;
/
CREATE OR REPLACE FUNCTION stockpivot (dataset refcur_pkg.refcur_t)
RETURN tickertypeset ...
BEGIN
INSERT INTO tickertable
SELECT *
FROM TABLE (stockpivot (CURSOR (SELECT *
FROM stocktable)));
END;
/
10/8/12
www.smartbiz.vn
18
19. III.3.
Piping
rows
out
from
Pipelined
function
q Return
data
iteratively,
asynchronous
to
termination
of
function.
As
data
is
produced
within
the
function,
it
is
passed
back
to
the
calling
process/query.
CREATE
FUNCTION
stockpivot
(p
refcur_pkg.refcur_t)
RETURN
tickertypeset
Add
PIPELINED
keyword
to
header
PIPELINED
IS
out_rec
tickertype
:=
tickertype
(NULL,
NULL,
NULL);
in_rec
p%ROWTYPE;
BEGIN
LOOP
FETCH
p
INTO
in_rec;
EXIT
WHEN
p%NOTFOUND;
Pipe
a
row
of
data
out_rec.ticker
:=
in_rec.ticker;
back
to
calling
block
out_rec.pricetype
:=
'O';
or
query
out_rec.price
:=
in_rec.openprice;
PIPE
ROW
(out_rec);
END
LOOP;
CLOSE
p;
RETURN...nothing
at
RETURN;
all!
END;
10/8/12
www.smartbiz.vn
19
20. III.4.
Top
Tip:
Stop
Writing
So
Much
SQL
q Bottom
line:
if
everyone
writes
SQL
Order
Entry
whenever
and
wherever
they
want
to,
it
Program
is
very
difLicult
to
maintain
and
optimize
the
code.
q General
principle:
Ligure
out
what
is
Order
Item
volatile
and
then
hide
that
stuff
behind
Table
Table
a
layer
of
code
to
minimize
impact
on
Application
application
logic.
Code
q Single
Point
of
(SQL)
Robust
DeLinition:
if
same
statement
appears
in
multiple
Intermediate
Layer
places
in
application
code,
very
difLicult
to
maintain
and
optimize
that
code.
q Avoid
SQL
Repetition
Order
Item
Table
Table
10/8/12
www.smartbiz.vn
20
22. IV.2.
Ways
to
call
PL/SQL
q ADHOC
v Send
the
PL/SQL
block
of
code
from
the
Client-‐Java
program
to
the
Server
for
processing.
v Query
based
on
Prepared
Statement
q FUNCTION
v Client
Side
calls
a
PL/SQL
function
in
Oracle.
PL/SQL
is
already
compiled
and
loaded
in
Oracle.
v Function
will
return
a
value
q PROCEDURE
v Client
calls
a
PL/SQL
procedure
v Similar
to
function,
but
does
not
return
values.
10/8/12
www.smartbiz.vn
22
23. IV.2.
ADHOC:
PL/SQL
Example
PreparedStatement
ps=null;
sSQL=new
String("declare"
+
"
l_id
number:=0;"
+
"
ret
number:=0;"
+
"
lssno
varchar2(32):=?;"
+
1
"
error_msg
varchar2(1026):=null;"
+
"
BEGIN"
+
"
select
count(*)
into
ret
from
person
where
SSNO=
lssno
;"
+
2
"
if(ret=0)
then"
+
"
insert
into
person(id,ssno)
values(seq_id.nextval,
lssno)
returning
id
into
ret;"
+
"
end
if;"
+
"
?:=ret;"
+
"
EXCEPTION
WHEN
OTHERS
THEN"
+
3
"
?:=SQLERRM;"
+
"
END;");
4
ps
=connection.prepareStatement(sbSQL.toString());
int
indx=1;
5
ps.setString(indx++,SSNO);
int
rettype=Types.INTEGER;
ps.registerOutParameter(indx++,rettype);
6
rettype=Types.VARCHAR;
ps.registerOutParameter(indx++,rettype);
ps.executeUpdate();
l_sError=ps.getString(2);
if(l_sError!=null)
{
System.err.println("PL/SQL
Error:
"
+
l_sError);
return;
7
}else
{
ret=ps.getInt(1);
System.out.println("New
USERID:
"
+
ret);
}
10/8/12
www.smartbiz.vn
23
24. IV.3.
Java
PL/SQL:
Function/Procedure
Call
import
java.sql.*;
q Key
Things
to
Note:
import
oracle.sql.*;
…
PreparedStatement
ps
=null;
v Minimize
SQL
code
on
ArrayDescriptor
desc
=
ArrayDescriptor.createDescriptor("JOEL.STR_VARRAY”,m_Conn);
client
side.
String
sCar="";
String
cars[]=new
String[10];
v loaded
into
Oracle
once
at
for(int
i=0;i<10;i++)
{
sCar="carX"+i;
//
CONTRIVE
A
NAME
of
a
CAR
install
time.
cars[i]=new
String(sCar);
}
v Ready
for
portability
ARRAY
array3
=
new
ARRAY
(desc,
m_Conn,
cars);
String
sql=new
String("{call
§ Within
Oracle
–
yes
api_person_pkg.storePersonCar('joelt',?)
}");
§ JDBC
Standard
-‐
using
other
ps=
m_Conn.prepareStatement(sql);
database
–
perhaps
//
Set
the
values
to
insert
((oracle.jdbc.driver.OraclePreparedStatement)ps).setARRAY(1,
§ SQL
Standard
-‐
SQL92
syntax?
array3);
v Consider
using
global
//
Insert
the
new
row
ps.executeUpdate();
temporary
table.
m_Conn.commit();
10/8/12
www.smartbiz.vn
24