executeUpdate() returns always 1 with MERGE statement - java

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;
}
}

Related

JDBC CallableStatement.execute() not throwing exception [duplicate]

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]

How to get *everything* back from a stored procedure using JDBC

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]

Inserting preparedstatement to database - PSQL

This seems like a really simple problem, but I cannot figure out what my problem is. I have a method addTask which adds some info to our database as seen in this code:
public static boolean addTask(String name, String question, int accuracy, int type){
StringBuilder sql = new StringBuilder();
sql.append("INSERT INTO tasks (name, question, type, accuracy) ");
sql.append("VALUES(?, ?, ?, ?)");
try {
Connection c = DbAdaptor.connect();
PreparedStatement preparedStatement = c.prepareStatement(sql.toString());
preparedStatement.setString(1, name);
preparedStatement.setString(2, question);
preparedStatement.setInt(3, type);
preparedStatement.setInt(4, accuracy);
preparedStatement.execute();
preparedStatement.close();
c.close();
return true;
}
catch (SQLException e) {
e.printStackTrace();
return false;
}
}
my problem is that preparedStatement.execute() always returns false, indicating the information hasnt been added to the database. I can run psql and this confirms that nothing has been written to the db. The connection definitely connects to the correct database (i put in some other printlns etc. to check this). I am trying to insert into a newly initialised table that looks like this:
CREATE TABLE tasks
(
id SERIAL PRIMARY KEY,
submitter INTEGER REFERENCES accounts (id),
name VARCHAR(100) NOT NULL,
question VARCHAR(100) NOT NULL,
accuracy INTEGER NOT NULL,
type INTEGER REFERENCES types (id),
ex_time TIMESTAMP,
date_created TIMESTAMP
);
code for DbAdaptor.connect():
public static Connection connect(){
try {
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Properties properties = new Properties();
properties.setProperty("user", USER);
properties.setProperty("password", PASSWORD);
try {
return DriverManager.getConnection(URL, properties);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
where USER and PASSWORD are static fields in the class
You misunderstood the return value of PreparedStatement#execute().
Please carefully read the javadoc:
Returns:
true if the first result is a ResultSet object; false if the first result is an update count or there is no result.
It thus returns — as fully expected — false on an INSERT query. It returns only true on a SELECT query (for which you'd however usually like to use executeQuery() instead which returns directly a ResultSet).
If you're interested in the affected rows, rather use PreparedStatement#executeUpdate() instead. It returns an int as per the javadoc:
Returns:
either (1) the row count for SQL Data Manipulation Language (DML) statements or (2) 0 for SQL statements that return nothing
A return value of 1 or greater would then indicate a successful insert.
Unrelated to the concrete problem: your code is leaking DB resources. Please carefully read How often should Connection, Statement and ResultSet be closed in JDBC?

statement.executeUpdate() always returns 1

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

Why I'm getting an error informing that "a result was not expected" when executing stored procedures on PostgreSQL from Java in a batch?

I have this procedure in the database:
CREATE OR REPLACE FUNCTION replacePageRelevance(id INT, value REAL) RETURNS VOID AS $$
BEGIN
INSERT INTO pageRelevance VALUES (id,value);
EXCEPTION WHEN unique_violation THEN
UPDATE pageRelevance SET relevance = value WHERE pageId = id;
END
$$
LANGUAGE plpgsql;
And this code that calls this function:
private final String PAGE_RELEVANCE_SQL = "SELECT replacePageRelevance(?,?::REAL)";
try (CallableStatement cstm = conn.prepareCall(PAGE_RELEVANCE_SQL)) {
for (Map.Entry<Integer, Double> entry : weightMap.entrySet()) {
cstm.setInt(1, entry.getKey());
cstm.setDouble(2, entry.getValue());
cstm.addBatch();
}
cstm.executeBatch();
} catch (SQLException e) {
LOGGER.error("Error discovering pages relevance: " + e.getNextException());
}
}
When I execute the batch, the values are inserted or replaced in the table, but after that, I'm getting an exception informing that A result was returned when none was expected.
I don't know what is wrong, if the way I call the procedure or the procedure itself. What can be the problem and how to solve it?
Call a procedure with SELECT is the right/only way?
From what I can tell you are using SELECT when call should be used.
An example from the PostgreSQL documentation on the JDBC interface:
// Turn transactions off.
con.setAutoCommit(false);
// Procedure call.
CallableStatement upperProc = con.prepareCall("{ ? = call upper( ? ) }");
upperProc.registerOutParameter(1, Types.VARCHAR);
upperProc.setString(2, "lowercase to uppercase");
upperProc.execute();
String upperCased = upperProc.getString(1);
upperProc.close();
Note that the ? = call syntax for the result is unnecessary in your case - you will want to use just call replacePageRelevance(?,?::REAL)
The reason that this syntax differs from actual PostgreSQL is because this is part of the JDBC specification.

Categories

Resources