JDBC exception handling: warning suppression - java

I am currently learning some basic java SQL coding, making a basic terminal UI for my SQL project. I have been using PostgreSQL
I am using PreparedStatement to ensure myself from SQL injections, better be safe than sorry. PreparedStatement seems to always fire warnings for some reason, which I figured is a subclass to exceptions (and should get caught in the exceptions).
In my SQL triggers and functions I have created and tested the cases where I should fire exceptions, and they are all getting and working properly catch block.
I guess using #Supresswarnings to let the compiler know I want to do suppress my warnings, but I might want to catch some warnings in the catch block, so I might be looking for a different solution.
The problem / question is:
I would like to have my prints in the try block, that is no exceptions were fired.
What could be done to have my prints after the execution of preparedStmt.executeQuery in the try block?
Is using #SuppressWarnings usually considered good coding practice when dealing with exception handling like this?
If I am incorrect about preparedStatement.executeQuery() always fires warnings, what is considered warnings in SQL language?
My code:
void registerStudent(Connection conn, String toBeInserted, String insertedTo)
throws SQLException{
ResultSet res;
String query = "INSERT INTO Registrations VALUES (?, ?)";
PreparedStatement ps = conn.prepareStatement(query);
ps.setString(1, toBeInserted);
ps.setString(2, insertedTo);
try {
ps.executeQuery();
}catch (SQLException e) {
SQLWarning warning = ps.getWarnings();
if (warning != null){
System.out.println("Yay, insert succeeded!
values: "+ toBeInserted +" were inserted into
"+insertedTo);
}else {
System.out.println("Nothing inserted, here goes the big exception SQL fired for ya'll: "
+ e.getMessage());
}
}
}

There are still some flaws. I added also the case of an AUTOINCREMENT primary key - which you can retrieve after inserting.
Flaws:
List which fields you insert, such that a later addition of a column in the database does not cause problems, and the order of columns is clear in the code.
Use try-with-resources to close statement, result set and such, even when one returns inside, or an exception is thrown.
Use executeUpdate for INSERT (and similar modifying operations) returning an update count.
You catched the SQLException, which then should be rethrown, to let the caller determine what to do on gross failure.
SQLWarnings (as does the actual SQL and the SQLException) might be interesting,
though I not often see SQLWarnings being treated. Maybe forget it for such simple case, and instead try new SQL statements first in a separate SQL tool.
So:
/**
* #return the generated primary key, the Student ID.
*/
int registerStudent(Connection conn, String toBeInserted, String insertedTo)
throws SQLException {
String query = "INSERT INTO Registrations(ToBeInserted, InsertedInto) VALUES(?, ?)";
try (PreparedStatement ps = conn.prepareStatement(query),
Statement.RETURN_GENERATED_KEYS) {
ps.setString(1, toBeInserted);
ps.setString(2, insertedTo);
int updateCount = ps.executeUpdate();
if (updateCount == 1) {
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) {
int id = rs.getInt(1);
return id;
}
}
throw new SQLException("No primary key generated");
} else {
SQLWarning warning = ps.getWarnings();
Logger.getLogger(Xyz.class.getName()).info("Not added, values: "
+ toBeInserted + " were not inserted into "+ insertedTo + ": " + warning);
throw new SQLException("Not added");
}
}
}

Thank you very much I know I really shouldn't respond to your post but it helped be a lot. I think I got confused about ps.executeQuery() and ps.executeUpdate() methods.
executeQuery() generated a warning because there was nothing being returned, although it did work to execute the update to the database with that method.
The other tips and tricks is something I will bear with me as well.

Related

Code executing time is incredibly long

