EclipseLink: call oracle StoredFunc with IN table type - oracle

I'm trying to call an oracle stored function using eclipselink 2.6.5, the storedfunction has an oracle type input.
This is te stored func:
create or replace function TEST_FUNC(PAR1 IN VARCHAR2, PAR2 IN MY_TYPE_T)
return varchar2 is
begin
if PAR2 is null then
return (PAR1 || ' 0');
else
return(PAR1 || ' ' || PAR2.count);
end if;
end TEST_FUNC;
This is type declaration:
CREATE OR REPLACE MY_TYPE_T as table of MY_TYPE_R
CREATE OR REPLACE TYPE MY_TYPE_R as object
(
COD_P VARCHAR2(3),
COD_S VARCHAR2(10)
)
This is the type mapping:
#Embeddable
#Struct(name="MY_TYPE_R", fields= {"COD_P", "COD_S"})
#PLSQLTable(
name="MY_TYPE_R",
compatibleType="MY_TYPE_T",
nestedType="MY_TYPE_R"
)
public class MyRecordType {
#Column(name="COD_P")
private String codP;
#Column(name="COD_S")
private String codS;
//costructors, getter and setter...
}
This is the stored mapping:
#NamedPLSQLStoredFunctionQuery(
name="testFunc",
functionName="TEST_FUNC",
returnParameter=#PLSQLParameter(
name="RESULT",
databaseType = "VARCHAR_TYPE"),
parameters = {
#PLSQLParameter(name = "firstParam", queryParameter="PAR1", databaseType = "VARCHAR_TYPE"),
#PLSQLParameter(name = "secondParam", queryParameter="PAR2", databaseType = "MY_TYPE_T"),
}
)
This is the call:
#Test
public void testFuncTest() {
List<MyRecordType> recTypeList = new ArrayList<MyRecordType>();
MyRecordType rec = new MyRecordType();
rec.setCodP("PValue");
rec.setCodS("SValue");
recTypeList.add(rec);
Query query = getEM().createNamedQuery("testFunc");
query.setParameter("firstParam", "FOO");
query.setParameter("secondParam", recTypeList);
Assert.assertEquals("FOO 1", query.getSingleResult());
}
When I execute the test I get:
PLS-00306: wrong number or types of arguments in call to 'TEST_FUNC'
Actually reading the documentation on #PLSQTable I'm a bit confused on the tree parameters:
name
compatibleType
nestedType
Anyone has some suggestion?

First you'll want to use the OracleObject annotation to define your fields into an Oracle object. It's somewhat duplicate to Struct but required anyways.
#OracleObject(
name = "MY_TYPE_R"
, javaType = MyRecordType.class
, fields = {
#PLSQLParameter(name = "COD_P")
,#PLSQLParameter(name = "COD_S")
}
)
For the PLSQLTable you want
#NamedPLSQLStoredFunctionQuery(
name="testFunc",
functionName="TEST_FUNC",
returnParameter=#PLSQLParameter(
name="RESULT",
databaseType = "VARCHAR_TYPE"),
parameters = {
#PLSQLParameter(name = "PAR1", queryParameter="PAR1", databaseType = "VARCHAR_TYPE"),
#PLSQLParameter(name = "PAR2", queryParameter="PAR2", databaseType = "MY_TYPE_T"),
}
)
so the name needs to match what's in your function. The PLSQL table piece looks right, I believe the OracleObject part was your missing component and causing your error.

Related

spark jdbc api can't use built-in function

I want to get subquery from impala table as one dataset.
Code like this:
String subQuery = "(select to_timestamp(unix_timestamp(now())) as ts from my_table) t"
Dataset<Row> ds = spark.read().jdbc(myImpalaUrl, subQuery, prop);
But result is error:
Caused by: java.sql.SQLDataException: [Cloudera][JDBC](10140) Error converting value to Timestamp.
I can use unix_timestamp function,but to_timestmap failed, why?
I found code in org.apache.spark.sql.execution.datasources.jdbc.JDBC.compute() exists some problem:
sqlText = s"SELECT $columnList FROM ${options.table} $myWhereClause"
$columList contains " like "col_name" , when I delete " it work fine.
I solve this problem by add dialect, default dialect will add "" to column name,
JdbcDialect ImpalaDialect = new JdbcDialect(){
#Override
public boolean canHandle(String url) {
return url.startsWith("jdbc:impala") || url.contains("impala");
}
#Override
public String quoteIdentifier(String colName) {
return colName;
}
};
JdbcDialects.registerDialect(ImpalaDialect);

