Best Practice for handling duplicate INSERT statements - java

I am currently using Java to try to INSERT a customer in my Database:
public void saveCustomer(Customer c) {
getConnection();
try {
CallableStatement pstmt = con.prepareCall("{call sp_saveCustomer(?,?)}");
pstmt.setString(1, c.getName());
pstmt.setString(2, a.getLastName());
pstmt.executeUpdate();
closeConnection();
} catch (SQLException e) {
// Handle Exception
}
}
When a user tries to INSERT a duplicate record, the catch block will trigger. What I'm worried about is that I don't know if this means a performance hit. Isn't a better way to check if a customer already exists? Something like:
public void saveCustomer(Customer c) {
getConnection();
boolean exists = false;
CallableStatement pstmt = con.prepareCall("{call sp_checkCustomerExistence(?,?)}");
pstmt.setString(1, c.getName());
pstmt.setString(2, a.getLastName());
ResultSet rs = pstmt.executeQuery();
if (rs.next()) exists = true;
if ( exists ){
closeConnection();
return;
}
CallableStatement pstmt = con.prepareCall("{call sp_saveCustomer(?,?)}");
pstmt.setString(1, c.getName());
pstmt.setString(2, a.getLastName());
pstmt.executeUpdate();
closeConnection();
}
What would be the best solution based on the best performance? Should I check myself if the customer exists or should I let the Exception be thrown?

I think checking the duplicate is preferable than catching the exceptions. Only suggestion I have it to perform the check in your procedure itself if you can i.e. create a procedure as sp_check_and_save_customer which will check for duplicate and save if it doesn't already exist. This way there will be very less time difference between the check and insert, which becomes more crucial in heavy loaded applications.
Still, please have your exception handling as well as it is still possible that another process inserts the data in between the check and insert.

