Unexpected IncorrectResultSizeDataAccessException when querying Oracle db - java

I have following table:
create table INTERNATIONALIZATION (
ID number not null unique,
LANG char(2) not null,
EXT_ID number not null,
EXT_COLUMN char(32) not null,
EXT_NAME char(32) not null,
TRANS_VAL nvarchar2(512) not null
);
And following code that aims to retrieve one and only one result from it (I am 100% sure that record exists).
public Optional<String> getTranslation(long idSkill, Locale lang, String extColumn, String extName) {
try {
return Optional.of(jdbcTemplate.queryForObject("select TRANS_VAL from INTERNATIONALIZATION where ext_id = ? and lang = ? and ext_column = ? and ext_name = ?", String.class, idSkill, lang.toLanguageTag(), extColumn, extName));
} catch (IncorrectResultSizeDataAccessException ex) {
return Optional.empty();
}
}
The problem is that when I always get the Optional.empty() as the IncorrectResultSizeDataAccessException is thrown, because no records are found.
When I execute this sql query in Oracle SQL Developer, I get the correct result.
What is causing this problem? Could it have something to do with nvarchar2 type of desired column?

You probably need to verify the arguments passed to the query. They may not be the same as those you used at Oracle SQL Developer.
For debugging, when you catch the exception, print the values used.
try {
return Optional.of(jdbcTemplate.queryForObject("select TRANS_VAL from INTERNATIONALIZATION where ext_id = ? and lang = ? and ext_column = ? and ext_name = ?", String.class, idSkill, lang.toLanguageTag(), extColumn, extName));
} catch (IncorrectResultSizeDataAccessException ex) {
return Optional.empty();
// print idSkill, lang.toLanguageTag(), extColumn, extName here
}
Also, as you are using CHAR columns, you have to take care with the white spaces padded when comparing. As you are using bind variables, there might be a issue when comparing chars against varchars.
Created a test table:
SQL> create table t1 (c1 char(5));
Table created.
SQL> insert into t1 values ('A');
1 row created.
SQL> commit;
Commit complete.
Comparing with literals, no problem:
SQL> select count(*) from t1 where c1 = 'A';
COUNT(*)
----------
1
SQL> select count(*) from t1 where c1 = 'A ';
COUNT(*)
----------
1
As the c1 is a char column, the data is padded to the left:
SQL> select '<'|| c1 || '>' from t1;
'<'||C1
-------
<A >
When comparing with a varchar2 bind variable
SQL> var s varchar2(5)
SQL> exec :s := 'A' ;
PL/SQL procedure successfully completed.
SQL> select count(*) from t1 where c1 = :s;
COUNT(*)
----------
0
Data has to be equal (5 characteres - one letter, 4 spaces):
SQL> exec :s :='A '
PL/SQL procedure successfully completed.
SQL> select count(*) from t1 where c1 = :s;
COUNT(*)
----------
1
That's just a tip, but I think you would be better off using varchar2 columns:
create table INTERNATIONALIZATION (
ID number not null unique,
LANG varchar2(2) not null,
EXT_ID number not null,
EXT_COLUMN varchar2(32) not null,
EXT_NAME varchar2(32) not null,
TRANS_VAL nvarchar2(512) not null
);

Related

Cannot perform fetch on a PLSQL statement

