Oracle Stored Procedure with out parameter using Nhibernate - oracle

How can I access the value of an out parameter of an oracle stored procedure in the .net code - Oracle stored procedure being called via Nhibernate?
Sample working code would help.

You have to use the latest version of NHibernate (2.1.2).
<sql-query name="ReturnSomethig" callable="true">
<return class="Somethig" />
{ call ReturnSomethig(:someParameter) }
</sql-query>
The Oracle Stored Procedure need to has the first parameter as a out sys_refcursor parameter.
And you can call the named query like that:
IQuery query = currentSession.GetNamedQuery("ReturnSomethig");
query.SetInt64("someParameter", someParameter);
var somethig = query.List<Somethig>();
And it will work.

I tried the 2.1.2 libraries without much luck. I had to actually make some modifications to the library based on this article. If you go this route, you'll want to make sure you are using the Oracle.DataAccess dll since it won't work with System.DataAccess.OracleClient dll.

Whilst looking into getting out parameters from NHibernate I found various links suggesting that to get a stored proc out parameter value (rather than using a refcursor) you need to construct the command in code rather than using the XML mappings file.
But I found difficulty finding a complete working example online. It's not a beautiful solution, but I post in case it helps someone.
Example Oracle stored proc:
create or replace PACKAGE BODY MY_PACKAGE AS
PROCEDURE add_one
(
p_out out number,
p_in in number
) AS
BEGIN
select p_in + 1 into p_out from dual;
END add_one;
END MY_PACKAGE;
Example C# code, where sessionFactory is an NHibernate ISessionFactory:
using (var session = sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var command = session.Connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "MY_PACKAGE.add_one";
var parmOut = command.CreateParameter();
parmOut.ParameterName = "p_out";
parmOut.DbType = DbType.Decimal;
parmOut.Direction = ParameterDirection.Output;
command.Parameters.Add(parmOut);
var parmIn = command.CreateParameter();
parmIn.ParameterName = "p_in";
parmIn.DbType = DbType.Decimal;
parmIn.Direction = ParameterDirection.Input;
parmIn.Value = 67;
command.Parameters.Add(parmIn);
transaction.Enlist(command);
command.ExecuteNonQuery();
int sixtyEight = (int) (decimal) parmOut.Value;
}

See this comment by Richard Brown. Some sample code can be found here.
Unfortunately I can't test it so I don't know whether it works or not.

A belated reply.
For oracle, out parameter is not supported by nHibernate unless it is a cursor. If you just want a scalar value, a work around is to wrap your stored procedure with an oracle function.
Then you can do this
<sql-query name="TestOracleFunction" callable="true">
<return-scalar column="MyOutputValue" type="String" />
<![CDATA[
select MyOracleFunction as MyOutputValue from dual
]]>
</sql-query>
This works!

Related

Error: Parameter Type is not supported in ADODB code of classic ASP

