I have a scenario where in i have to pass null to a SQL parameter through java but i am getting SQLGrammarException. If i am passing some value it works fine. Please guide if i am wrong somewhere.
Below is my code:
StringBuffer query;
query = new StringBuffer(
"SELECT * "+
"FROM table(package.func(travel_type => travel_search_type("+
"travel_place_no => :travelPlaceNo"+
")))" );
Query query1 = em.createNativeQuery(query.toString());
query1.setParameter("travelPlaceNo",null);
searchresults = query1.getResultList();
Exception:
org.hibernate.exception.SQLGrammarException
This is what i do through SQL Developer and it works fine:
SELECT *
FROM table(package.func(travel_type => travel_search_type(
travel_place_no => NULL))) ;
Please guide.
While calling the 2 argument signatures of the method, null is not an allowed value. You can use, instead, the 3 argument signature setParameter(String name,
Object val,
Type type) specifying the data type and it should work.
EDIT:
Ok, I see there is also another problem: even if the replacemente worked, what you are trying to execute is a >= NULL. In this case, I'm afraid that you are going to have to handle mannually the StringBuffer creation. Maybe just force an always false condition without parameters if its null, like `1!=2', and otherwise just handle it as you are doing on your sql example (write mannually 'NULL' instead of the parameter placeholder).
Related
Assume the following function declaration:
FUNCTION ARTTEXTJN
(p_art_id in number
,p_arttextart in varchar2 default 'basis'
,p_sprache in varchar2 default null
,p_aufart in number default null
,p_fallback_arttextart in varchar2 default 'J'
)
RETURN VARCHAR2
Expect the first parameter all parameter have a default value.
jOOQ generate a package method like this:
public static Field<String> arttextjn(Field<? extends Number> pArtId, Field<String> pArttextart,
Field<String> pSprache, Field<? extends Number> pAufart, Field<String> pFallbackArttextart) {
Arttextjn f = new Arttextjn();
f.setPArtId(pArtId);
f.setPArttextart(pArttextart);
f.setPSprache(pSprache);
f.setPAufart(pAufart);
f.setPFallbackArttextart(pFallbackArttextart);
return f.asField();
}
If I want to use it in a query I have to pass null to the function:
dsl.select(KAMPARTIKEL.ARTNR, KAMPARTIKEL.ARTNRKAMP,
PaBez.arttextjn(KAMPARTIKEL.ART_ID, null, null, null, null))
But then Oracle does not use the default values.
Is there a way to tell jOOQ to generate overloaded methods with all possible combinations? Otherwise I'm not able to use that function in a select clause.
Is there a way to tell jOOQ to generate overloaded methods with all possible combinations?
No, there would be way too many combinations. Of course, you could extend the code generator yourself, but I would advise against it.
Otherwise I'm not able to use that function in a select clause.
Yes, you can use it! But not using that auxiliary method PaBez.arttextjn. It's possible to call it as a standalone function call:
Arttextjn f = new Arttextjn();
f.setPArtId(1);
f.execute();
String result = f.getReturnValue();
It should be possible to use in a SQL statement as well:
Arttextjn f = new Arttextjn();
f.setPArtId(KAMPARTIKEL.ART_ID);
var result =
dsl.select(KAMPARTIKEL.ARTNR, KAMPARTIKEL.ARTNRKAMP, f.asField())
.from(KAMPARTIKEL)
.fetch();
In your case, this should work out of the box.
Note that as of jOOQ 3.11, and in Oracle, jOOQ passes function arguments by index in this case, not by name (as it does for PostgreSQL). The generated SQL is:
select KAMPARTIKEL.ARTNR, KAMPARTIKEL.ARTNRKAMP, pa_bez.arttextjn(KAMPARTIKEL.ART_ID)
from KAMPARTIKEL
Which works because you're using only the first parameter, applying defaults to the rest. It wouldn't work if you would pass the last parameter, in case of which the generated SQL would have to use named parameters:
select
KAMPARTIKEL.ARTNR,
KAMPARTIKEL.ARTNRKAMP,
pa_bez.arttextjn(p_art_id => KAMPARTIKEL.ART_ID)
from KAMPARTIKEL
I've created an issue to fix this for jOOQ 3.12:
https://github.com/jOOQ/jOOQ/issues/8560
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 have this method to call a stored function from ORACLE, in java (spring) - using entity manager + createNativeQuery ..
(...)
String set_professional = "{? = call
pk_backoffice.set_professional(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?}";
//32 parameters IN
query = entity.createNativeQuery(set_professional);
(...)
And everytime I try to test it, it shows:
Positional parameter does not exist: 31 in query: {? = call (...)
But do I have something at position 31..it exists..
query.setParameter(31, prof.getFax()); // fax
Also, I started the parameters at 1 cause in previous exceptions it said it was 1-based
I've tried with a string and a null value instead of the get, still the same outcome..
About the query, I also counted the ? many times, so I'm sure it has 32 (for parameters) + 1(return - first ?)...
Can anyone help?
Found a solution, I replaced all ? for variables, even the first one, and the error disappeared.
Given following Statment:
String query = "Select * from T_spareParts where SparePartPK IN (? )"
In my BackBean (JSF 2) I first iterate through all cars in table and build a String of all the cars currently selected (by picking each cars ID as primary key) so the final String before being passed to SQL could look like:
String finalString = " '1','2','3','4'";
And then :
this.prepareStatement= this.connection.prepareStatement(query);
this.prepareStatement.setString(1,finalString);
this.prepareStatement.executeQuery();
Exception thrown is:
Error converting data type nvarchar to bigint.
Now my understanding is that exception is due to SparePartPK is type bigint and we're passing a String .
But in SQL Server (2008) i can do :
Select * from T_spareParts where SparePartPK IN ('1','2','3','4')"
which returns results as expected. Why am i getting the exception and how can i correct the issue? (also feel free to comment if this isn't the best approach)
Update:
I've also tried to produce the finalString without single quotes which causes the same exception to be thrown :
String finalString = " 1,2,3,4";
You should put the numbers into an array (not a string), and use preparedStatement.setArray()
Thanks for the suggestions. Though as an alternative I created a StoredProcedure where it takes one parameter and inside the StoredProcedure i run the above Query in question formulating the IN section using the passed parameter .
So all above code still applies, The parameter passed to the storeDProcedure is one String separated by , without the single quotes.
Might not be the optimum answer but works quit well :) .