Working with PL/SQL procedures in ADF BC Model
I believe this is very common use case when we need to invoke
some PL/SQL procedure in our Java code in ADF BC model. The standard approach
for that is to use JDBC callable statement. For example, we have a PL/SQL
procedure:
create or replace procedure somePLSQLProcedure
(aStringParam Varchar2,
aIntegerParam Number,
aDateParam Date,
aOutParam out Varchar2
)
And Java method invoking this procedure using JDBC callable statement is going to look like this:
create or replace procedure somePLSQLProcedure
(aStringParam Varchar2,
aIntegerParam Number,
aDateParam Date,
aOutParam out Varchar2
)
And Java method invoking this procedure using JDBC callable statement is going to look like this:
public String
execPLSQLProcedure(String aStringParam, Integer aIntParam,
Date aDateParam) {
DBTransactionImpl dbti = (DBTransactionImpl)getDBTransaction();
CallableStatement statement =
dbti.createCallableStatement(("BEGIN
"+"somePLSQLProcedure(?,?,?,?);" +
"END;"), 0);
try
{
statement.setString(1, aStringParam);
statement.setInt(2, aIntParam);
statement.setDate(3, aDateParam);
statement.registerOutParameter(4, Types.VARCHAR);
statement.execute();
return statement.getString(4);
}
catch (SQLException sqlerr) {
throw new JboException(sqlerr);
}
finally {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException closeerr) {
throw new JboException(closeerr);
}
}
}
Don't you think this piece of code is a little bit ugly? It is
doing a prosaic thing - invoking an extremely simple PL/SQL procedure and it is
so complicated. Moreover, there is no any control as for number and types of
the procedure's parameters. So, writing this code each time whenever I need to
invoke a PL/SQL procedure gets me a bit annoyed, and I developed some set of
helper classes and interfaces in an effort to make it much easier. For example,
a method invoking the same PL/SQL procedure should look like this:
public String
execSmartPLSQLProcedure(String aStringParam,
Integer aIntParam,
Date aDateParam) {
IStoredProc proc =
StoredProc.getStoredProc(getDBTransaction(), "somePLSQLProcedure");
proc.setStringValue("aStringParam", aStringParam);
proc.setIntegerValue("aIntegerParam", aIntParam);
proc.setDateValue("aDateParam", aDateParam);
proc.execute();
return proc.getStringValue("aOutParam");
}
Much better, isn't it?
Let's consider an example of invoking of a PL/SQL function. We have a PL/SQL function:
create or replace function somePLSQLFunction
(aStringParam Varchar2,
aIntegerParam Number,
aDateParam Date
) return Varchar2
And a Java method invoking this function is very simple:
public String
execSmartPLSQLFunction (String aStringParam,
Integer aIntParam,
Date aDateParam) {
IStoredProc proc =
StoredProc.getStoredProc(getDBTransaction(), "somePLSQLFunction");
proc.setStringValue("aStringParam", aStringParam);
proc.setIntegerValue("aIntegerParam", aIntParam);
proc.setDateValue("aDateParam", aDateParam);
proc.execute();
return proc.getStringValue("RESULT");
}
And what about working with complex SQL types? For example we have some SQL type representing an array of Varchar2:
CREATE OR REPLACE TYPE varchar2_array_type AS TABLE OF VARCHAR2(2000)
And our PL/SQL procedure accepts a parameter of this type:
create or replace procedure somePLSQLProcedure
(aStringParam Varchar2,
aIntegerParam Number,
aDateParam Date,
aArrayParam varchar2_array_type,
aOutParam out Varchar2
)
So our Java code should look like this:
public String
execSmartPLSQLProcedure(String aStringParam,
Integer aIntParam,
oracle.jbo.domain.Date aDateParam,
ArrayList<String> aArrayParam) {
IStoredProc proc =
StoredProc.getStoredProc(getDBTransaction(), "somePLSQLProcedure");
proc.setStringValue("aStringParam", aStringParam);
proc.setIntegerValue("aIntegerParam", aIntParam);
proc.setDateValue("aDateParam", aDateParam);
proc.setArrayValue("aArrayParam", aArrayParam,
"VARCHAR2_ARRAY_TYPE");
proc.execute();
return proc.getStringValue("aOutParam");
}
One of the principal features of this approach is that you know all necessary information about the procedure's parameters right after the execution of StoredProc.getStoredProc(...) . So you have the full list of parameters containing their names and data types. In order to retrieve this information from the database StoredProc class uses DatabaseMetaData method getProcedureColumns. This technique is described in details in this post.
The list of parameters allows us to control their names and types actually before execution of the procedure. Furthermore, we can assume that names of attributes of our Entity and names of parameters of PL/SQL procedures (inserting, updating and deleting the data) are equal and we can implement some method in an EntityImpl class performing DML operations:
private void
executeDMLprocedure(String procedureName) {
IStoredProc storedProc =
StoredProc.getStoredProc(getDBTransaction(), procedureName);
for
(IStoredProcParam param : storedProc.getParams()) {
if (param.isIn() || param.isInOut()) {
param.setParamValue(getAttribute(param.getParamName()));
}
}
storedProc.execute();
for
(IStoredProcParam param : storedProc.getParams()) {
if (param.isOut())
populateAttribute(getEntityDef().getAttributeIndexOf(
param.getParamName()),
param.getParamValue());
}
}
The source code of this tool is available here.
It can be deployed into ADF library and, perhaps, it can make your life a bit easier.
ADF Call Stored Procedure with Out Parameters
Step1 Create DB Procedure
stored_db_proc(Param1
IN VARCHAR2,
Param2 IN DATE,
x_errCode OUT VARCHAR2,
x_errMsg OUT VARCHAR2);
Step 2 Create Public/Protected method in ApplicationModule to call
import oracle.jbo.server.DBTransaction;
Param2 IN DATE,
x_errCode OUT VARCHAR2,
x_errMsg OUT VARCHAR2);
Step 2 Create Public/Protected method in ApplicationModule to call
import oracle.jbo.server.DBTransaction;
public
String[] callStoredProcOutParam(String p_param1,String p_param2Date) {
String
_xErrCode;
String
_xErrMsg;
DBTransaction
trx =(DBTransaction)getTransaction();
CallableStatement
st = null;
try {
// 1.
Define the PL/SQL block for the statement to invoke
String
stmt = "begin stored_db_proc (?,?,?,?); end;";
// 2.
Create the CallableStatement for the PL/SQL block
st =
trx.createCallableStatement(stmt,0);
// 3.
Register the positions and types of the OUT parameters
st.registerOutParameter(3,Types.VARCHAR);
st.registerOutParameter(4,Types.VARCHAR);
// 4.
Set the bind values of the IN parameters
st.setObject(1,p_param1);
st.setObject(2,
p_param2Date);
// 5.
Execute the statement
st.executeUpdate();
//6.
Set value of _xErrCode property using first OUT param
_xErrCode
= st.getString(3);
//7.
Set value of _xErrMsg property using 2nd OUT param
_xErrMsg
= st.getString(4);
// 8.
Return the result
return
new String[] {_xErrCode,_xErrMsg} ;
} catch
(SQLException e) {
throw new JboException(e);
} finally {
if (st != null) {
try {
// 9. Close the JDBC CallableStatement
st.close();
}
catch (SQLException e) {
e.getStackTrace();
} } } }
Step3 Expose callStoredProcOutParam method in PageDefBindings
Add method in Application Module Client
Double
click AppMOdule à Ckick Java à Client
Interface
(Note
: This will add Interface
public class csvAMClient extends ApplicationModuleImpl implements csvAM)
public class csvAMClient extends ApplicationModuleImpl implements csvAM)
Refresh Data Control to see newly added method.
Open Page Def Bindings, Click + Button , Select Method Action,
Click on
DataControl Select Method to be included.
Step 4 In Managed Bean Listner method
import oracle.binding.DataControl;
import oracle.binding.OperationBinding;
import oracle.binding.OperationBinding;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCBindingContainer;
........
FacesContext context = FacesContext.getCurrentInstance();
Application
app = context.getApplication();
DCBindingContainer binding = (DCBindingContainer)
app.evaluateExpressionGet(context,
"#{bindings}",DCBindingContainer.class);
// Call AM insContData Method
OperationBinding callAmMethod =
binding.getOperationBinding("callStoredProcOutParam
");
callAmMethod.getParamsMap().put("p_param1",Val1);
callAmMethod.getParamsMap().put("p_param2Date ",castToJBODate(Val2));
callAmMethod.getParamsMap().put("p_param2Date ",castToJBODate(Val2));
callAmMethod.execute();
Object
ReturnValue[] = (Object[])callAmMethod.getResult();
String _errCode
= (String)ReturnValue[0];
String _errMsg = (String)ReturnValue[1];
Invoking/Calling
Store Procedures from Oracle ADF Application
This post shows how you
can call a pl/sql stored procedure from ADF business component. Here I have
shown three scenario -> a. how you can call a procedure which does not have
any input and output parameter, b.
calling a procedure which has only input parameter but no output parameter and c. calling a procedure with both i/p and o/p parameter.
calling a procedure which has only input parameter but no output parameter and c. calling a procedure with both i/p and o/p parameter.
Double click on your
existing application module component. Select the Java node. Now select the
Application Module Class: AppModuleImpl > Generate Java File selectbox.
Click ok. Double click on Application Module Implementation java file [The file
with “Impl” name]. A Java file will open.
CALLING A PROCEDURE WITHOUT INPUT AND
OUTPUT PARAMETER
Let’s take one
simple procedure without Input and Output parameter like the following one
->
procedure sampleProcedure is
begin
insert into address (name,phone) values ('d','d');
commit;
end sampleProcedure;
For calling this
procedure, write one method in your AppModuleImpl.java class as follows ->
public void callStoreprocedureWithoutInput() {
String stmt =
"begin SAMPLEPROCEDURE; end;";
PreparedStatement st
= null;
try {
st = getDBTransaction().createPreparedStatement(stmt, 0);
st.executeUpdate();
st.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
Suppose we have a
simple procedure which takes some Input parameters.
PROCEDURE SAMPLEPROCEDURE4(nm varchar2,ph varchar2)
as
BEGIN
insert into Address (Name,Phone) values (nm,ph);
commit;
END;
For calling this
procedure write one method in your AppModuleImpl.java class as follows ->
public void callStoredProcedureWithInput(String name, String
phone) {
PreparedStatement st
= null;
Object[]
bindVariables={name,phone};
try {
st=getDBTransaction().createPreparedStatement("begin
SAMPLEPROCEDURE4(?,?) ;end;", 0);
if (bindVariables != null) {
for (int z = 0; z < bindVariables.length; z++) {
st.setObject(z + 1, bindVariables[z]);
}
}
st.executeUpdate();
} catch (SQLException
e) {
throw new JboException(e);
} finally {
if (st != null) {
try {
// 5. Close the statement
st.close();
} catch (SQLException e) {
}
}
}
}
}
Here we have added
the bind variables in PreparedStatement using setObject() function. After that
we have executed the query.
CALLING A PROCEDURE WITH INPUT AND
OUTPUT PARAMETERS
Suppose we have a
package where our function Function1 is defined as follows ->
package newPackage is
-- Public function and procedure declarations
function Function1(p_name varchar2) return varchar2;
end newPackage;
For calling this
procedure write one method in your AppModuleImpl.java class as follows
->
public String callStoreProcedureWithInputOutput(String name){
CallableStatement st
= null;
try {
String stmt = "begin newPackage.Function1(?); end;";
st = getDBTransaction().createCallableStatement(stmt,0);
st.registerOutParameter(1,Types.VARCHAR);
st.setObject(1,name);
st.executeUpdate();
st.getString(3);
System.out.println(" 1 "+st.getString(3));
return st.getString(3);
} catch (SQLException
e) {
throw new JboException(e);
} finally {
if (st != null) {
try {
st.close();
}catch (SQLException e) {}
}
}
}
EXPOSING APPLICATION MODULE CODES IN
PRESENTATION/VIEW LAYER
Double click on
Application Module component. Select Client Interface node. Place all available
written method in selected site.
In Oracle Adf we will doing DML operation by calling the
Procedure.Here the syntax of calling
Stored procedure is as follow:
Stored procedure is as follow:
Here
Procedure name is -- Vinayprocedure(empno,job,name)
StoredProcedureCall
call = new StoredProcedureCall();
call.setProcedureName("Vinayprocedure");
call.addUnamedArgument("empno");
call.addUnamedInOutputArgument("job",
"job", String.class);
call.addUnamedOutputArgument("name");
DataModifyQuery
query = new DataModifyQuery();
query.setCall(call);
query.addArgument("empno");
query.addArgument("job");
query.addArgument("name");
session.addQuery("Vinayprocedure
", query);
Vector
parameters = new Vector();
parameters.addElement(new Integer(7369));
parameters.addElement("engineer");
parameters.addElement("Vinay");
Object
data = session.executeQuery(query, parameters);
When we
are calling data from some view object, we can use this code
:-
VinayViewObject1Iterator=
view object iterator.
StoredProcedureCall
call = new StoredProcedureCall();
call.setProcedureName("Vinayprocedure2");
call.addNamedArgument("empno");
call.addNamedArgument("job");
call.addNamedArgument("name");
DataModifyQuery
query = new DataModifyQuery();
query.setCall(call);
query.addArgument("empno");
query.addArgument("job");
query.addArgument("name");
Vector
parameters = new Vector();
DCIteratorBinding
iteratorobject= findIterator("VinayViewObject1Iterator");
RowSetIterator
rowObject = iteratorobject.getRowSetIterator();
rowObject.setRangeSize(rowObject.getRowCount());
RowKeySet
keysetobject = this.getTable().getSelectionState();
Set
keySetobject = keysetobject.getKeySet();
Iterator
itrobject = keysetobject.iterator();
while (itrobject.hasNext())
{
Integer
intr = (Integer)itr.next();
VinayViewObjectRowImpl
row =
(VinayViewObjectRowImpl)keysetobject.getRowAtRangeIndex(intr.intValue());
parameters.addElement(row.getAttribute(“empno));
parameters.addElement(row.getAttribute(“job”));
parameters.addElement(row.getAttribute(“name”));
session.addQuery("Vinayprocedure2",
query);
Object
data = session.executeQuery(query, parameters);
parameters.clear();
|
No comments:
Post a Comment