I have written code to insert call parameterized stored procedure written in oracle pl/sql. I have given all parameters properly as displayed in below code.
function CallSp(str_id, ref_no, note, userId, strdatestamp, writtenDate)
Dim strcon2 : set strcon2=server.createObject("ADODB.Connection")
Dim strcmd2
Dim sql2
Dim ReturnVal
strcon2.Open "Proper Connectionstring provided here"
sql2 = "Fr_Store_Notes"
Set strcmd2 = Server.CreateObject("ADODB.Command")
Set strcmd2.ActiveConnection = strCOn2
strcmd2.CommandText = sql2
strcmd2.CommandType = 4
strcmd2.Parameters.Refresh
strcmd2.Parameters.Append strcmd2.CreateParameter("p_str_id", 12,1)
strcmd2.Parameters("p_str_id") = str_id
strcmd2.Parameters.Append strcmd2.CreateParameter("p_ref_no", 12,1)
strcmd2.Parameters("p_ref_no") = ref_no
strcmd2.Parameters.Append strcmd2.CreateParameter("p_UserId", 12,1)
strcmd2.Parameters("p_UserId") = userId
strcmd2.Parameters.Append strcmd2.CreateParameter("p_note", 12,1)
strcmd2.Parameters("p_note") = note
strcmd2.Parameters.Append strcmd2.CreateParameter("p_Datestamp", 12,1)
strcmd2.Parameters("p_Datestamp") = strdatestamp
strcmd2.Parameters.Append strcmd2.CreateParameter("p_WrittenDate", 12,1)
strcmd2.Parameters("p_WrittenDate") = writtenDate
strcmd2.Parameters.Append strCmd2.CreateParameter("p_return", 3, 2)
strcmd2.Execute
ReturnVal = strcmd2.Parameters("p_return").Value
CallSp=ReturnVal
set strCmd2=Nothing
strCon2.close
end function
But I am receiving error as
Parameter Type is not supported at the line strcmd2.Execute
Database stored procedure is as like below and working fine if we execute it from database
create or replace
procedure Fr_Store_Notes (
P_STR_ID IN VARCHAR2,
p_Ref_no in VARCHAR2,
P_UserId in VARCHAR2,
P_Note IN VARCHAR2,
P_datestamp IN VARCHAR2,
p_WrittenDate IN VARCHAR2,
p_return OUT number)
AS
BEGIN
--Expected Code Block is there and working fine
End;
Can anyone help me in sorting out this issue
Update: - Apparently after a bit of research (as I don't work with Oracle) ADODB doesn't support adVariant (which is 12) and you should use adVarChar (which is 200) instead.
See A: Classic ASP calling Oracle stored procedure with OraOleadb Driver
Leaving the rest of the answer below as it's probably still relevant once this issue is fixed.
The cause is of that particular error is usually a mismatch of data type once the ADODB talks to the provider defined by the connection.
Just looking at the procedure definition in Oracle in comparison to your ADODB.Command object I can see that the p_return parameter appears to be incorrect. I talk about this in a previous answer to a similar question.
According to Data Type Mapping (a great resource for Data Type Mapping in ADO) adInteger (which is 3) maps to Int in Oracle not Number. Instead, you should use adNumeric (which is 131) which should fix that particular error.
Try changing this line
strcmd2.Parameters.Append strCmd2.CreateParameter("p_return", 3, 2)
to
strcmd2.Parameters.Append strCmd2.CreateParameter("p_return", 131, 2)
Useful Links
A: Using Stored Procedure in Classical ASP .. execute and get results
A: ADODB.Parameters error '800a0e7c' Parameter object is improperly defined. Inconsistent or incomplete information was provided (recommend this to learn how to use METADATA in global.asa to have ADO Named Constants always available to an ASP Web Application)

how to call oracle stored procedure in jpa2.0 which returns integer as out variable

I have written one stored procedure which will return integer value. but am not able to get the count in jpa. can any one suggest me the way to call stored procedure and get the return out value into some variable in java using jpa.
int count = 0;
String id = "m123"
count = getEm().createNativeQuery("call sample_procedure(?,?)")
.setParameter(1, id)
.setParameter(2, count)
.executeUpdate();
stored procedure: I have some logic in procedure and inside loop I was incrementing the count.
create or replace
PROCEDURE sample_procedure(
id IN VARCHAR,
count OUT NUMBER)
IS
........
BEGIN
.....
LOOP
---------
count := count + 1;
Looks similar to this answer
Options which I tried:
Tried with curly braces before the call statement.
Query q = entitymanager.createNativeQuery("{call sample_procedure(?,?)}").setParameter(1, inparam).setParameter(2, outparam);
q.executeUpdate();
It did not work. Did not return the output to java class.
Tried with this:
Query q = entitymanager.createNativeQuery("{call sample_procedure(?,?)}", Integer.class).setParameter(1, inparam).setParameter(2, outparam);
q.executeUpdate();
It did not work either. Did not return the output to java class.
Tried with this as well:
Query q = entitymanager.createNativeQuery("{call sample_procedure(?,?)}").setParameter(1, inparam).setParameter(2, outparam);
q.getSingleResult();
This also threw an exception: Cannot perform an update or delete operation on select query: "{call sample_procedure(?,?)}".
I believe it is not possible to do it like this as mentioned by Sean Patrick Floyd in the answer link i have provided above.(Note: I do not have the book :)) If you need to process your out parameter and if that is the reason you need it, you need to handle it logically. Seems its not possible in jpa2.0
I have some logic in procedure and inside loop I was incrementing the count.
First, in you given use case, count has to be declared INOUT (both input and output).
PROCEDURE sample_procedure(
id IN VARCHAR,
count INOUT NUMBER)
Then:
For JPA < 2.1 don't have support for getting back values from OUT or INOUT parameters. So, you are probably stuck if you need to stay with that version.
JPA >= 2.1 has explicit support for calling stored procedure using the EntityManager.html.createStoredProcedureQuery method:
count = getEm().createStoredProcedureQuery("sample_procedure")
.registerStoredProcedureParameter(1, String.class, ParameterMode.IN)
.registerStoredProcedureParameter(2, Integer.class, ParameterMode.INOUT)
.setParameter(1, id)
.setParameter(2, count);
storedProcedure.execute();
int count = (Integer)storedProcedure.getOutputParameterValue(2);
Untested. Beware of typos !