Checking before hand = two database calls or two IO interactions. Attempting to just insert with the possibility of an exception occurring exception is only one. As long as the exceptions are handled, they aren't necessarily a bad thing.
Although probably not preferred, it would be the fastest of the two methods you've listed.

} catch (SQLException e) {
The SQLException that you are catching could mean anything. It doesn't necessarily mean that it occured because of a duplicate row in the database table. So, simply it is not reliable enough to be used for that use-case.
You just have to make a call before trying to create one, to handle it in a proper manner.

Related

H2 Database result set is readonly

I'm getting the SQLNonTransientException error when trying to update one of my rows in a H2 database.
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
//read data from database
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM BCSTASKS_SERVICE");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
if(rs.getString("Status").equals("Neu") && rs.getBoolean("wasShown") == false) {
rs.updateBoolean("WASSHOWN", true);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
The error message already suggests that I should use conn.createStatement and set the ResultSet to CONCUR_UPDATABLE. The error occurs at the line with rs.updateBoolean(...);
Error Message:
The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE). [90140-210]
The problem is I don't know where and how I should use this method. In the same function or at the start of the program?
Most DB code I see doesn't attempt to use the fact that resultsets are updatable, and will instead fire off an additional UPDATE query, which works fine.
However, sure, H2 supports updateable resultsets too. However, some of the features that ResultSets have actually have quite a cost; the DB engine needs to do a boatload of additional bookkeeping to enable such features which have a performance cost. Lots of database queries are extremely performance sensitive, so by default you do not get the bookkeeping and therefore these features do not work. You need to enable them explicitly, that's what the error is telling you.
You're currently calling the 'wrong' preparedStatement method. You want the more extended one, where you pick and choose which additional bookkeeping you want H2 to do for you, in order to enable these things. You want this one.
conn.prepareStatement(
"SELECT * FROM BCSTASKS_SERVICE",
ResultSet.TYPE_SCROLL_INSENSITIVE, // [edited]
ResultSet.CONCUR_UPDATABLE);
That CONCUR_UPDATABLE thing is just a flag you pass to say: Please do the bookkeeping so that I can call .update.
[edited] This used to read 0 before, but as #MarkRotteveel pointed out, that's not valid according to the documentation.
You have to put update query for update data in database but you are going with select query that is the problem.
Select query is used if you have to fetch data from database.
Update query is used for update data in database where data already stored in database but you just overwrite data.
Here down is modified code:
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
PreparedStatement stmt = conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET wasShown = ? WHERE status = ? AND wasShown = ?");
stmt.setBoolean(1, true);
stmt.setString(2, "Neu");
stmt.setBoolean(3, false);
stmt.executeUpdate();
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
You need to create a separate query/prepareStatement for an update. In your case as far as I can see you need only one update query:
conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET WASSHOWN=true where
Status = 'Neu' and wasShown = false "

Commit/Autocommit or Rollback recommended after calling Stored Procedures

It's not so usual to call stored procedures in Java so I fear the information online about the process is a bit limited. Usually I use connection.setAutoCommit(false); in the beginning and connection.commit(); in the end with regular DB-Updates.
Before I wasn't using the c.commit(); line and since this function is called via Ajax I saw in FireBug the Ajax-Request never finishes which made me add the ps.setQueryTimeout(99); line.
Right now my method for calling stored procedures looks like this:
private void callProcedure(int customer, int user, String stmt) {
Connection c = null;
PreparedStatement ps = null;
try {
c = ConnectionTools.getConnection();
c.setAutoCommit(false);
//"EXEC CustomerUpdate ?, ?";
ps = c.prepareStatement(stmt);
ps.setEscapeProcessing(true);
ps.setQueryTimeout(99);
ps.setInt(1, customer);
ps.setInt(2, user);
ps.executeUpdate();
c.commit();
} catch (NamingException | SQLException ex) {
if (c != null) {
try {
c.rollback();
} catch (SQLException ex1) {
Logger.getLogger(CustomerProcedures.class.getName()).log(Level.SEVERE, null, ex1);
}
}
Logger.getLogger(CustomerProcedures.class.getName()).log(Level.INFO, "SQL: {0}; {1}; {2}", new Object[]{stmt, customer, user});
Logger.getLogger(CustomerProcedures.class.getName()).log(Level.SEVERE, null, ex);
} finally {
ConnectionTools.disconnect(ps, c);
}
}
From the material related to my question there is one example for Oracle but rolling back, not committing: calling PLSQL procedure from Java - this example seems to be a good one since there was just a minor mistake not related to the issue itself. In my impression it seems fine to set a conn.setAutoCommit(false);.
In another example with only little attention nothing like that was used: Java: Calling a stored procedure in an oracle database
In other sources I there wasn't much to find either. Coderanch had a short discussion, but in my Stored Procedure is no commit (http://www.coderanch.com/t/621006/java/java/commit-written-procedures-called-Java)
In the official documentation there isn't much attention on the commit issue too: https://docs.oracle.com/javase/tutorial/jdbc/basics/storedprocedures.html
They just recommend to use prepareCall instead of prepareStatement and execute instead of executeUpdate
cs = this.con.prepareCall("{call RAISE_PRICE(?,?,?)}");
cs.setString(1, coffeeNameArg);
cs.setFloat(2, maximumPercentageArg);
cs.registerOutParameter(3, Types.NUMERIC);
cs.setFloat(3, newPriceArg);
cs.execute();
There's just:
Note: As with Statement objects, to call the stored procedure, you can
call execute, executeQuery, or executeUpdate depending on how many
ResultSet objects the procedure returns. However, if you are not sure
how many ResultSet objects the procedure returns, call execute.
But so far nothing to commit...

java.lang.NullPointerException ... Initializing the JDBC MYSQL connection

Please don't suggest me to use InternalFrame or Dialogs. I can't start the project from beginning.
Theme: I'm building a GUI program to display mark-sheet. I've taken 3 JFrames & 1 simple class...
Frame1.java
It's having 1 JTextField to enter roll_no. & 2 buttons to feedData in DB & showResult. feedData button calls Frame2 & showResult button calls Frame3.
Frame2.java
For feeding data have several JTextFields & Buttons that transfer content to mySQL DB.
Frame3.java
is a result window that fetches content from DB.
Support.java
Contains static variables & getter-setter methods for them
.....
.....//contains in Support.java
public boolean add() {
query = "Insert into table1 (enroll,Sname,Fname,sub1,sub2,sub3,sub4,sub5 )values(?,?,?,?,?)";
try {
PreparedStatement psmt = conn.prepareStatement(query);
psmt.setString(1, enroll);
psmt.setString(2, Sname);
psmt.setString(3, Fname);
psmt.setInt(4, sub1);
psmt.setInt(5, sub2);
psmt.setInt(6, sub3);
psmt.setInt(7, sub4);
psmt.setInt(8, sub5);
int y = 0;
y = psmt.executeUpdate();
if (y == 0) {
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
add() is called on pressing save button in Frame2.java . . . If catch block is executing, why println(query) printing NULL
Based on some of your question tags and responses in the comments to other answers and on the question itself, I'm presuming that somewhere in your code, you intend to call
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection(url, username, password);
This is not happening before your add() method is called. In order to fix it, I'd recommend this (bulk of code borrowed from Vivek bhatnagar's answer):
try {
conn = DriverManager.getConnection(url, username, password);
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO `table`
(pid,tid,rid,tspend,description) VALUE
(?,?,?,?,?)");
pstmt.setString(1, pid );
pstmt.setString(2, tid);
pstmt.setString(3, rid);
pstmt.setInt(4, tspent);
pstmt.setString(5,des );
pstmt.executeUpdate();
} catch (Exception e) {
// whatever you want to do to handle the exception
} finally {
// close your connection
}
If you're on Java 7, set up like this:
try (Connection conn = DriverManager.getConnection(url, username, password)) {
try (PreparedStatement pstmt = conn.prepareStatement(/*sql here*/)) {
// Your code here
} catch (SQLException sqle) {
// handle exceptions from the statement
}
} catch (SQLException outerSqlEx) {
// handle exceptions from connecting
}
How could I tell what your problem was (general help for NullPointerException)?
NullPointerException is only thrown when you try to call a method on a null variable (and at a few other specific times, as noted in the API documentation). The easy way to locate a NullPointerException is to look for the line the stack trace indicates, and then look for the dots on the line. There's only two lines in your try block that can throw a NullPointerException.
Statement stmt = conn.createStatement();
// could be here ----^
and
y = stmt.executeUpdate(query);
// or --^
So let's look at the dots. The first one will throw when conn is null. The second one will throw when stmt is null. In your original code, which you've now edited in response to the other answers, you set the value of query after you called conn.createStatement();. Since query was still null in your catch block, we know that it hadn't yet been set, and thus it must be the first one, so conn is null at that point in the program.
Furthermore, since the API Documentation for createStatement
implies that it will either return a valid Connection object or throw an SQLException, we can be pretty sure that stmt will never be null when executeUpdate is called.
In your try block, you are calling a method that is possible to throw an exception before setting the variable in question:
Statement stmt = conn.createStatement();
query = "Insert into table1 (enroll,Sname,Fname,sub1,sub2,sub3,sub4,sub5 )values('" + getEnroll() + "','" + getSname() + "','"+getFname()+"',"+getSub1()+","+getSub2()+","+getSub3()+","+getSub4()+","+getSub5()+")";
Therefore, if your code fails on the conn.createStatement() line, it will enter the catch block without the query variable being initialized.
You can fix this simply by switching the order of the statements, or by putting the query line outside and before the try/catch blocks.
Adding to what #Southpaw answered :
you can use something like this also :
PreparedStatement pstmt = con.prepareStatement("INSERT INTO `table`
(pid,tid,rid,tspend,description) VALUE
(?,?,?,?,?)");
pstmt.setString(1, pid );
pstmt.setString(2, tid);
pstmt.setString(3, rid);
pstmt.setInt(4, tspent);
pstmt.setString(5,des );
pstmt.executeUpdate();
Kindly Note its benefits:
1."Query is rewritten and compiled by the database server"
If you don't use a prepared statement, the database server will have to parse, and compute an execution plan for the statement each time you run it. If you find that you'll run the same statement multiple times (with different parameters) then its worth preparing the statement once and reusing that prepared statement. If you are querying the database adhoc then there is probably little benefit to this.
2."Protected against SQL injection"
This is an advantage you almost always want hence a good reason to use a PreparedStatement everytime. Its a consequence of having to parameterize the query but it does make running it a lot safer. The only time I can think of that this would not be useful is if you were allowing adhoc database queries; You might simply use the Statement object if you were prototyping the application and its quicker for you, or if the query contains no parameters.

How to catch constraint violation inside the resultset loop?

I was working on a servlet that will generate a unique code and update that in a mySQL database.
Now, in that, I want to catch any exception thrown in case that unique code already exists in the mySQL table and generate a new code and try updating the database. The problem is I want to do this WITHIN the for loop itself. The code is as follows:
try
{
connection = datasource.getConnection();
SQLUpdate = "INSERT INTO Voucher_dump VALUES( '"+unique_code+"','08-10-2011 04:48:48','0')";
PreparedStatement ps1 = connection.prepareStatement(SQLUpdate);
ps1.executeUpdate();
ResultSet r = ps1.getResultSet(); // this is where I'm checking if it's a duplicate
if(r==null)
out.println("This is a duplicate");
else out.println("Updated");
trial12= "08-10-2011 04:48:480.03999855056924717a";
SQLUpdate = "INSERT INTO Voucher_dump VALUES( '"+trial12+"','08-10-2011 04:48:48','0')";
ps1 = connection.prepareStatement(SQLUpdate);
ps1.executeUpdate();
r = ps1.getResultSet();
if(r==null)
out.println("This is a duplicate");
else out.println("Updated");
}
catch (SQLException sqle)
{
sqle.printStackTrace();
}
I don't want to wait till the end of the entire loop to catch the SQLException (I have already defined this key in mySQL as primary). The moment, the result comes back as a duplicate entry, I want to re-generate this key and attempt the update again.My output for this particular code is coming blank on my output page (all other parameters are showing correctly). Neither is "This is a duplicate" displayed nor is "Updated". Maybe, ResultSet is not the best way to do it. Could you guys give me some advice on what would be the best way forward ?
Some advice in no particular order:
Close the connection in a finally block.
Close statements individually if you'll be creating many of them before closing the connection. ("Many" is defined by your DBAs.)
Format your code.
Don't use stdout and/or stderr from real code. Pick a logging framework.
Consider using some helper classes to simplify (and correct) your database access, like Spring's JdbcTemplate.
Make sure to include relevant context when you post example code.
Due to #6, I don't know what out is, but I suspect the reason you're not seeing anything is that you're inserting a duplicate value with the first statement, which will cause a SQLException from that line, not at getResultSet(), where you seem to expect it. Since the error is written to stdout, it'll show up in your server logs somewhere, but nothing will be written to out. I'm not sure why you think getResultSet() will return null or not null depending on whether there was a constraint violation. Take a look at the javadoc for that method.
Update: 7. As BalusC points out, never, ever concatenate a string directly into a JDBC Statment. Use PreparedStatment's placeholders and set* methods. For info on SQL injection, see Wikipedia and XKCD.
How about this code?
try {
Class.forName(driver).newInstance();
conn = DriverManager.getConnection(url + dbName);
System.out.println("Connected to the database");
int i = 1; //get the unique code
boolean isInserted = false;
while (!isInserted) {
try {
PreparedStatement preparedStatement = conn.prepareStatement("INSERT INTO test values (?)");
preparedStatement.setInt(1, i);
preparedStatement.executeUpdate();
isInserted = true;
} catch (com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException e) { //Catch the particular exception which throws error on unique constraint. This may depend on Java/MySQL your version
i++; //get the next unique code
}
}
System.out.println("Disconnected from database");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.close();
} catch (Exception e) {
}
}

Why is this query not returning any results?

I am leaning some Java for school, and for some reason the code below doesn't return any results? Is there a problem with this code? There is a single record in the mysql database, with a studentID (which is int(10)) of 12.
public static ResultSet GetByID(int studentID) {
// This method loads the mysql driver and establishes the database connection
Connect();
ResultSet results = null;
try {
String query = "SELECT * FROM student where studentID = ?";
PreparedStatement statement = Connection.prepareStatement(query);
statement.setInt(1, studentID);
results = statement.executeQuery();
} catch (SQLException ex) {
LogException(ex);
} catch (Exception ex) {
System.out.println(ex);
}
// This method terminates the mysql connection.
Disconnect();
return results;
}
The calling code is:
#Override
public ResultSet query() {
return DB.GetByID(getStudentID()); // this is 12
}
This does not return null, rather just an empty result set.
A ResultSet can only be used while the connection is still open. Read it before you "disconnect".
If fixing that doesn't give you a different result, the most likely cause is that the query didn't match any rows in the table.
You should get into the habit of closing connections, streams, etc in finally blocks to prevent resource leakage. (Your lecturer should have explained that. Check your notes / text book.)
Finally, since you are a beginner, it is worth pointing out that you should always conform to the accepted Java conventions for method naming. Java method names should start with a lower-case letter. GetByID should be getByID and Disconnect should be disconnect.
(And if your lecturer / tutor doesn't or didn't dock marks for that, he / she should be condemned to writing Visual Basic for the next 5 years for crimes against the Software Engineering industry.)
If you want to return the resultSet after closing the connection you have to use CachedRowSet.
As StephenC said, your ResultSet is empty after closing the connection.

Categories