Unable to call stored procedure in oracle through JDBC

I am trying to call a stored procedure where i am inserting 7 values into a table. But the below code is not working, please tell what am i doing wrong ?
i do not get any error, the page just remains static though after successful query execution it is suppose to redirect to a new page.
public class admincontrol extends TagSupport
{
HttpServletRequest request;
HttpServletResponse response;
String msg="";
public int doStartTag() throws JspException
{
request=(HttpServletRequest)pageContext.getRequest();
response=(HttpServletResponse)pageContext.getResponse();
return EVAL_PAGE;
}
public void check ()
{
JspWriter out=pageContext.getOut();
Connection con;
CallableStatement stmt;
ResultSet rs;
try
{
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException ex)
{
out.println(ex.getMessage());
}
HttpSession mysession=request.getSession();
String sess=(String)mysession.getAttribute("user");
String rr=(String)adminmodel.time.trim();
String tempid=(String)adminmodel.employeid.trim();
String tdept=(String)adminmodel.department.trim();
String tsup=(String)adminmodel.supervisor.trim();
String tact=(String)adminmodel.action.trim();
String tdate=(String)adminmodel.date.trim();
HttpSession session1=request.getSession();
session1.setAttribute("requestnum",rr);
Random rand = new Random();
int r= rand.nextInt(80001) + 19999;
String reff = String.valueOf(r);
if (!tempid.matches(".*[%#^<>&;'\0-].*") && !tdept.matches(".*
[%#^<>&;'\0-].*") && !tsup.matches(".*[%#^<>&;'\0-].*"))
{
if (tempid.equals(sess) )
{
if (adminmodel.department!="" && adminmodel.supervisor!="" && adminmodel.action!="" && adminmodel.date!="" && adminmodel.time!="")
{
try
{
con= DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521:XE","gaurav","oracle");
stmt=con.prepareCall("begin requestdetail (?,?,?,?,?,?,?); end;");
stmt.setString(1,tempid);
stmt.setString(2,tsup);
stmt.setString(3,tdept);
stmt.setString(4,tact);
stmt.setString(5,tdate);
stmt.setString(6,rr);
stmt.setString(7,reff);
rs=stmt.executeQuery();
response.sendRedirect("requestnum.jsp");
}
catch(SQLException ex)
{
out.println(ex.getMessage());
}
catch(Exception ex)
{
out.println(ex.getMessage());
}
}
else
out.println("Enter complete details");
}
else
out.println("Incorrect Employee Id");
}
else
out.println("Invalid Details ");
}
catch(Exception ex)
{
}
}
public int doEndTag() throws JspException
{
check();
return super.doEndTag();
}
}
Below is the stored procedure
create or replace procedure requestdetail (id number, sup varchar2, department varchar2,aaction varchar2, adate number,atime number, ref number)
is
begin
insert into myadmin(employe_id,supervisor,department,action,sdate,stime,reference_no)values (id,sup,department,aaction,adate,atime, ref);
end;
/
You can't use a procedure in a query, only a function. To execute a procedure you need to do:
stmt=con.prepareCall("{ call requestdetail (?,?,?,?,?,?,?) }");
stmt.setString(1,tempid);
stmt.setString(2,tsup);
stmt.setString(3,tdept);
stmt.setString(4,tact);
stmt.setString(5,tdate);
stmt.setString(6,rr);
stmt.setString(7,reff);
rs=stmt.execute();
Or if you prefer you can pass a PL/SQL block:
stmt=con.prepareCall("begin; requestdetail (?,?,?,?,?,?,?); end;");
You will also need to commit at some point, unless you have auto commit turned on for the connection.
Also, you're setting all the parameters using setString, and you said in a comment that the table was created with all varchar2 columns; but your procedure is expecting numbers for the id, adate and atime columns:
create or replace procedure requestdetail (id number, sup varchar2,
department varchar2, aaction varchar2, adate number, atime number,
ref number) is
That will get a numeric or value error if the values you pass in for tempid, tdate and rr are not actually numbers. It looks like you're expecting tempid at least to be a string.
But if they are numeric (or, as the name suggests, date) values then your table columns should be of the appropriate type anyway, not all varchar2. Always use the correct data type; don't try to store numbers or dates as strings, it will only cause you pain later. It affects performance as Oracle can't optimise execution plans and indexes based on the actual data type. But more importantly because you can get invalid or just bad data, which you might not notice until much later, and will struggle to correct.
If someone inserts a string into your 'number' field nothing will stop them; but at some point when you query with where adate = 1 it'll throw an error. Same with dates, but even worse depending on the format(s) used - even with a single format, if you think everything is DD/MM/YYYY and someone puts in a value by mistake as MM/DD/YYYY, again you won't know; and it'll either work when retrieved (if the DD and MM are both <= 12) but have the wrong value and you will have no way to tell; or it'll fail when retrieved. If the column was DATE then it's up to the person inserting to get it right, not up to you to try and fix mistakes when you retrieve the data.

passing an object having array to oracle stored procedure

I've a class "User" as follows:
public class User{
private String firstname;
private String lastname;
private String[] departments;
}
I want to pass an object of User type to my oracle stored procedure that takes an object type as parameter. So I did this to achieve my solution:-
1. Created USER_SEARCH_OBJ as a type in database as follows:-
create or replace
TYPE USER_SEARCH_OBJ AS OBJECT (
FIRST_NAME VARCHAR2(256),
LAST_NAME VARCHAR2(256),
DEPARTMENTS TABLE_OF_VALUES,
);
2. Created TABLE_OF_VALUES as a type in database as follows:-
create or replace TYPE TABLE_OF_VALUES AS TABLE OF VARCHAR2(20);
3. Passed the User object from Java class as follows:-
Object[] departments = {"1","2"};
StructDescriptor objDescriptor = StructDescriptor.createDescriptor("USER_SEARCH_OBJ", conn.getMetaData().getConnection());
ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor("TABLE_OF_VALUES", conn.getMetaData().getConnection());
ARRAY departmentArr = new ARRAY(arrayDescriptor, conn.getMetaData().getConnection(), departments );
Object[] userProperties = new Object[2];
userProperties [0] = "paras";//first_name
userProperties [1] = "anand";//last_name
userProperties [2] = departmentArr ;//department array
STRUCT searchObj = new STRUCT(objDescriptor, conn.getMetaData().getConnection(), userProperties );
CallableStatement cStmt = conn.prepareCall("PCK_SEARCH2.USER_SEARCH(?,?)");
cStmt.setObject(1, searchObj);
cStmt.registerOutParameter(2, OracleTypes.CURSOR);
cStmt.execute();
But when I run this code I get an exception as follows:-
java.sql.SQLException: Inconsistent java and sql object types
at oracle.sql.StructDescriptor.toOracleArray(StructDescriptor.java:709)
at oracle.sql.StructDescriptor.toArray(StructDescriptor.java:1296)
at oracle.sql.STRUCT.<init>(STRUCT.java:165)
at com.ensenda.lmp.web.controller.User.main(User.java:75)
This exception comes at the following line of code:-
STRUCT searchObj = new STRUCT(objDescriptor, conn.getMetaData().getConnection(), userProperties );
Please let me know where I'm going wrong.
Firstly I think you should get an ArrayOutOfBoundException because you have declared an Object[2] and trying to put a 3rd element into it. Considering that is a typo.
For the original problem instead of using setObject you can use
*JDBCUtil.setStruct(conn,cStmt,1,"USER_SEARCH_OBJ",userProperties);*

Building tree like structure for feeding Freemarker from JDBC

I want to generate code with Groovy and Freemarker (especially GFreeMarker) - I want to generate a Java wrapper for calling stored procedures. So I query ORACLE with this statement:
select PACKAGE_NAME, OBJECT_NAME, POSITION, ARGUMENT_NAME, DATA_TYPE, IN_OUT
from USER_ARGUMENTS
This data is denormalized (each parameter is on a line) and hierarchical:
- Package (PACKAGE_NAME)
- Function (OBJECT_NAME)
- Arguments (ARGUMENT_NAME, DATA_TYPE, IN_OUT)
I want to store it into an hierarchical structure to feed it to Freemarker to generate code. This means that for every row I have to create a new Package instance or select an existing on, then create a new Function instance or select an existing one and create a new argument.
Can this be done via an ObjectGraphBuilder or should I go into a combination with maps and lists?
How is this done in a "groovy way"? Thanks for your help!
Groovy or not, I did it the following way, please provide better answers and I will accept it!
First I build the following structure:
Map packages = new LinkedHashMap()
class Package {
String name
Map functions = [:]
}
class Function {
String name
String returnType
boolean isFunction = false
Map parameters = [:]
}
class Parameter {
String name
String dataType
String inOut
}
Then I read it with
sql.eachRow("""\
select PACKAGE_NAME, OBJECT_NAME, POSITION, ARGUMENT_NAME, DATA_TYPE, IN_OUT
from USER_ARGUMENTS
order by PACKAGE_NAME, OBJECT_NAME, POSITION""")
{
// create or get package
def thePackage = packages[it.PACKAGE_NAME ?: '']
if (thePackage == null) {
thePackage = new Package(name : it.PACKAGE_NAME ?: '')
packages[it.PACKAGE_NAME ?: ''] = thePackage
}
// create or get function
def theFunction = thePackage.functions[it.OBJECT_NAME ?: '']
if (theFunction == null) {
theFunction = new Function(name: it.OBJECT_NAME ?: '')
thePackage.functions[it.OBJECT_NAME ?: ''] = theFunction
}
// Position 0 is the return value
if (it.POSITION == 0) {
theFunction.isFunction = true
theFunction.returnType = it.DATA_TYPE
}
else {
// create the argument
def theParameter = new Parameter(
name: it.ARGUMENT_NAME, dataType: it.DATA_TYPE, inOut: it.IN_OUT)
theFunction.parameters[it.ARGUMENT_NAME ?: ''] = theParameter
}
}

using Spring jdbctemplate to invoke a oracle function returning cursor

Hi I am using spring jdbctemplate to invoke an oracle function which takes an integer input.
Oracle function is :
FUNCTION get_key_types (type_id IN integer)
RETURN tcur_key_types_det
IS
lv_cur tcur_key_types_det;
BEGIN
IF type_id IS NULL
THEN
RAISE KEY_ERROR;
ELSE
OPEN lv_cur FOR
SELECT key_criteria_cd,
key_type_name,
KEY_COLUMN_TXT,
data_type_cd
FROM key_criteria
WHERE criteria_type_id = type_id
ORDER BY key_type_name;
END IF;
RETURN lv_cur;
END get_key_types;
I have a Java class that extends from Class StoredProcedure which passes and integer argument to invoke the oracle function as follows.
public class KeyTypeService extends StoredProcedure{
public KeyTypeService(DataSource dataSource,String sqlString) {
setDataSource(dataSource);
setFunction(true);
setSql(sqlString);
declareParameter(new SqlParameter("type_id",Types.INTEGER));
declareParameter(new SqlOutParameter("functionName",OracleTypes.CURSOR,new KeyTypeMapper()));
compile();
}
public Map execute(int category_id) {
Map<String, Object> inputs = new HashMap<String, Object>();
inputs.put("type_id", category_id);
Map output = execute(inputs);
return output;
}
}
I invoke the oracle function as follows.
int i = 60;
KeyTypeService keyTypeService = new KeyTypeService((DataSource)c.getBean ("DataSource"),"get_key_types");
map = keyTypeService.execute(i);
I get the following error indicating that I am not passing the correct data type expected.
PLS-00306: wrong number or types of arguments in call to 'get_key_types'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
Any help would be highly appreciated..
Since my comment was correct:
The out parameter must come first.
Spring documentation might not explicitly talk about the order but the parameter order must be the same order as when you would call a pl/sql function without spring. I.e.:
out = call pacakge.function(in)

Resources