I'm trying to move data from one table to another(both table are same basically), the method that I'm using is INSERT-SELECT.
The problem that I'm facing is my java program seem like frozen there, but I can still force close it with ^C easily, so I think it might be still alive but stuck for some reason.
This is my code which stuck in this problem
public String moveData(String sql, int day) {
Connection con = null;
PreparedStatement stmt = null;
int count;
try {
con = DriverManager.getConnection(DSN, Username, Password);
stmt = con.prepareStatement(sql);
stmt.setInt(1, day);
count = stmt.executeUpdate();
return String.valueOf(count);
} catch (SQLException e) {
System.out.println("SQL exception: " + e.getMessage());
e.printStackTrace();
return "false";
} finally {
try {
stmt.close();
} catch (Exception e) {
}
try {
con.close();
} catch (Exception e) {
}
}
}
and the SQL, executing it in SQL developer is really fast with 0.231 sec for 23k row of data
insert into Request_History (customer_id, request_id, status, transaction_date, last_modified)
SELECT customer_id, request_id, status, transaction_date, current_timestamp
from Request_Log
where transaction_date <= (sysdate - NUMTODSINTERVAL(:1 ,'DAY'))
I see no problem on them, is there anything that I missed?
Update
Since there's no resolve on the program and SQL command, I'd like to change a way of thinking on the DB side.
Could anyone please tell me what kind of privileges do I need to execute my INSERT-SELECT SQL command on 11g without problem? because from what I can see that this command would only needs basic privileges such as SELECT/INSERT/UPDATE/DELETE to execute.
The time of the whole insert process depends on three factors:
How many rows are in the query result set
Is there any index, which can be used at query level (best for this would be an index on Request_Log.transaction_date
Are there any constraints, indexes which have to be maintained during the insert phase (Request_History table)
Check the exec plan of the statement, to see if there's anything - worst things are full table scans. If you allowed, you can paste here the execution plan as well.
there is probably something wrong with the way you open your connection, something with you session parameters. You might not have enough undo space or another limiting factor. Maybe you can get a dba to check on it while you execute this over java vs. SQL Developer.
alter you statement to
create table Request_History_test
as SELECT
customer_id, request_id, status, transaction_date, current_timestamp
from
Request_Log
where
transaction_date <= (sysdate - NUMTODSINTERVAL(:1 ,'DAY'))
if this goes fast in contrast to your "insert select", then contact your DBA. There might be known issues with your undo space / redo logs. I'm not a DBA, so this answer is vague, I just had similar problems once.
oh, and check your dba_hist_sqlstat to see where the time is lost.

Return integer for the last row in MYSQL using JDBC

I'm new to working with JDBC commands. I have a database in MYSQL and each entry gets an ID. As initially created the ID was just a static variable that I iterated when the constructor runs. This was okay until I started deleting entries or running the program a second time. Then I start getting collisions. I need a way to return the highest row in the table and assign it to an integer that I can iterate.
The QuerySELECT MAX(ID) FROM table seems to get the value that I'm looking for. But I'm not sure of the syntax to get that value into an integer so I can return it.
public int getHighestRow() {
PreparedStatement ps;
int highestID = 0;
try {
ps = getSQLDB().prepareStatement("SELECT MAX(studentID) FROM student");
ps.execute();
} catch (SQLException e){
Logger.getLogger(Undergraduate.class.getName()).log(Level.SEVERE, null, e);
}
if (highestID > 0) return highestID;
else return 0;
I have a feeling this is very simple, but I wasn't able to find an existing answer. Or is there a more elegant way to do this in general?
SQL of different providers solve the retrieval of automatic generated keys differently. JDBC provides a standard solution.
Better use this JDBC solution, as it prevents mixing up those keys when insertions are done at the same time.
try (PreparedStatement ps = getSQLDB().prepareStatement(
"INSERT INTO student(....) VALUES(?, ..., ?)",
Statement.RETURN_GENERATED_KEYS)) { // Without StudentId
ps.setString(1, name);
...
ps.executeUpdate();
try (ResultSet rsKeys = ps.getGeneratedKeys()) {
if (rsKeys.next()) { // Only one record inserted
int studentId = rsKeys.getInt(1); // One key generated
}
}
} catch (SQLException e){
Logger.getLogger(Undergraduate.class.getName()).log(Level.SEVERE, null, e);
}
The mechanism with try(...). try-with-resources, ensures that close is called automatically.

SQLServerException: A result set was generated for update

I'm trying to insert a new record into an MS SQL database, and I'm getting an exception I've never seen before. When I call executeUpdate the following exception is thrown:
com.microsoft.sqlserver.jdbc.SQLServerException: A result set was generated for update.
This is the Java code that produces the error:
// addComment method adds a new comment for a given requestId
public CommentBean addComment(CommentBean comment) {
PreparedStatement stmt = null;
INative nat = null;
Connection conn = null;
try {
nat = dbConn.retrieveNative();
conn = (Connection)nat.getNative("java.sql.Connection");
stmt = conn.prepareStatement(ADD_COMMENT);
stmt.setInt(1, comment.getRequestId());
stmt.setString(2, comment.getComment());
stmt.setString(3, new SimpleDateFormat("MM/dd/yyyy").format(comment.getDateCreated()));
stmt.setString(4, comment.getCreatedBy());
comment.setCommentId(stmt.executeUpdate()); // exception
} catch(Exception ex) {
System.err.println("ProjectRegistration::SQLDAO - addComment");
ex.printStackTrace();
} finally {
try {
if (stmt != null) stmt.close();
} catch (Exception e) {}
}
return comment;
}// end addComment
Where ADD_COMMENT is defined as a String:
private static final String ADD_COMMENT = "INSERT INTO RequestComments OUTPUT INSERTED.commentId VALUES(?,?,?,?)";
For the sake of being thorough, the table is defined as:
CREATE TABLE RequestComments (
commentId int NOT NULL PRIMARY KEY IDENTITY(1,1),
requestId int FOREIGN KEY REFERENCES Requests(requestId),
comment varchar(400),
dateCreated date,
createdBy varchar(12)
);
I don't think I'm doing anything terribly complicated here, but I can't think of why I'm getting this exception. I have a method in the same class which does the exact same type of insertion (literally the same query with a different table name and number of values), and it has no issues. Does anyone have any ideas on how to resolve this issue?
This particular error can also be caused by an INSERT-trigger, which has a SELECT-statement as a part of the trigger code.
To test whether this is the case, you can try:
using executeQuery(), instead of executeUpdate() - and display the result.
executing the insert in tool like MySQL Workbench, SQL Server Management Studio, or whatever flavour of database design tools are available for your DBMS, to see whether a result is returned.
Related: sql server error "A result set was generated for update"
I'm hoping this may help others looking at the same error message, as it did for me. My solution was to live with a call to executeQuery(), although it only handles an underlying issue, instead of fixing it.
This instruction stmt.executeUpdate() is not returning the commentId, it returns a ResultSet which you could then get the commentId from. Something like this,
ResultSet rs = stmt.executeQuery(); // Not update, you're returning a ResultSet.
if (rs.next()) {
comment.setCommentId(rs.getInt(1));
}
you are using OUTPUT in your insert query i.e you will get a resultset after your query executes and to hold that you need an object of class ResultSet to hold that data
SqlServer : When SET NOCOUNT is ON, the count is not returned. When SET NOCOUNT is OFF, the count is returned.
Connection conn = DriverManager.getConnection(connectDB,user,pwd);
String sql = " set nocount off;INSERT INTO test (name) values (1)";
PreparedStatement prepareStatement = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
System.out.println(prepareStatement.executeUpdate());
ResultSet generatedKeys = prepareStatement.getGeneratedKeys();
if(generatedKeys.next()){
System.out.println(generatedKeys.getString(1));
}
Related:
set-nocount-on-usage
I've had a similar problem where after a while an insert on a autonumber table would give a "A result set was generated for update." at random. I use connection pooling and somehow the driver can get into a state where executeUpdate in combination with Statement.RETURN_GENERATED_KEYS doesn't work anymore. I found out that in this state an executeQuery does the trick, but in the initial state executeQuery does not work. This lead me to the following workaround:
PreparedStatement psInsert = connection.prepareStatement("INSERT INTO XYZ (A,B,C) VALUES(?,?,?)", Statement.RETURN_GENERATED_KEYS);
ResultSet rs = null;
try {
psInsert.setString(1, "A");
psInsert.setString(2, "B");
psInsert.setString(3, "C");
Savepoint savePoint = connection.setSavepoint();
try {
psInsert.executeUpdate();
rs = psInsert.getGeneratedKeys();
} catch (SQLServerException sqe)
{
if (!sqe.getMessage().equals("A result set was generated for update."))
throw sqe;
connection.rollback(savePoint);
rs = psInsert.executeQuery();
}
rs.next();
idField = rs.getInt(1);
} finally {
if(rs != null)
rs.close();
psInsert.close();
}

Resultset not open

I get following error on Result set
java.sql.SQLException: ResultSet not open. Verify that autocommit is OFF.
at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source)
at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source)
at org.apache.derby.client.am.ResultSet.next(Unknown Source)
public ResultSet insertDb(int Id,String firstName,String lastName,String title) throws SQLException{
try {
try {
Class.forName(driver);
con = DriverManager.getConnection(connectionURL);
} catch (SQLException ex) {
Logger.getLogger(Connect.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(Connect.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(con.getAutoCommit());
statement = con.createStatement() ;
res = statement.executeQuery("SELECT * FROM CUSTOMER") ;
con.setAutoCommit(false);
System.out.println(con.getAutoCommit());
while(res.next()){
if(res.getString("ID").equalsIgnoreCase(Integer.toString(Id))){
UNIQUE = false;
error= "Duplicate Entry Found Please Enter New Data";
throw new SQLException("Duplicate info<br>ID " + Id );
}
}
// IF value to be added IS UNIQUE
if(UNIQUE){
String qry1= "insert into CUSTOMER(ID, FIRSTNAME,LASTNAME,TITLE) values(?,?,?,?)";
stm = con.prepareStatement(qry1);
String ID=Integer.toString(Id);
stm.setString(1, ID);
stm.setString(2, firstName);
stm.setString(3, lastName);
stm.setString(4, title);
stm.executeUpdate();
}
}
catch(Exception e){
String errorMessage = "Exception caught : ";
System.out.println(errorMessage + e.toString());
}finally{
if (con != null){
con.close();
}
}
return res;
}
Try moving the setAutoCommit() and getAutoCommit() to before you create and execute the statement. Changing it after you execute the statement may be invalidating the query.
The problem is that you have closed your query before reading your resultset. Closing the query, closes the resultset, hence why you get the "ResultSet not open" error. You should close the query right at the end, in a finally block:
i.e. con.setAutoCommit(false);
will close the query and along iwth it it closes the resultset also.
Not strictly related, but your code probably doesn't do what you expect. This kind of read-modify-write code doesn't work well when there are multiple concurrent invocations.
If you imagine two invocations running though the code, it becomes clear that sometimes, depending on the execution order, BOTH invocations could reach the insert statement.
In addition, selecting from a table without using a WHERE clause is not generally useful. In this case you select '*', then iterate over all the results to see if "ID" == Id. The database is much much better at that than java is. You should add a where clause. (Note that this still won't solve the above problem)
Its also generally a bad idea to 'select *' from any table. Just pick the columns that you need. This will 'fail fast' if the schema changes and the columns that you need are no longer available, and will allow the database optimiser to do the 'right thing' about its disk accesses.
Finally, if its just a numeric ID that you are looking to assign, its normal practice to use 'autonumber' for these, rather than get the program to pick them. Different databases call them different things, so you might also know them as IDENTITY, or have to use a sequence.
In case it helps anyone down the line, I had the same error with Derby 10.5.1.1 that turned out to be a bug in the driver itself that would appear some times and not others depending on the underlying data. Upgrading the driver to a newer version (10.8.2.2) resolved the problem.

Strange problem with JDBC, select returns null

I am trying to use JDBC and my query is working in some cases but not working in others.
I would really appreciate any help.
Some of my code:
public Result getSpecificTopic()
{
String query = "Select msg_body, msg_author from lawers_topic_msg";// where msg_id=2 order by msg_id desc";
try
{
con = mysql.getConnection();
//Statement stmt = con.createStatement();
PreparedStatement stmt = con.prepareStatement(query);
//stmt.setInt(1, topicId);
ResultSet rs = stmt.executeQuery(query);
int rowCount = rs.getRow();
specificTopic = ResultSupport.toResult(rs);
con.close();
stmt.close();
}
catch(Exception e)
{
}
return this.specificTopic;
}
public void setTopicId(String num)
{
this.topicId = Integer.parseInt(num);
}
public int getTopicId()
{
return this.topicId;
}
However if i change
String query = "Select msg_body, msg_author from lawers_topic_msg";
to the
String query = "Select msg_body, msg_author from lawers_topic_msg where msg_id = " + topicId;
Then the resultset retunrs nothing....
I am breaking my head here and still cannot figure out what is the problem
You still aren't closing your resources properly. That should be done in a finally block:
http://www.java-blog.com/correct-closing-jdbc-resources
As a first step, it'd be worth making sure an exception's not being thrown - at the very least log something in your catch() block.
Also be worth logging the SQL generated, and making sure that actually returns what you expect from the database when running it directly.
If you have multiple databases, it'd be worth confirming you're running against the one you think you are - I'm embarrassed to admit I've been caught out that way before.
several issues with your code, i'll keep it short:
don't encapuslate with try / catch at this layer, aspecially not since you're doing no error management. this.specificTopic looks global, so if your query fails it will return whatever was stored in this.specificTopic.
also try what BobbyShaftoe said. print in console or use your debugger. This should give you a good indication on what is wrong.
my first guess would be Integer.parseInt(num) could throw an exception. if so, the sql statement will be broken.
secondly, as Makach pointed out, there are several issues. first the catch-all
you should not use string concatenation, like
....where msg_id = " + topicId;
but rather
....where msg_id = ?"
stmt.set Int(1,topicId)
edit: it seems thats what you were trying anyways, SO sucks in some characters.

Categories

Resources