I am getting ORA-00900 error while trying the call the Oracle function from code, whereas it works from sqldeveloper.
Code from which I am trying to call the Oracle function is below.
passwordCriteria = session.doReturningWork(connection -> {
try (CallableStatement function = connection
.prepareCall("{ ? = call test$ui_mob.password_change (?,?,?,?) }")) {
function.setEscapeProcessing(false);
function.registerOutParameter("p_response", Types.VARCHAR);
function.setString("p_username", "TEST_USER");
function.setString("p_companycode", "BSH");
function.setString("p_deviceid", "123456798");
function.setString("p_language", "en");
function.executeQuery();
return function.getString(1);
}
});
}
and stack trace is
Caused by: java.sql.SQLSyntaxErrorException: ORA-00900: invalid SQL
statement
According to the JavaDoc:
The interface used to execute SQL stored procedures. The JDBC API provides a stored procedure SQL escape syntax that allows stored procedures to be called in a standard way for all RDBMSs. This escape syntax has one form that includes a result parameter and one that does not. If used, the result parameter must be registered as an OUT parameter. The other parameters can be used for input, output or both. Parameters are referred to sequentially, by number, with the first parameter being 1.
{?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
{call <procedure-name>[(<arg1>,<arg2>, ...)]}
It looks like there should be no spaces between {?=, while you have spaces.
Related
I'm trying to define a java (written in Kotlin) method that returns multiple result sets when called as a stored procedure. Code is below. the Hsqldb website;s features page indicates that this should be possible, what am I missing? I currently get an array out of bounds error on index 1:
val createProcedure = """
CREATE PROCEDURE GET_CACHED(dir VARCHAR(100), hashCode INT)
MODIFIES SQL DATA
LANGUAGE JAVA
DYNAMIC RESULT SETS 9
EXTERNAL NAME 'CLASSPATH:integration.FileCache.getResultSets'
"""
#JvmStatic
#Throws(SQLException::class)
public fun getResultSets(conn: Connection, dir: String, hashCode: Int, result: Array<ResultSet?>) {
val file = getFile(dir, hashCode, "sql")
//A list of cached sql statements
val sqlList = BufferedReader(InputStreamReader(file.inputStream())).readLines()
val stmt = conn.createStatement()
for(i in sqlList.indices) {
result[i] = stmt.executeQuery(sqlList[i])
}
}
Given that I can set a breakpoint and reach the inside of the function, I don't think I need to add any more of my code, but if that is wrong let me know.
I'm in a quest to find an in-memory database that can handle multiple result sets for testing purposes (We're modernizing an application, setting up tests first, containerized testing is currently out of reach). I've tried H2, sqlite, and now hsqldb, if there's a better solution I'm open to it.
HSQLDB supports multiple result test, but the Guide states :HyperSQL support this method of returning single or multiple result sets from SQL/PSM procedures only via the JDBC CallableStatement interface. Note the reference to SQL/PSM, rather than SQL/JRT. Currently there is no Java mechanism to return multiple result sets from a Java language PROCEDURE.
For test purposes, you could use a text template consisting of an SQL/PSM CREATE PROCEDURE statement written in SQL, with placeholders for the actual SQL statements that you want to execute. Process the template with your test SQL statements from your file and execute the resulting CREATE PROCEDURE statement.
I have this procedure in my postgreSQL database:
CREATE OR REPLACE FUNCTION getItemsForCategory(categoryId integer)
RETURNS SETOF ITEM AS $_$
DECLARE
result ITEM;
BEGIN
FOR result IN SELECT *
FROM item it
JOIN item_category itcat ON it.id = itcat.item_id WHERE itcat.category_id = categoryId LOOP
RETURN NEXT result;
END LOOP;
END; $_$ LANGUAGE 'plpgsql';
which works excellent using terminal, but I have trouble with calling it using JPA. Here is my code snippet(4 is value of argument cateforyId):
transactions.begin();
final StoredProcedureQuery storedProcedureQuery = entityManager.createStoredProcedureQuery("getItemsForCategory");
storedProcedureQuery.setParameter(1,4).execute();
final List<ItemEntity> itemEntityList = (List<ItemEntity>) storedProcedureQuery.getResultList();
transactions.commit();
after running code above I receive this error:
Exception in thread "main" java.lang.IllegalArgumentException:
You have attempted to set a parameter at position 1 which does not exist in this query string getItemsForCategory
Has anyone some idea how to set the value of argument correctly? I've also tried to set parameter using 0 instead of 1, calling setParameter with others datatypes of arguments (String,Object) but everytime I am receiving familiar kind of error like the one, which is shown there. Thank you very much
You need to register your parameters, before setting values.
spq.registerStoredProcedureParameter("categoryId", int.class, ParameterMode.IN);
See this link.
I am trying to call a PL/SQL procedure which has defaults defined for some of its parameters. I am doing this using CallableStatement in JDBC.
This procedure has a large number of parameters with defaults defined. I do not want to explicitly set the defaults in the Java code. Doing this would make maintaining the code harder. If the PL/SQL code changes , would have to make the same changes in the Java code too.
Is it possible to accomplish this in JDBC ? For instance just bind values to the parameter you are interested in and ignore the rest.
I tried this on the following sample procedure :
-- PURPOSE: Takes a parameter which has defaults set. Returns the value of the same parameter
-- Example of: FUNCTION that takes a parameter with DEFAULT values
FUNCTION handle_defaults(empId IN NUMBER DEFAULT 20 , empCity IN VARCHAR2) RETURN NUMBER IS
BEGIN
RETURN empId;
EXCEPTION
WHEN others THEN
dbms_output.put_line('Error!');
END handle_defaults;
Here is the relevant portions of the code (NOTE: Have stripped off the try catch block , cleaning up of database resources etc for sake of readability)
// Create a database connection
conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PWD);
// Create a query string
String queryStr = "{ ? = call HR.EMP_PKG.handle_defaults( ? , ? ) }";
// Create a Callable Statements
callStmt = conn.prepareCall(queryStr);
// Bind values to the IN parameter
callStmt.setString(3, "Mumbai");
// Register OUT parameter
callStmt.registerOutParameter(1, java.sql.Types.NUMERIC);
// Execute the Callable Statement
callStmt.execute();
// Retrieve the value of the OUT parameter
parameterValue = callStmt.getInt(1);
System.out.println("Value returned : " + parameterValue);
I get the following error:
Exception occured in the database
java.sql.SQLException: Missing IN or OUT parameter at index:: 2
Database error code: 17041
As a desperate attempt I also tried to pass Nulls for those parameters. Just included the following line:
callStmt.setNull(2, java.sql.Types.NUMERIC);
I get the following result:
Value returned : 0
That makes sense bcoz setNull supplies SQL Null to the parameter.
I am using Oracle 11g and Oracle 12c Jdbc Driver Version 12.1.0.2.
I don't think there's a simple answer to this question, and that's not because of JDBC, but because of Oracle.
Put simply, the only way I know of calling a procedure and using the default value for a parameter is to not specify the parameter when you call the procedure.
If you are writing
String queryStr = "{ ? = call HR.EMP_PKG.handle_defaults( ? , ? ) }";
you are always specifying both parameters, so you can never use the default value for one of them. If you only want to specify one of them and use the default for the other, write:
String queryStr = "{ ? = call HR.EMP_PKG.handle_defaults( empCity => ? ) }";
In this case you need to specify the parameter name in the call, as the first parameter is the optional one. If the second parameter was optional instead, the parameter name can be dropped.
Unfortunately, this becomes quite complicated for your real procedure with lots of parameters. What I would do would be to:
Use a StringBuilder to build up the procedure call string.
Run through the parameters, adding paramName => ? parts to it for each parameter you have a value for. Ensure the parts are separated by commas.
Prepare a CallableStatement using the output of the StringBuilder.
Run through the parameters again and call various setString/setInt/setDate/etc. methods on the CallableStatement for each parameter you have a value for.
I am trying to use UCanAccess to query a MS Access .accdb file. Everything works great, except when I query multi-value fields. For example those that have entries in the Row Source of a table field's Lookup tab in design view in MS Access. My code crashes when I try to output the result:
ResultSet rslt = stmt.executeQuery("SELECT [singleValue], [multiValue] FROM [TableName];");
int count = 0;
while (rslt.next())
System.out.println(count++ + "\t" + rslt.getString(1) + "\t" + rslt.getString(2));
The ResultSet is returned fine, and the singleValue prints fine, but the following error is thrown if I try to print the multiValue from the ResultSet:
Exception in thread "main" net.ucanaccess.jdbc.UcanaccessSQLException: incompatible data type in conversion: from SQL type OTHER to java.lang.String, value: instance of org.hsqldb.types.JavaObjectData
I have tried querying a query that is stored in the .accdb, but that does not work, because it just triggers the original query, and returns the same ResultSet.
Am I missing something simple or is this something UCanAccess can not handle?
This is the first question about it I have ever seen.
You can see an example of the complex types usage with UCanAccess in the ucanaccess web site, tab "Getting Started" (at the end of the page).
Here's a junit test case:
https://sourceforge.net/p/ucanaccess/code/HEAD/tree/ucanaccess/trunk/src/test/java/net/ucanaccess/test/ComplexTest.java
(see the testComplex method).
In particular you can't call rslt.getString(2) but have to use rslt.getObject(2) .
You'll get a ucanaccess wrapper of your data.
If you wanted to get string that described the data content you can use
rslt.getObject(2).toString().
The wrapping classes are:
net.ucanaccess.complex.Attachment,
net.ucanaccess.complex.SingleValue,
net.ucanaccess.complex.Version.
In your example, rslt.getObject(2) should return an array of net.ucanaccess.complex.SingleValue.
Then you can call the method singleValue.getValue() on each array element to get the wrapped value.
I'm using EclipseLink for a web project involving EJB 3 and Oracle stored procedures. We're still in the beginning, so I had a simple procedure set up for testing, here's the signature:
p_test.testProcedure(as_param in varchar2)
Here's the code I use to call the procedure, similar to the Using Basic Query API article on Eclipsepedia:
JpaEntityManager jem = (JpaEntityManager) em.getDelegate();
StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("p_test.testProcedure");
call.addNamedArgument("param", "param", String.class);
DataReadQuery query = new DataReadQuery();
query.setCall(call);
query.addArgument("param", String.class);
Vector<String> values = new Vector<String>();
values.add("test");
jem.getActiveSession().executeQuery(query, values);
I keep getting this error:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.1.1.v20100817-r8050):
org.eclipse.persistence.exceptions.DatabaseException Internal Exception:
java.sql.SQLException: ORA-06550: line 1, column 7: PLS-00306: wrong number or types of
arguments in call to 'TESTPROCEDURE' ORA-06550: line 1, column 7: PL/SQL: Statement
ignored Error Code: 6550 Call: BEGIN p_test.testProcedure(param=>?); END; bind => [test] Query: DataReadQuery()
I also have tried setting call argument type to java.sql.Types.VARCHAR and none at all, to no effect.
Someone have any ideas what am I doing wrong? Thanks everyone.
It looks like you are specifying a different procedureParameterName in the addNamedArgument method than what is specified in your stored procedure, i.e., "param" vs "as_param". Try changing your logic to the following:
call.addNamedArgument("as_param", "param", String.class);
This method maps the parameter name between what is used in your call to query.addParameter and what is defined in the stored procedure.
From TopLink docs
addNamedArgument
public void addNamedArgument(java.lang.String procedureParameterName,
java.lang.String argumentFieldName)
PUBLIC: Define the argument to the
stored procedure and the
field/argument name to be substitute
for it. The procedureParameterName is
the name of the procedure argument
expected. The argumentFieldName is the
field or argument name to be used to
pass to the procedure. If these names
are the same (as they normally are)
this method can be called with a
single argument.
public void addNamedArgumentValue(java.lang.String procedureParameterName,
java.lang.Object argumentValue)
call.addNamedArgumentValue("param",(Object)values );
Solved using a combination of the above two answers and my own wits.
First, I used addNamedArgumentValue asu suggested by #1.
Second, I used the correct procedure parameter name as suggested by #2.
Last, I used directly jem.executeNonSelectingCall(call) and it worked. (Also with OUT parameters).
So they both get a vote up!