I have a problem with the JDBC executeUpdate() method. It always returns 1 whether it updates a row or not. As far as I understand the method it should return 0 is no rows are altered.
Here is a sample of my code:
try {
conn = pool.getConnection();
PreparedStatement ps = conn.prepareStatement("{CALL UPDATE_USER (?,?,?)}");
ps.setString(1, field.toString());
ps.setString(2, change);
ps.setString(3, userID);
int updated = ps.executeUpdate();
System.out.println(updated);
if(updated==0){
throw new NoUserException();
}
ps.close();
} catch (SQLException e) {
log.error("An error occurred while creating the connection");
e.printStackTrace();
} finally {
pool.returnConnection(conn);
}
Could this be because I'm using a prepared statement or a stored procedure?
Here is the stored procedure:
create or replace
PROCEDURE UPDATE_USER
(
updateColumn IN user_tab_columns.column_name%type,
changeStr IN VARCHAR2,
unID IN VARCHAR2
)
IS
BEGIN
EXECUTE IMMEDIATE
'UPDATE
users
SET ' || updateColumn || '= :1
WHERE
uniqueID = :2'
USING changeStr, unID;
END;
It can't get info from stored proc execution. If you want to get row count (or anything else) switch PROCEDURE to FUNCTION, add return clause in this function, and change your call to something like ? = CALL ... Test with sql%rowcount inside function to get impacted row count.
You can use Callable statement a procedure or function. And return the no of rows affected as a return parameter
Related
ExecuteUpdate() is always returning 1. Pls suggest and appreciate any input.
Procedure:
PROCEDURE INSERT_USER_PREFERENCES(owner_id_var varchar2, stripeid_var varchar2, type_var varchar2, metadata_var CLOB) AS
BEGIN
MERGE INTO CXO_USER_PREFERENCES d
USING(SELECT stripeid_var id FROM dual) s
ON (d.stripe_id = s.id)
WHEN NOT MATCHED THEN
INSERT (OWNER_ID, STRIPE_ID, PREF_TYPE, METADATA, CREATED_DATE )
VALUES(owner_id_var, stripeid_var, type_var, metadata_var, CURRENT_TIMESTAMP);
COMMIT;
END INSERT_USER_PREFERENCES;
Java code:
try (CallableStatement stmt = connection.prepareCall(CREATE_USER_PREF_SQL)) {
SQLParameterMapper sqlParamMapper = new SQLParameterMapper(CREATE_USER_PREF_SQL);
sqlParamMapper.setString(stmt, ":ownerId", userName);
sqlParamMapper.setCharacterStream(stmt, ":metadata", reader, metadata.length());
sqlParamMapper.setString(stmt, ":stripeId", stripeId);
sqlParamMapper.setString(stmt, ":type", userPreference.getType());
// invoke the database
int value = stmt.executeUpdate();
return value;
} catch (SQLException e) {
throw e;
}
stmt.executeUpdate() - always returns 1, even though insertion happens only once. Appreciate any inputs on this. Ideally, if there are no insertions or errors, it should return 0 or any exception trace. Pls, suggest.**
We can make the column a primary key. Now when multiple VM's tries to execute, the other one will throw a Unique Integrity exception and in the catch block, we can read the SQLException. Once we read the SQLException, we can also read the sqlstate and we can perform the needed operations based on the sqlstate.
Sample code below
if (e instanceof SQLException) {
String sqlState = ((SQLException) e).getSQLState();
if (sqlState.equalsIgnoreCase("08000") || sqlState.equalsIgnoreCase("08006")) {
logger.error("POD DB is down with the sql state:" + sqlState);
return true;
}
}
I occasionally encounter two forms of strange behaviour when using JDBC to work with SQL Server stored procedures:
Issue 1: I run a stored procedure in SQL Server Management Studio (SSMS) and it returns a result set. However, when I try
try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) {
ResultSet rs = cs.executeQuery();
I get the exception
com.microsoft.sqlserver.jdbc.SQLServerException: The statement did not return a result set.
Issue 2: I run a stored procedure in SSMS and it raises an error, but when I use JDBC to .execute the stored procedure no exception is thrown.
Why do these issues occur and how can I avoid them?
When we execute a stored procedure in JDBC we get back a series of zero or more "results". We can then process those "results" sequentially by calling CallableStatement#getMoreResults(). Each "result" can contain
zero or more rows of data that we can retrieve with a ResultSet object,
an update count for a DML statement (INSERT, UPDATE, DELETE) that we can retrieve with CallableStatement#getUpdateCount(), or
an error that throws an SQLServerException.
For "Issue 1" the problem is often that the stored procedure does not begin with SET NOCOUNT ON; and executes a DML statement before doing a SELECT to produce a result set. The update count for the DML is returned as the first "result", and the data rows are "stuck behind it" until we call getMoreResults.
"Issue 2" is essentially same problem. The stored procedure produces a "result" (usually a SELECT, or possibly an update count) before the error occurs. The error is returned in a subsequent "result" and does not cause an exception until we "retrieve" it using getMoreResults.
In many cases the problem can be avoided by simply adding SET NOCOUNT ON; as the first executable statement in the stored procedure. However, a change to the stored procedure is not always possible and the fact remains that in order to get everything back from the stored procedure we need to keep calling getMoreResults until, as the Javadoc says:
There are no more results when the following is true:
// stmt is a Statement object
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
That sounds simple enough but as usual, "the devil is in the details", as illustrated by the following example. For a SQL Server stored procedure ...
ALTER PROCEDURE dbo.TroublesomeSP AS
BEGIN
-- note: no `SET NOCOUNT ON;`
DECLARE #tbl TABLE (id VARCHAR(3) PRIMARY KEY);
DROP TABLE NonExistent;
INSERT INTO #tbl (id) VALUES ('001');
SELECT id FROM #tbl;
INSERT INTO #tbl (id) VALUES ('001'); -- duplicate key error
SELECT 1/0; -- error _inside_ ResultSet
INSERT INTO #tbl (id) VALUES ('101');
INSERT INTO #tbl (id) VALUES ('201'),('202');
SELECT id FROM #tbl;
END
... the following Java code will return everything ...
try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) {
boolean resultSetAvailable = false;
int numberOfResultsProcessed = 0;
try {
resultSetAvailable = cs.execute();
} catch (SQLServerException sse) {
System.out.printf("Exception thrown on execute: %s%n%n", sse.getMessage());
numberOfResultsProcessed++;
}
int updateCount = -2; // initialize to impossible(?) value
while (true) {
boolean exceptionOccurred = true;
do {
try {
if (numberOfResultsProcessed > 0) {
resultSetAvailable = cs.getMoreResults();
}
exceptionOccurred = false;
updateCount = cs.getUpdateCount();
} catch (SQLServerException sse) {
System.out.printf("Current result is an exception: %s%n%n", sse.getMessage());
}
numberOfResultsProcessed++;
} while (exceptionOccurred);
if ((!resultSetAvailable) && (updateCount == -1)) {
break; // we're done
}
if (resultSetAvailable) {
System.out.println("Current result is a ResultSet:");
try (ResultSet rs = cs.getResultSet()) {
try {
while (rs.next()) {
System.out.println(rs.getString(1));
}
} catch (SQLServerException sse) {
System.out.printf("Exception while processing ResultSet: %s%n", sse.getMessage());
}
}
} else {
System.out.printf("Current result is an update count: %d %s affected%n",
updateCount,
updateCount == 1 ? "row was" : "rows were");
}
System.out.println();
}
System.out.println("[end of results]");
}
... producing the following console output:
Exception thrown on execute: Cannot drop the table 'NonExistent', because it does not exist or you do not have permission.
Current result is an update count: 1 row was affected
Current result is a ResultSet:
001
Current result is an exception: Violation of PRIMARY KEY constraint 'PK__#314D4EA__3213E83F3335971A'. Cannot insert duplicate key in object 'dbo.#tbl'. The duplicate key value is (001).
Current result is a ResultSet:
Exception while processing ResultSet: Divide by zero error encountered.
Current result is an update count: 1 row was affected
Current result is an update count: 2 rows were affected
Current result is a ResultSet:
001
101
201
202
[end of results]
I occasionally encounter two forms of strange behaviour when using JDBC to work with SQL Server stored procedures:
Issue 1: I run a stored procedure in SQL Server Management Studio (SSMS) and it returns a result set. However, when I try
try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) {
ResultSet rs = cs.executeQuery();
I get the exception
com.microsoft.sqlserver.jdbc.SQLServerException: The statement did not return a result set.
Issue 2: I run a stored procedure in SSMS and it raises an error, but when I use JDBC to .execute the stored procedure no exception is thrown.
Why do these issues occur and how can I avoid them?
When we execute a stored procedure in JDBC we get back a series of zero or more "results". We can then process those "results" sequentially by calling CallableStatement#getMoreResults(). Each "result" can contain
zero or more rows of data that we can retrieve with a ResultSet object,
an update count for a DML statement (INSERT, UPDATE, DELETE) that we can retrieve with CallableStatement#getUpdateCount(), or
an error that throws an SQLServerException.
For "Issue 1" the problem is often that the stored procedure does not begin with SET NOCOUNT ON; and executes a DML statement before doing a SELECT to produce a result set. The update count for the DML is returned as the first "result", and the data rows are "stuck behind it" until we call getMoreResults.
"Issue 2" is essentially same problem. The stored procedure produces a "result" (usually a SELECT, or possibly an update count) before the error occurs. The error is returned in a subsequent "result" and does not cause an exception until we "retrieve" it using getMoreResults.
In many cases the problem can be avoided by simply adding SET NOCOUNT ON; as the first executable statement in the stored procedure. However, a change to the stored procedure is not always possible and the fact remains that in order to get everything back from the stored procedure we need to keep calling getMoreResults until, as the Javadoc says:
There are no more results when the following is true:
// stmt is a Statement object
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
That sounds simple enough but as usual, "the devil is in the details", as illustrated by the following example. For a SQL Server stored procedure ...
ALTER PROCEDURE dbo.TroublesomeSP AS
BEGIN
-- note: no `SET NOCOUNT ON;`
DECLARE #tbl TABLE (id VARCHAR(3) PRIMARY KEY);
DROP TABLE NonExistent;
INSERT INTO #tbl (id) VALUES ('001');
SELECT id FROM #tbl;
INSERT INTO #tbl (id) VALUES ('001'); -- duplicate key error
SELECT 1/0; -- error _inside_ ResultSet
INSERT INTO #tbl (id) VALUES ('101');
INSERT INTO #tbl (id) VALUES ('201'),('202');
SELECT id FROM #tbl;
END
... the following Java code will return everything ...
try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) {
boolean resultSetAvailable = false;
int numberOfResultsProcessed = 0;
try {
resultSetAvailable = cs.execute();
} catch (SQLServerException sse) {
System.out.printf("Exception thrown on execute: %s%n%n", sse.getMessage());
numberOfResultsProcessed++;
}
int updateCount = -2; // initialize to impossible(?) value
while (true) {
boolean exceptionOccurred = true;
do {
try {
if (numberOfResultsProcessed > 0) {
resultSetAvailable = cs.getMoreResults();
}
exceptionOccurred = false;
updateCount = cs.getUpdateCount();
} catch (SQLServerException sse) {
System.out.printf("Current result is an exception: %s%n%n", sse.getMessage());
}
numberOfResultsProcessed++;
} while (exceptionOccurred);
if ((!resultSetAvailable) && (updateCount == -1)) {
break; // we're done
}
if (resultSetAvailable) {
System.out.println("Current result is a ResultSet:");
try (ResultSet rs = cs.getResultSet()) {
try {
while (rs.next()) {
System.out.println(rs.getString(1));
}
} catch (SQLServerException sse) {
System.out.printf("Exception while processing ResultSet: %s%n", sse.getMessage());
}
}
} else {
System.out.printf("Current result is an update count: %d %s affected%n",
updateCount,
updateCount == 1 ? "row was" : "rows were");
}
System.out.println();
}
System.out.println("[end of results]");
}
... producing the following console output:
Exception thrown on execute: Cannot drop the table 'NonExistent', because it does not exist or you do not have permission.
Current result is an update count: 1 row was affected
Current result is a ResultSet:
001
Current result is an exception: Violation of PRIMARY KEY constraint 'PK__#314D4EA__3213E83F3335971A'. Cannot insert duplicate key in object 'dbo.#tbl'. The duplicate key value is (001).
Current result is a ResultSet:
Exception while processing ResultSet: Divide by zero error encountered.
Current result is an update count: 1 row was affected
Current result is an update count: 2 rows were affected
Current result is a ResultSet:
001
101
201
202
[end of results]
I have a below query, which needs to select a row by using a column as key and return generated keys.
INSERT INTO t_tpms_cc_request
(process_identifier,
request_source_id,
amount,
etc_account_id,
retry_count,
status,
store_identifier,
version_no,
next_process_time,
composite_transaction_id,
payment_id,
processed_time,
replenishment_id,
pay_type,
agency_id,
response_code,
file_id,
request_date,
auth_file_id,
auth_date_time,
merc_file_id,
merc_date_time,
cc_num,
cc_expiration_date,
merchant_id,
ext_sys_ref,
encrypt_cc_number,
cc_month_cd,
cc_year_cd,
orig_txn_ref,
auth_code,
avs_code,
cvv_code)
SELECT CC.process_identifier,
CC.request_source_id,
CC.amount,
CC.etc_account_id,
CC.retry_count,
CC.status,
CC.store_identifier,
CC.version_no,
CC.next_process_time,
CC.composite_transaction_id,
CC.payment_id,
CC.processed_time,
CC.replenishment_id,
CC.pay_type,
CC.agency_id,
CC.response_code,
CC.file_id,
CC.request_date,
CC.auth_file_id,
CC.auth_date_time,
CC.merc_file_id,
CC.merc_date_time,
CC.cc_num,
CC.cc_expiration_date,
CC.merchant_id,
CC.ext_sys_ref,
CC.encrypt_cc_number,
CC.cc_month_cd,
CC.cc_year_cd,
CC.orig_txn_ref,
CC.auth_code,
CC.avs_code,
CC.cvv_code
FROM t_tpms_cc_request CC
WHERE CC.order_id = ?
And, I have wrriten a below java code to do this:
String key[] = {"order_id"};
DataSource ds = null;
Connection con = null;
ResultSet rs = null;
try {
ds = jdbcTemplate.getDataSource();
con = ds.getConnection();
PreparedStatement ps =
con.prepareStatement(insertCCRequest.trim(), key);
ps.setString(1, OrderId);
int i= ps.executeUpdate();
rs = ps.getGeneratedKeys();
if (rs.next()) {
return rs.getString(1);
}
} catch (SQLException e) {
logger.debug("SQL exception in RebillDao.insertCCrequest()
method..!! ");
logger.debug("Exception cause: "+e.getMessage());
e.printStackTrace();
throw e;
}
finally {
if(con!=null){
con.close();
}
}
return "";
When i run this, I get below exception:
java.sql.SQLSyntaxErrorException: ORA-00933: SQL command not properly ended
Please tell me the ways to fix this.
Also, Using JDk 1.6 and ojdbc6-11.2.0.4.jar
I suspect that when you use generated keys with a prepared statement, the Oracle JDBC driver adds the RETURNING INTO clause to the INSERT statement, and that the JDBC driver is too dim to realise that the RETURNING INTO clause can't be used with INSERT INTO ... SELECT ... statements. I get the same ORA-00933 error if I attempt to run an INSERT INTO ... SELECT ... RETURNING ... statement.
What you could try instead is a PL/SQL block where we fetch the 'old' row into a record and then use an INSERT ... VALUES statement with a RETURNING_INTO clause to insert the values into the 'new' row:
DECLARE
l_row t_tpms_cc_request%ROWTYPE;
BEGIN
SELECT * INTO l_row FROM t_tpms_cc_request WHERE order_id = ?;
INSERT INTO t_tpms_cc_request (some_column, some_other_column, ...)
VALUES (l_row.some_column, l_row.some_other_column, ...)
RETURNING order_id INTO ?;
END;
As we're returning values from this, we need to prepare this as a CallableStatement instead of a PreparedStatement, and we need to register parameter 2 as an out parameter. We can then use this out parameter, instead of the getGeneratedKeys() method you're using at the moment, to return the generated key value.
Clearly this approach is Oracle-specific and won't work on other databases. I don't know how much of an issue database portability is to you, nor whether you can return generated keys from an INSERT INTO ... SELECT ... statement in other databases.
I have a Stored Procedure that runs on MS SQL Server. It takes one parameter as input. Basically, it returns multiple rows.
I am calling the SP from my java application by using CallableStatement.
I would like to know if it possible to get the rows returned by the stored procedure in the form of ResultSet in my DAO layer?[like how we get a resultset when we do select * from EmployeeTable] . If yes, how do we do that?
P.S: I don’t have privilege to modify the stored procedure.
SQL Server knows two types of procedures returning results:
Batches
The procedure looks something like this:
CREATE PROCEDURE p_results(
#p_result_sets INT
)
AS
BEGIN
IF #p_result_sets = 1 BEGIN
SELECT 1 a;
END
ELSE IF #p_result_sets = 2 BEGIN
SELECT 1 a;
SELECT 1 b UNION SELECT 2 b;
END
END;
In this case, you don't know in advance what the result sets will look like, and how many of them you'll get. You will have to run the procedure using Statement.execute() as follows:
try (CallableStatement stmt = con.prepareCall("...")) {
boolean results = stmt.execute();
for (;;) {
if (results)
try (ResultSet rs = stmt.getResultSet()) {
// ... Fetch your results here
}
else if (stmt.getUpdateCount() != -1) {}
else
break;
results = stmt.getMoreResults();
}
// After all results are fetched, you can also retrieve OUT parameters, if applicable
}
Table-valued functions
The function looks something like this:
CREATE FUNCTION f_tables1 ()
RETURNS #out_table TABLE (
column_value INTEGER
)
AS
BEGIN
INSERT #out_table
VALUES (1)
RETURN
END
In this case, you don't really need a CallableStatement. An ordinary SELECT statement will do:
try (PreparedStatement stmt = con.prepareStatement("SELECT * FROM f_tables1()");
ResultSet rs = stmt.executeQuery()) {
// ... Fetch your results here
}