I have a table in an Oracle database where three columns are automatically populated by a trigger when a row in inserted. To retrieve those values when the insert happens, I am using some procedural code but it doesn't exist as a named stored procedure. The code looks something like this but with more outputs.
DECLARE widgetId NUMBER;
BEGIN INSERT INTO widget(foo) VALUES('bar')
RETURNING widget_id INTO widgetId;
END;
Does Spring JdbcTemplate provide any clean, convenient way of calling that code? I see where JdbcTemplate supports calling a stored procedure but that seems to require that the code live in the database and can be called by name. JdbcTemplate's KeyHolder seems promising but I'm not entirely sure if it can be used to retrieve an arbitrary number of values generated by a trigger. I also haven't had much luck finding anything but simple examples. Does anyone have any insight into a clean way of calling that code using JdbcTemplate?
To be called the code will have to be a stand-alone procedure or function, or will have to be an externally visible procedure or function in a package. As far as I'm aware there's no way to execute anonymous code.
Share and enjoy.
Related
First of all usually I am working with MSSQL. But I have a stored procedure in MSSQL, which I need to use in Oracle now and since I am absolutely new to Oracle I have no idea at all how to do it correct.
I needed to use user defined table types in my MS SQL stored procedure because I am using "logical" tables in my stored procedure, which I also need to pass them to a dynamic sql statement within this procedure (using column names of "physical" tables as variables/parameters).
I've started to add the oracle function in a package I made before for another function. It looks like
TYPE resultRec IS RECORD
(
[result columns]
);
TYPE resultTable IS TABLE OF resultRec;
Function MyFunctionName([A LOT PARAMETERS]) RETURN resultTable PIPELINED;
I also described the layout of the tables (the user defined table types in MSSQL), which I want to use within this function in this package header.
So far so good, but now I don't really know where I have to declare my table variables or user defined table types. I also tried to put them in the package header, but if I am trying to use these tables in the package body, where I am describing my function, Oracle tells met, that the table or view does not exist.
I also tried it to describe the tables within the package body or in the block of my function, which looks like that:
FUNCTION MyFunctionName
(
[MyParameters]
)
RETURN resultTable PIPELINED is rec resultrec;
TYPE tableVariableA IS TABLE OF tableRecA;
TYPE tableVariableB IS TABLE OF tableRecB;
BEGIN
INSERT INTO tableVariableA
SELECT ColumnA, ColumnB FROM physicalTable WHERE[...];
[A LOT MORE TO DO...]
END;
But in this case Oracle also tells me, that it doesn't know the table or view.
I also tried a few more things, but at the end I wasn't able to tell Oracle what table it should use...
I would appreciate every hint, which helps me to understand how oracle works in this case. Thanks a lot!
You can't insert into a collection (e.g. PL/SQL table). You can use the bulk collect syntax to populate the collection:
SELECT ColumnA, ColumnB
BULK COLLECT INTO tableVariableA
FROM physicalTable
WHERE [...];
However, you might want to check this is an appropriate approach, since SQL Server and Oracle differ quite a bit. You can't use PL/SQL tables in plain SQL (at least prior to 12c), even inside your procedure, so you might need a schema-level type rather than a PL/SQL type, but it depends what you will do next. You might not really want a collection at all. Trying to convert T-SQL straight to PL/SQL without understanding the differences could lead you down a wrong path - make sure you understand the actual requirement and then find the best Oracle mechanism for that.
We know that it is possible to dynamically figure out the name of the procedure or package that is currently executing as explained here and here. This generally applies to statements being executed from other stored procedures (compiled) in the database.
The problem:
We have been trying to log all UPDATE activity on a specific column (called STATE) by placing a trigger on the table and invoking who_called_me from within the trigger. The purpose of doing this is apparently as per the application design the column STATE could get updated by multiple pieces of code (residing in the database) based on certain business conditions. In addition to that, the column could also get updated by the application which is a hibernate based application and at times when the update happens by a hibernate query the who_called_me function returns nothing. There are multiple parts in the application that could also UPDATE the column STATE based on certain conditions.
The who_called_me strategy is working well for us in cases where a stored procedure (which resides in the database) issues the UPDATE statement and who_called_me is successfully capturing the corresponding owner, name, line no. etc. of the stored procedure. But in case the UPDATE happens from hibernate, the function captures no details.
Is there a way to capture which hibernate query UPDATEd the row through the trigger? Or is there any other way?
Note: The trigger code is similar to the answer posted on this question.
you can track the query with ora_sql_text function, e.g. this is the function I use for that:
-- getting sql code, which is calling the current event, as clob
function getEventSQLtext
return clob
is
sqllob clob;
sql_text ora_name_list_t;
dummy integer;
begin
dummy := ora_sql_txt(sql_text);
dbms_lob.createtemporary(sqllob,false);
for i in 1..sql_text.count loop
dbms_lob.writeappend(sqllob,length(sql_text(i)),sql_text(i));
end loop;
return sqllob;
if dummy is null then null; end if; -- removing warning of non-used variable :)
end;
This will be a query which is generated by hibernate and this is the only information you can get because this should be the only thing hibernate can do with DB.
It turns out, the who_called_me approach works better for stored procedure calls where the stack trace can point exactly which line invoked a DML. In, case of hibernate it is possible that the code may not call a stored procedure but in-turn may have individual DMLs which get invoked based on certain conditions. As opposed to other answer given by #simon, the ora_sql_txt function may only work in system event triggers or I may be wrong, but either way it is not capable of capturing the SQL Statement issued by Hibernate (tested that it does not works and retunrs a NULL value).
So at the end of the day, to find what SQL Hibernate is using, DB Trace files and Hibernate debug level logs is the only way for now.
I am constructing a rather large Oracle 10g package full of functions etc.
The problem is some of these functions are pulling information from materilized view's or tables that other functions are creating.
Is there a way to successfully compile a package even though some of the functions cannot find the information they are looking for (But these functions will work once the views have been created?).
Attempts:
I have looked into PRAGMA RESTRICT_REFERENCES but have had no success so far. Am I even on the right track or is this not even possible?
You cannot refer using static SQL to objects that do not exist when the code is compiled. There is nothing you can do about that.
You would need to modify your code to use dynamic SQL to refer to any object that is created at runtime. You can probably use EXECUTE IMMEDIATE, i.e.
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM new_mv_name'
INTO l_cnt;
rather than
SELECT COUNT(*)
INTO l_cnt
FROM new_mv_name;
That being said, however, I would be extremely dubious about a PL/SQL implementation that involved creating any new tables and materialized views at runtime. That is almost always a mistake in Oracle. Why do you need to create new objects at runtime?
I just discovered Oracle's ANYDATA and ANYTYPE and thought it would be useful to generic-ify some very repetitive code I have. I tried a very simple code sample that looks like this:
declare
--simple test query
cursor myCurs is
select sysdate from dual;
type t_arr is table of myCurs%rowtype;
v_arr t_arr;
v_anydata ANYDATA;
begin
open myCurs;
fetch myCurs bulk collect into v_arr;
close myCurs;
dbms_output.put_line(v_arr.count || ' records');
v_anydata := ANYDATA.convertCollection(v_arr);
dbms_output.put_line(anydata.gettypename(v_anydata));
end;
/
Running this code gives me the error "ORA-22370: incorrect usage of method ORA-22370 incorrect ussage of method ConvertCollection" (Yes, it actually repeats ORA-22370).
My question is: Is it possible to use ANYDATA on collections of rowtypes? I ask this because all examples I've seen of ANYDATA (so far) with collections use object types, none use rowtypes, but I haven't seen anything (yet) that explicitly says that rowtypes can't be used.
My goal was to clean up some very repetitive bulk-collect/insert code that does the same thing over and over, but with different cursors for different tables.
(Oracle 10g)
ANYDATA works with SQL data_types, i.e. anything you could use to define a table column.
%ROWTYPE is a PL/SQL construct, and so is not recognised by the SQL engine. If you think about the dynamic nature of %ROWTYPE you'll be able to figure out why.
Wanting to clean up repetitive code is a noble idea. I have previously implemented an ETL processor in SQL Types which used a supertype Object to own the generic processing, and subtypes to execute table specific code (such as inserts). These subtypes are completely generatable: not as slick as a dynamic %ROWTYPE implementation would have been, but still easy enough.
I have a PL/SQL function in an Oracle database that I can't change. This function takes a parameter which identifies an entity, creates a copy of that entity and then returns the ID of the copy. This looks something like
FUNCTION copy_entity(id IN NUMBER) RETURN NUMBER
I need to call this function from Hibernate. I tried creating a named SQL query with something similar to
CALL copy_entity(:id)
as the query, but from this I can't seem to get the return value of the function. Hibernate's "return-scalar" and similar options require a column name to return and I don't have a column name. This lead me to
SELECT copy_entity(:id) AS newEntityId
with "return-scalar" using newEntityId as column name, but this also did not work since Oracle then throws an exception that I can't call INSERT (to save the copy) in a SELECT.
Is there any way to get the return value of such a PL/SQL function? The function is actually much more complex and still required in other parts of the app, so re-writing it is not really an option.
I hope/think you can use an anonymous PL/SQL block:
begin
:myresult = copy_entity(:id);
end;
Now you have 'column name' myresult with the result.
I've never used hibernate so I hope it works. I don't know how flexible Hibernate is.
I think you are stuck using straight JDBC. The Hibernate documentation has this in the limitations section for Oracle:
For Oracle the following rules apply:
A function must return a result set.
The first parameter of a procedure
must be an OUT that returns a result
set. This is done by using a
SYS_REFCURSOR type in Oracle 9 or 10.
In Oracle you need to define a REF
CURSOR type. See Oracle literature for
further information.
Since this function accepts a number and returns a number you are out of luck with Hibernate and would probably be better off making a simple JDBC CallableStatement.