Calling an oracle procedure with optional parameter with nhibernate

I have a procedure like this:
Procedure MyProc(param1 IN VARCHAR2 default 'default_value', param2 IN VARCHAR2 default null);
To debug this in PL-SQL i'm doing:
MyPackage.MyProc(param2 => '20130301');
And it's mapped in Nhibernate inside a hbm file:
<sql-query name="MyMappedProc">
<![CDATA[ call MyPackage.MyProc(param2 => :parametro2); ]]>
</sql-query>
and being called in application:
_Query = Session.GetNamedQuery("MyMappedProc");
_Query.SetParameter("parametro2", "value_var");
var lista = _Query.List<object>();
And I'm having this error:
{"ORA-00907: missing right parenthesis"}
Is there a way to call a procedure with optional parameters with NHibernate?
Thanks.
How I'm doing this in production code right now
<sql-query name="Recalc">
CALL P_DATA.RECALC.CONST_QUERY(:map, :id)
</sql-query>
and then from C# code
session.GetNamedQuery("Recalc").SetParameter("id", this.id)
.SetParameter("map", "MyMap").ExecuteUpdate();
As usual ShowSQL or the marvelous Ayende's NHibernate profiler are your friends

How to return a RefCursor from Oracle function?

I am trying to execute a user-defined Oracle function that returns a RefCursor using ODP.NET. Here is the function:
CREATE OR REPLACE FUNCTION PKG.FUNC_TEST (ID IN TABLE.ID%type)
RETURN SYS_REFCURSOR
AS
REF_TEST SYS_REFCURSOR;
BEGIN
OPEN REF_TEST FOR
SELECT *
FROM TABLE;
RETURN REF_TEST;
END;
/
I can call this function in Toad (select func_test(7) from dual) and get back a CURSOR. But I need to get the cursor using C# and ODP.NET to fill a DataSet, but I keep getting a NullReferenceException - "Object reference not set to an instance of an object". Here is what I have for that:
OracleConnection oracleCon = new OracleConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
OracleCommand sqlCom = new OracleCommand("select func_test(7) from dual", oracleCon);
sqlCom.Parameters.Add("REF_TEST", OracleDbType.RefCursor, ParameterDirection.ReturnValue);
OracleDataAdapter dataAdapter = new OracleDataAdapter();
dataAdapter.SelectCommand = sqlCom;
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet); //FAILS HERE with NullReferenceException
I was able to find lots of info and samples on using stored procedures and ODP.NET, but not so much for returning RefCursors from functions.
EDIT: I do not want to explicitly add input parameters to the OracleCommand object (i.e. sqlCom.Parameters.Add("id", OracleDbType.Int32,ParameterDirection.Input).Value = 7;) as that makes it difficult to implement this as a generic RESTful web service, but I'm reserving it as my last resort but would use stored procedures instead.
Any help is much appreciated!
I think you are missing the sqlCom.ExecuteNonQuery();
also, instead of running the select func_test(7) from dual; lets switch it to run the function and pass in the param
OracleConnection oracleCon = new OracleConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
// Set the command
string anonymous_block = "begin " +
" :refcursor1 := func_test(7) ;" +
"end;";
//fill in your function and variables via the above example
OracleCommand sqlCom= con.CreateCommand();
sqlCom.CommandText = anonymous_block;
// Bind
sqlCom.Parameters.Add("refcursor1", OracleDbType.RefCursor);
sqlCom.Parameters[0].Direction = ParameterDirection.ReturnValue;
try
{
// Execute command; Have the parameters populated
sqlCom.ExecuteNonQuery();
// Create the OracleDataAdapter
OracleDataAdapter da = new OracleDataAdapter(sqlCom);
// Populate a DataSet with refcursor1.
DataSet ds = new DataSet();
da.Fill(ds, "refcursor1", (OracleRefCursor)(sqlCom.Parameters["refcursor1"].Value));
// Print out the field count the REF Cursor
Console.WriteLine("Field count: " + ds.Tables["refcursor1"].Columns.Count);
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
finally
{
// Dispose OracleCommand object
cmd.Dispose();
// Close and Dispose OracleConnection object
con.Close();
con.Dispose();}
this is based on the example ODP that can be found # %ora_home%\Client_1\ODP.NET\samples\RefCursor\Sample5.csproj
If you want to avoid (for better or worst!) the custom built param collection for each proc/function call you can get around that by utilizing anonymous blocks in your code, I have ammended (once again untested!) the code above to reflect this technique.
Here is a nice blog (from none other than Mark Williams) showing this technique.
http://oradim.blogspot.com/2007/04/odpnet-tip-anonymous-plsql-and.html

what are the OleDbTypes associated with Oracle Number and varchar2 when calling a function

I'm trying to map OleDb parameters to an Oracle Function. I was able to do this using the System.Data.Oracle namespace but then found that this is depricated, so I thought i would re-write it as OldDb to avoid installing the Oracle Provider.
I have defined the following oracle function as an example:
create function GetImagePath (AIRSNumber in number)
return varchar2
is
begin
return '\\aiimg524\images\Ofndrtrk\2010\01\0kvrv1p000lcs74j';
end;
and I'm calling it using the following code:
using (var command = new OleDbCommand())
{
command.Connection = con;
command.CommandText = ConfigurationManager.AppSettings[OTRAK_PHOTO_FUNC];
command.CommandType = CommandType.StoredProcedure;
string parm = ConfigurationManager.AppSettings[OTRAK_PHOTO_PARM];
command.Parameters.Add(parm, OleDbType.Decimal); // maps to oracle Number
command.Parameters[parm].Direction = ParameterDirection.Input;
command.Parameters[parm].Value = airsNumber;
command.Parameters.Add(RETURN_VALUE, OleDbType.Variant); // maps to Oracle varchar2
command.Parameters[RETURN_VALUE].Direction = ParameterDirection.ReturnValue;
try
{
con.Open();
command.ExecuteNonQuery();
path = command.Parameters[RETURN_VALUE].Value.ToString();
}
I tried a bunch of different OleDB types for the parameter and the return value. the current attempt is from a mapping table i found on the web that said number = decimal and varchar2 = variant. I'm about to try every permutation of types in the enum and wanted to ask for help. the not so useful error message i get is:
[System.Data.OleDb.OleDbException] = {"ORA-06550: line 1, column 7:\nPLS-00306: wrong number or types of arguments in call to 'GETIMAGEPATH'\nORA-06550: line 1, column 7:\nPL/SQL: Statement ignored"}
This actually had nothing to do with the type of the parameters but the order. Using the OleDb provider for Oracle does not respect the names of the parameters in the parameter collection but rather the order that the parameters are added. Wwhen calling an oracle function, the return value is a free parameter that must be declared first. by adding my return value parameter and then the actual function parameter things started working.
using the command.Parameters.AddWithValue(parm, value) also simplifies things a bit.

Resources