this is my table
CREATE TABLE accounts (
accountid VARCHAR(255),
name VARCHAR2(255),
phone_number INTEGER,
email_addresses VARCHAR2(255),
account_number VARCHAR(255),
password VARCHAR2(255)
);
and now i created one procedure
create or replace PROCEDURE getaccount (
param1 IN VARCHAR2
) AS
BEGIN
DBMS_OUTPUT.PUT_LINE(' SELECT
accountid,
password
FROM
accounts
WHERE
account_number = '||param1) ;
END getaccount;
and also why we used || or operator
second i run this code in SQLDEVELOPER
I GET THIS OUT
call getaccount('123456');
Call completed.
GET THIS MESSAGE
NOW I AM CONNECTING TO JAVA
HERE IS MY CODE IN JAVA
CallableStatement pst = Get.connection().prepareCall("{call getAccount(?)}");
pst.setString(1, Number);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
System.out.println(rs.getString(1));
here Get.connection return Connection .
after i run this i get this error
Enter Account Number : 123456
java.sql.SQLException: Cannot perform fetch on a PLSQL statement: next
at ojdbc10/oracle.jdbc.driver.InsensitiveScrollableResultSet.next(InsensitiveScrollableResultSet.java:409)
at bank.services.SignValidator.getBank(SignValidator.java:20)
at bank.services.KingobankMain.getBank(KingobankMain.java:49)
at bank.services.KingobankMain.main(KingobankMain.java:14)
Your procedure does not return values (output variable ) and you are trying to get data.
This results in an error.
Stored Procedure OUT Parameters:
reate or replace procedure getaccount (
p_account_number in varchar2,
p_account_id out varchar2,
p_password out varchar2
) as
begin
select
accountid,
password
into
p_account_id,
p_password
from
accounts
where
account_number = p_account_number ;
end getaccount;
/
CallableStatement pst = Get.connection().prepareCall("{call getAccount(?,?,?)}");
pst.setString(1, "....");//set account number
pst.registerOutParameter(2, OracleTypes.VARCHAR);
pst.registerOutParameter(3, OracleTypes.VARCHAR);
pst.execute();
//read the OUT parameter now
System.out.println("AccountID="+pst.getString(2)+",password="+pst.getString(3));
..........your of code....
Stored Procedure Oracle SYS_REFCURSOR:
create or replace procedure getaccount (
p_account_number in varchar2,
p_cur out sys_refcursor
) as
begin
open p_cur for
select
accountid,
password
from
accounts
where
account_number = p_account_number ;
end getaccount;
/
CallableStatement pst = Get.connection().prepareCall("{call getAccount(?,?)}");
pst.setString(1, ".....");// set account number
pst.registerOutParameter(2, OracleTypes.CURSOR);
pst.execute();
//read the OUT parameter now
rs = (ResultSet) pst.getObject(2);
while(rs.next()){
System.out.println("AccountID="+rs.getString("accountid")+",password="+rs.getString("password"));
..........your of code....
}
Addition links:
JDBC Basics
Calling PL/SQL from Java
I don't know Java, but: procedure you wrote results in output displayed on the screen, but only if tool you used to call the procedure supports it - e.g. SQL*Plus, SQL Developer, TOAD and similar. For the rest, there will be no result at all.
It looks as if you wanted to run
select accountid, password from accoutns where account_number = 123456
in Java. You should then use a function instead of a procedure. It should return a VARCHAR2 (that select statement) and then you'd execute it in Java. How? Can't tell; as I said, I don't know Java. In Oracle, you'd use dynamic SQL.
Or, if it has to be a procedure, then it should have an OUT parameter so that it returns that select statement to the caller. But, caller then has to have "something" (a variable?) to accept that output into.
As you requested, a procedure with an OUT parameter:
CREATE OR REPLACE PROCEDURE getaccount (param1 IN VARCHAR2,
par_query OUT VARCHAR2)
AS
BEGIN
par_query :=
'SELECT accountid, password FROM accounts WHERE '
|| ' account_number = '
|| DBMS_ASSERT.enquote_literal (param1);
END getaccount;
Note dbms_assert, which is used to prevent SQL injection.
In order to run it in Oracle, you'd have to use a PL/SQL block with a variable that accepts the result returned by that procedure.
SQL> set serveroutput on
SQL>
SQL> create or replace procedure getaccount (param1 in varchar2,
2 par_query out varchar2)
3 as
4 begin
5 par_query :=
6 'SELECT accountid, password FROM accounts WHERE '
7 || ' account_number = '
8 || dbms_assert.enquote_literal (param1);
9 end getaccount;
10 /
Procedure created.
SQL> declare
2 l_out varchar2(200);
3 begin
4 getaccount('12345', l_out);
5 dbms_output.put_line('Result is: ' || l_out);
6 end;
7 /
Result is: SELECT accountid, password FROM accounts WHERE account_number = '12345'
PL/SQL procedure successfully completed.
SQL>
Apparently, you don't want to return a command represented as a string, but result that is produced by that select statement. As commented, consider returning a refcursor. Moreover, use a function instead of a procedure; they are designed to "return" a value. If it is a procedure, you'll have to create OUT parameter(s) and accept their values into something (local variables, probably).
Suppose this is table's contents:
SQL> select * From accounts;
ACCOUNTID PASSWORD ACCOUNT_NUMBER
---------- ---------- --------------------
1 my_pwd 12345
SQL>
Function that returns refcursor:
SQL> create or replace function getaccount (param1 in varchar2)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 begin
6 open l_rc for
7 'SELECT accountid, password FROM accounts WHERE '
8 || ' account_number = '
9 || dbms_assert.enquote_literal (param1);
10 return l_rc;
11 end getaccount;
12 /
Function created.
SQL> select getaccount('12345') from dual;
GETACCOUNT('12345')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ACCOUNTID PASSWO
---------- ------
1 my_pwd
SQL>
Or, a procedure with 2 OUT parameters:
SQL> create or replace procedure getaccount
2 (param1 in varchar2,
3 p_accid out varchar2,
4 p_pwd out varchar2) is
5 begin
6 select accountid, password
7 into p_accid, p_pwd
8 from accounts
9 where account_number = param1;
10 end;
11 /
Procedure created.
SQL> set serveroutput on
SQL> declare
2 l_id varchar2(20);
3 l_pwd varchar2(20);
4 begin
5 getaccount('12345', l_id, l_pwd);
6 dbms_output.put_line('ID = ' || l_id ||', PWD = ' || l_pwd);
7 end;
8 /
ID = 1, PWD = my_pwd
PL/SQL procedure successfully completed.
SQL>

java.sql.SQLException: Subquery returns more than 1 row

I have a program that when executed it gets lots of words from a file and inserts them into a database, after being inserted if the word is inserted twice it calculates the "IDF" again using a trigger. The problem is that if I do this directly into MySQL it has no problem, but if I do this on Java it returns this error:
Exception in thread "main" java.sql.SQLException: Subquery returns more than 1 row
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1086)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2828)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2777)
at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:949)
at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:795)
at model.Consultas.altaBajaCambio(Consultas.java:29)
at model.DatosBD.agregarPalabra(DatosBD.java:23)
at search.Search.main(Search.java:36)
Java Result: 1
I assume the problem has to be with the st.execute(), since it only gives back one int, but I have search on the web for a solution and I cannot find one.
Query:
String query2 = "INSERT IGNORE INTO Search.IndiceInv (DocID, Term, TF) VALUES ("+doc+",'"+term+"',1) ON DUPLICATE KEY UPDATE `TF` = `TF` + 1;";
c.altaBajaCambio(query2);
Execution:
try (Connection con = c.getConnection()) {
if (con == null) {
System.out.println("No hay conexion");
} else {
Statement st = con.createStatement();
st.execute(query);
}
Database:
-- -----------------------------------------------------
-- Table `Search`.`Doc`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Search`.`Doc` (
`DocID` INT NOT NULL,
PRIMARY KEY (`DocID`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `Search`.`Term`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Search`.`Term` (
`Term` VARCHAR(45) NOT NULL,
`IDF` INT NOT NULL,
PRIMARY KEY (`Term`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `Search`.`IndiceInv`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Search`.`IndiceInv` (
`DocID` INT NOT NULL,
`Term` VARCHAR(45) NOT NULL,
`TF` INT NOT NULL,
PRIMARY KEY (`DocID`, `Term`),
ENGINE = InnoDB;
Trigger:
delimiter //
create trigger IDF
after update on IndiceInv
for each row
begin
update Term
set IDF = (SELECT LOG((SELECT count(*) FROM Doc)/(SELECT count(DocID) FROM IndiceInv WHERE Term = new.Term)) FROM Doc, IndiceInv)
where Term = new.Term;
end;//
delimiter ;
Try to run manually:
SELECT LOG((SELECT count(*) FROM Doc)/(SELECT count(DocID) FROM IndiceInv WHERE Term = new.Term)) FROM Doc, IndiceInv
(assign the relevant values to new.Term etc)

Calling a PostgreSQL function from Java

I have written a function that I would like to call in Java. But I don't think it is able to do anything with the query that I passed. Following is my code from java:
String QUERY_LOCATION = "select (license_plate) as test from carInst( (select category_name from reservation where rid = ?) , (select lname from reservation where rid = ?))";
//PreparedStatement check_location = null;
PreparedStatement check_location = connection.prepareStatement(QUERY_LOCATION);
check_location.setInt(1, rid);
check_location.setInt(2, rid);
rs = check_location.executeQuery();
if (rs.next()) {
System.out.print("Car found: "+rs.getString("test")+"\n");
license_plate = rs.getString("test");
update_reservation.setString(5, license_plate);
bool = false;
} else {
System.out
.print("There is no car available\n");
}
And following is my stored procedure written in PL/pgSQL (PostgreSQL):
CREATE OR REPLACE FUNCTION carInst(cname varchar(20), loc varchar(20) )
RETURNS TABLE (license_plate varchar(6) ) AS $$
BEGIN
DECLARE cur CURSOR
FOR SELECT carinstance.license_plate, carmodel.category_name, carinstance.lname FROM carinstance,carmodel
WHERE carinstance.mid = carmodel.mid ;
BEGIN
FOR rec IN cur LOOP
RETURN QUERY SELECT distinct carinstance.license_plate FROM Carinstance
WHERE rec.category_name = cname
AND rec.lname = loc
AND rec.license_plate=carinstance.license_plate;
END LOOP;
END;
END;
$$ LANGUAGE plpgsql;
When I run the code in Java, the print statement prints a null value for Car found. I would really appreciate some help here.
Problems
Most importantly, the query in the LOOP is nonsense. You select rows from carinstance, but all conditions are on rec. This select all rows multiple times.
One END too many. FOR has no END, only LOOP has.
Whenever you feel the temptation to work with an explicit cursor in plpgsql, stop right there. Chances are, you are doing it wrong. A FOR loop has an implicit cursor anyway.
Don't mess with mixed case identifiers without double quotes. I converted all identifiers to lower case.
You use one simple query, spread out over a cursor and another query. This can all be much simpler.
Solution
Try this simple SQL function instead:
CREATE OR REPLACE FUNCTION car_inst(_cname text, _loc text)
RETURNS TABLE (license_plate text)
LANGUAGE sql AS
$func$
SELECT DISTINCT ci.license_plate
FROM carmodel cm
JOIN carinstance ci USING (mid)
WHERE cm.category_name = $1
AND ci.lname = $2
$func$;
Call:
SELECT license_plate AS test FROM car_inst(
(SELECT category_name FROM reservation WHERE rid = ?)
, (SELECT lname FROM reservation WHERE rid = ?)
);
Or build it all into your function:
CREATE OR REPLACE FUNCTION car_inst(_cname text, _loc text)
RETURNS TABLE (license_plate text)
LANGUAGE sql AS
$func$
SELECT DISTINCT ci.license_plate
FROM carmodel cm
JOIN carinstance ci USING (mid)
JOIN reservation r1 ON r1.category_name = cm.category_name
JOIN reservation r2 ON r2.lname = ci.lname
WHERE r1.rid = $1
AND r2.rid = $2;
$func$;
Call:
"SELECT license_plate AS test FROM car_inst(? , ?)";
Remember: The OUT parameter license_plate is visible anywhere in the body of the function. Therefore you must table-qualify the column of the same name at all times to prevent a naming collision.
DISTINCT may or may not be redundant.

Delete Row from Oracle table by passing table name and column name

I have jsp page where user selects table name, column name and column value, with those three condtion I want to delete all matching row from the database. Is there a way to pass table name, column name and column value in oracle to delete certain row from the table? Any example would help me.. Thank you
I'd worry about SQL Injection attacks as you are supplying the table and column names.
You could create an Oracle function to remove the records required and test for certain conditions to be met before removing the row:
CREATE OR REPLACE
FUNCTION delete_record (
p_table IN VARCHAR2,
p_column IN VARCHAR2,
p_value IN VARCHAR2
)
RETURN NUMBER
AS
v_table user_tables.table_name%TYPE;
v_columns user_tab_cols.column_name%TYPE;
BEGIN
-- Check table exists in DB
SELECT table_name
INTO v_table
FROM user_tables
WHERE table_name = UPPER(p_table);
-- Check column exists in DB table
SELECT column_name
INTO v_colums
FROM user_tab_cols
WHERE table_name = UPPER(p_table)
AND column_name = UPPER(p_column);
EXECUTE IMMEDIATE
'DELETE FROM '||DBMS_ASSERT.SIMPLE_SQL_NAME(p_table)||
' WHERE '||DBMS_ASSERT.SIMPLE_SQL_NAME(p_column)||' = :col_value'
USING p_value;
RETURN SQL%ROWCOUNT;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
-- Either return -1 (error) or log an error etc.
RETURN -1;
WHEN others
THEN
<Your exception handling here>
END delete_record;
/
This (or something like this) would check the table and column variables supplied exist in the database before then deleting the records and returning the number of records deleted.
If there is a problem with the number deleted you can issue a rollback statement, if it is OK then you can issue a commit.
Of course, if you want to supply a fully qualified table name (recommended) then you would use the DBMS_ASSERT.QUALIFIED_SQL_NAME function instead of the DBMS_ASSERT.SIMPLE_SQL_NAME function.
Hope it helps...
EDIT: In response to Jack's question about adding date from and date to.
If you add two new conditions that are passed in to the function as:
CREATE OR REPLACE
FUNCTION delete_record (
p_table IN VARCHAR2,
p_column IN VARCHAR2,
p_value IN VARCHAR2,
p_date_from IN DATE,
p_date_to IN DATE
)
Then you'd need to expand the EXECUTE IMMEDIATE with:
EXECUTE IMMEDIATE
'DELETE FROM '||DBMS_ASSERT.SIMPLE_SQL_NAME(p_table)||
' WHERE '||DBMS_ASSERT.SIMPLE_SQL_NAME(p_column)||' = :col_value'||
' AND date BETWEEN :date_from AND :date_to'
USING p_value,
p_date_from,
p_date_to;
N.B. This assumes your date column in the table is called "date".
I don't have a SQL interface in front of me at the moment but this should be close enough to what you need to get it working.
If you are passing the p_date_XXXX parameters in as VARCHAR2 and not DATE types then you's need to "TO_DATE" the values before passing them into the dynamic SQL.
e.g.
EXECUTE IMMEDIATE
'DELETE FROM '||DBMS_ASSERT.SIMPLE_SQL_NAME(p_table)||
' WHERE '||DBMS_ASSERT.SIMPLE_SQL_NAME(p_column)||' = :col_value'||
' AND date BETWEEN :date_from AND :date_to'
USING p_value,
TO_DATE(p_date_from, <date_format>),
TO_DATE(p_date_to, <date_format>);
DELETE FROM table_name WHERE column_name = column_value
The problem is that you can't bind table or column names in PreparedStatement, only column values.
This should work (from memory; not tested):
Statement stmt = null;
try
{
stmt = conn.createStatement("DELETE FROM " + tableName + " WHERE " + columnName + " = '" + condition + "'");
int deleted = stmt.execute();
}
catch (SQLException e)
{
... report error
}
try
{
if (stmt != null)
stmt.close();
}
catch (SQLException ignore)
{
}

Stored Procedure with parameters/variables - how to get result from procedure execution?

Have a question about stored procedure:
How to retrieve the variable's value from stored procedure that is declared as an output variable.
Running procedure as :
EXEC pn_investment_type_eqt {args}
Here is stored procedure:
create proc pn_investment_type_eqt
(
#product_type varchar(10),
#country varchar(10),
#fi_treatment varchar(1)= '',
#investment_type varchar(10) output,
#investment_description varchar(50) output
)
as
set nocount on
if #country <> 'US'
select #country = 'FOREIGN'
if ( #fi_treatment not in ('Y','N') )
select #fi_treatment = 'N'
if not exists(select 1 from d_investment_type_eqt
where product_type = #product_type and isNull(country,'') = isNull(#country,'') and fi_treatment = #fi_treatment and row_status='A' )
begin
select #country = 'ANY'
end
if exists ( select 1 from d_investment_type_eqt
where product_type = #product_type and isNull(country,'') = isNull(#country,'') and fi_treatment = #fi_treatment and row_status='A' )
begin
select #investment_type= investment_type , #investment_description = description
from d_investment_type_eqt
where product_type = #product_type and isNull(country,'') = isNull(#country,'') and fi_treatment = #fi_treatment and row_status='A'
end
else
return (-1)
I need to get values of #investment_type & #investment_description.
I can't change procedure.
I am using a custom implementation of jdbcTemplate of Spring 2.0 (sql, mapper, args)
DB is Sybase
How could I get results from this stored procedure ?
Take a look at this sybase-in-and-out-parameters post, it possibly helps you further.
I decided to use next design :
declare #investment_type_value varchar(10)
declare #investment_description_value varchar(50)
SET #investment_type_value = '3041'
EXEC global..pn_investment_type_eqt 'CVPFDST', 'US', 'N', #investment_type = #investment_type_value output , #investment_description = #investment_description_value OUTPUT
select investment_type = #investment_type_value, investment_description = #investment_description_value
GO
It give me possibility to retrieve variable values.

Categories