Transaction not starting unless I do modification - java

I'm having an issue where my transaction doesn't truly seem to be starting unless I issue a command that appears to be transaction worthy (the last part is conjecture).
Below I have a method decorated with the #Transaction attribute, it's public, it's being called via the spring proxy. I'm connecting to SQL Server, using Microsoft's JDBC driver version 4.2. And getting an app lock, if I try and get the app lock before doing something that's transaction worth the transaction doesn't appear to start. I'm guessing this based on the returned error code (-999) which is consistent with not being in a transaction.
This code works perfectly fine in my other project, but there we are doing more than just grabbing an app lock.
Here's the method (Ignore the fact that this would just lose the app lock right after fetching it)
#Transactional
public Boolean getAppLock(String lockString, Integer timeoutMilliseconds) {
try {
Session session = entityManager.unwrap(Session.class);
int result = session.doReturningWork(connection -> {
int execResult = -1;
// Works if I add this line
// connection.prepareStatement("insert into foo (baz) values('bar')").execute();
// This line works also, which is stupid, cause it doesn't even update anything
// connection.prepareStatement("update foo set baz = baz where id = 1231241").execute();
try (PreparedStatement statement = connection.prepareStatement("declare #i int; exec #i = sp_getapplock ?,?,?,?; select #i;")) {
statement.setString(1, lockString);
statement.setString(2, "Exclusive");
statement.setString(3, "Transaction");
Integer timeout = timeoutMilliseconds == null ? 0 : timeoutMilliseconds;
statement.setInt(4, timeout);
try (ResultSet resultSet = statement.executeQuery()) {
resultSet.next();
// Complains about not being in a transaction, unless
// I have a random statement that seems transaction worthy
execResult = resultSet.getInt(1);
} catch (Exception ex) {
logger.error("Failed getting the result set from the app lock", ex);
}
} catch (Exception ex) {
logger.error("Error executing statement sp_getapplock", ex);
}
return execResult;
});
// see return codes here (https://msdn.microsoft.com/en-us/library/ms189823.aspx), 0+ is good, < 0 is bad
return result >= 0;
}
catch (Exception ex){
throw new RuntimeException(ex.getMessage());
}
}
I'm not sure if this is a driver or a spring thing, I'm leaning toward driver thing.

Related

The driver was unable to create a connection to mysql

I'm programming in java SE and I get an error when trying to access to create a connection to mysql. I can connect to mysql, in fact, the error shows up when running a bucle.
What I do in this program is to check for a String in the table Colors of my database and if it finds nothing it creates this String in the table with an autoincrementing id.
It works fine, but after having checked it for a while it gives me the error.
I attach the image of the error and the code where I create the connection.
public Integer codiColor(String col){
Integer codi=null;
if(col.equals(""))
return 1;
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(url, usuari, password);//here is the error
try {
Statement st = con.createStatement();
String sql = "SELECT CODICOL FROM COLORES where COLOR ='"+col+"'";
ResultSet res = st.executeQuery(sql);
if(res.next()){
codi = res.getInt("CODICOL");
}
try { res.close(); } catch (Exception e) {}
try { st.close(); } catch (Exception e) {}
}
catch(SQLException s){
JOptionPane.showMessageDialog(null, "Error:\n"+s.getMessage(),
"ERROR.",JOptionPane.ERROR_MESSAGE);
}
finally{
try { con.close(); } catch (Exception e) {}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return codi;
}
This code is the one that checks if the color already exists or not.
EDIT:
url = "jdbc:mysql://192.168.1.200:3306/mybbdd?zeroDateTimeBehavior=convertToNull";
The problem is that con is evidently a member variable where it should be a local variable. If this piece of code and others like it are called from multiple threads, a con value will be overwritten and therefore lost, so a connection leak will result. You will almost certainly also have other problems due to concurrent use of the connection. Make it a local variable.
NB you haven't needed the Class.forName() line since 2007. The close of the connection, statement, and result set would be redundant if you used try-with-resources. And you should use a prepared statement.
String sql = "SELECT CODICOL FROM COLORES where COLOR =?";
try (con = DriverManager.getConnection(url, usuari, password);
PreparedStatement st = con.prepareStatement(sql);
) {
st.setObject(1, col);
ResultSet res = st.executeQuery();
if(res.next()){
codi = res.getInt("CODICOL");
}
}
catch(SQLException s){
JOptionPane.showMessageDialog(null, "Error:\n"+s.getMessage(),
"ERROR.",JOptionPane.ERROR_MESSAGE);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
I find the way out. I googled the number the java error gave to me and I found that adding a registry key to be able to do more connections should work.
I first did it on the server, to allow more connections from clients but it didn't still work so I tried to do it on my computer and for now it works.
This is the link from microsoft
In your code you giving a new connection on every call but you need to understand your OS allows you Only Limited Connection.
after cross Limit it will Not allow you to make another Connection.
i don't know what's your requirement but if you really need Connection
So better option is that you need to make Connection Pool. If user required a connection to do some task then user can Take Connection from connection Pool and after Work Finished user can return that connection to connection pool.
For Achieve this you need to Design Your Configuration File Or you need Design interface library interface and implement according to your Requirement.

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) {
}
}

delete sql not deleting

I'm trying to delete an event from my table. However I can't seem to get it to work.
My SQL statement is:
public void deleteEvent(String eventName){
String query = "DELETE FROM `Event` WHERE `eventName` ='"+eventName+"' LIMIT 1";
db.update(query);
System.out.println (query);
}
Using MySQL db
Try using the following :
String query = "DELETE FROM `Event` WHERE `eventName` ='"+eventName+"' LIMIT 1";
try {
Connection con = getConnection();
Statement s = con.createStatement();
s.execute(query);
} catch (Exception ex) {
ex.printStackTrace();
}
You have to code your getConnection() method to return a valid Database Connection.
I would suggest using Statement.executeUpdate method, since it returns an integer. So after performing this delete query you will also have information if you really deleted any records (in this case you would expect this method to return 1, since you are using LIMIT=1). I would also suggest closing Statement as soon as you don't need it, here is skeleton implementation:
private void performDelete(Connection conn, String deleteQuery, int expectedResult) throws SQLException {
Statement stmt = conn.createStatement();
int result = -1;
try {
result = stmt.executeUpdate(deleteQuery);
if(result != expectedResult) {
//Here you can check the result. Perhaps you don't need this part
throw new IllegalStateException("Develete query did not return expected value");
}
} catch(SQLException e) {
//Good practice if you use loggers - log it here and rethrow upper.
//Or perhaps you don't need to bother in upper layer if the operation
//was successful or not - in such case, just log it and thats it.
throw e;
} finally {
//This should be always used in conjunction with ReultSets.
//It is not 100% necessary here, but it will not hurt
stmt.close();
}
}

How can I avoid ResultSet is closed exception in Java?

As soon as my code gets to my while(rs.next()) loop it produces the ResultSet is closed exception. What causes this exception and how can I correct for it?
EDIT: I notice in my code that I am nesting while(rs.next()) loop with another (rs2.next()), both result sets coming from the same DB, is this an issue?
Sounds like you executed another statement in the same connection before traversing the result set from the first statement. If you're nesting the processing of two result sets from the same database, you're doing something wrong. The combination of those sets should be done on the database side.
This could be caused by a number of reasons, including the driver you are using.
a) Some drivers do not allow nested statements. Depending if your driver supports JDBC 3.0 you should check the third parameter when creating the Statement object. For instance, I had the same problem with the JayBird driver to Firebird, but the code worked fine with the postgres driver. Then I added the third parameter to the createStatement method call and set it to ResultSet.HOLD_CURSORS_OVER_COMMIT, and the code started working fine for Firebird too.
static void testNestedRS() throws SQLException {
Connection con =null;
try {
// GET A CONNECTION
con = ConexionDesdeArchivo.obtenerConexion("examen-dest");
String sql1 = "select * from reportes_clasificacion";
Statement st1 = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY,
ResultSet.HOLD_CURSORS_OVER_COMMIT);
ResultSet rs1 = null;
try {
// EXECUTE THE FIRST QRY
rs1 = st1.executeQuery(sql1);
while (rs1.next()) {
// THIS LINE WILL BE PRINTED JUST ONCE ON
// SOME DRIVERS UNLESS YOU CREATE THE STATEMENT
// WITH 3 PARAMETERS USING
// ResultSet.HOLD_CURSORS_OVER_COMMIT
System.out.println("ST1 Row #: " + rs1.getRow());
String sql2 = "select * from reportes";
Statement st2 = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
// EXECUTE THE SECOND QRY. THIS CLOSES THE FIRST
// ResultSet ON SOME DRIVERS WITHOUT USING
// ResultSet.HOLD_CURSORS_OVER_COMMIT
st2.executeQuery(sql2);
st2.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs1.close();
st1.close();
}
} catch (SQLException e) {
} finally {
con.close();
}
}
b) There could be a bug in your code. Remember that you cannot reuse the Statement object, once you re-execute a query on the same statement object, all the opened resultsets associated with the statement are closed. Make sure you are not closing the statement.
Also, you can only have one result set open from each statement. So if you are iterating through two result sets at the same time, make sure they are executed on different statements. Opening a second result set on one statement will implicitly close the first.
http://java.sun.com/javase/6/docs/api/java/sql/Statement.html
The exception states that your result is closed. You should examine your code and look for all location where you issue a ResultSet.close() call. Also look for Statement.close() and Connection.close(). For sure, one of them gets called before rs.next() is called.
You may have closed either the Connection or Statement that made the ResultSet, which would lead to the ResultSet being closed as well.
Proper jdbc call should look something like:
try {
Connection conn;
Statement stmt;
ResultSet rs;
try {
conn = DriverManager.getConnection(myUrl,"","");
stmt = conn.createStatement();
rs = stmt.executeQuery(myQuery);
while ( rs.next() ) {
// process results
}
} catch (SqlException e) {
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
} finally {
// you should release your resources here
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
} catch (SqlException e) {
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
}
you can close connection (or statement) only after you get result from result set. Safest way is to do it in finally block. However close() could also throe SqlException, hence the other try-catch block.
I got same error everything was correct only i was using same statement interface object to execute and update the database.
After separating i.e. using different objects of statement interface for updating and executing query i resolved this error. i.e. do get rid from this do not use same statement object for both updating and executing the query.
Check whether you have declared the method where this code is executing as static. If it is static there may be some other thread resetting the ResultSet.
make sure you have closed all your statments and resultsets before running rs.next. Finaly guarantees this
public boolean flowExists( Integer idStatusPrevious, Integer idStatus, Connection connection ) {
LogUtil.logRequestMethod();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = connection.prepareStatement( Constants.SCRIPT_SELECT_FIND_FLOW_STATUS_BY_STATUS );
ps.setInt( 1, idStatusPrevious );
ps.setInt( 2, idStatus );
rs = ps.executeQuery();
Long count = 0L;
if ( rs != null ) {
while ( rs.next() ) {
count = rs.getLong( 1 );
break;
}
}
LogUtil.logSuccessMethod();
return count > 0L;
} catch ( Exception e ) {
String errorMsg = String
.format( Constants.ERROR_FINALIZED_METHOD, ( e.getMessage() != null ? e.getMessage() : "" ) );
LogUtil.logError( errorMsg, e );
throw new FatalException( errorMsg );
} finally {
rs.close();
ps.close();
}
A ResultSetClosedException could be thrown for two reasons.
1.) You have opened another connection to the database without closing all other connections.
2.) Your ResultSet may be returning no values. So when you try to access data from the ResultSet java will throw a ResultSetClosedException.
It happens also when using a ResultSet without being in a #Transactional method.
ScrollableResults results = getScrollableResults("select e from MyEntity e");
while (results.next()) {
...
}
results.close();
if MyEntity has eager relationships with other entities. the second time results.next() is invoked the ResultSet is closed exception is raised.
so if you use ScrollableResults on entities with eager relationships make sure your method is run transactionally.
"result set is closed" happened to me when using tag <collection> in MyBatis nested (one-to-many) xml <select> statement
A Spring solution could be to have a (Java) Spring #Service layer, where class/methods calling MyBatis select-collection statements are annotated with
#Transactional(propagation = Propagation.REQUIRED)
annotations being:
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
this solution does not require to set the following datasource properties (i.e., in JBoss EAP standalone*.xml):
<xa-datasource-property name="downgradeHoldCursorsUnderXa">**true**\</xa-datasource-property>
<xa-datasource-property name="resultSetHoldability">**1**</xa-datasource-property>

JDBC connection with auto reconnect

I am using JDBC to connect to a database server.
The connection is over a wireless network and can be dodgy at times.
At the moment when the connection is lost I need to close and restart the application.
Does anyone have some examples of code where I could write some sort of wrapper to automatically reconnect and rerun the last query? This would save a lot of hassles.
I am just not sure how it should/could be implemented.
Maybe there is already something available?
Even if you use JDBC connection pool either application server provided or apache commons pooling, it is worthwhile to code a retry logic. Based on the configuration of your application server, the app server would purge all the pooled connections and recreate a fresh set of connections. Here is a sample:
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//
// How many times do you want to retry the transaction
// (or at least _getting_ a connection)?
//
int retryCount = 5;
boolean transactionCompleted = false;
do {
try {
conn = getConnection(); // assume getting this from a
// javax.sql.DataSource, or the
// java.sql.DriverManager
retryCount = 0;
stmt = conn.createStatement();
String query = "Some sample SQL";
rs = stmt.executeQuery(query);
while (rs.next()) {
}
rs.close();
rs = null;
stmt.close();
stmt = null;
conn.close();
conn = null;
transactionCompleted = true;
} catch (SQLException sqlEx) {
//
// The two SQL states that are 'retry-able'
// for a communications error.
//
// Only retry if the error was due to a stale connection,
// communications problem
//
String sqlState = sqlEx.getSQLState();
if ("Substitute with Your DB documented sqlstate number for stale connection".equals(sqlState) ) {
retryCount--;
} else {
retryCount = 0;
}
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException sqlEx) {
// log this
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlEx) {
// log this
}
}
if (conn != null) {
try {
//
// If we got here, and conn is not null, the
// transaction should be rolled back, as not
// all work has been done
try {
conn.rollback();
} finally {
conn.close();
}
} catch (SQLException sqlEx) {
//
// If we got an exception here, something
// pretty serious is going on, so we better
// pass it up the stack, rather than just
// logging it. . .
throw sqlEx;
}
}
}
} while (!transactionCompleted && (retryCount > 0));
Let a connection pool handle this for you, many of them can validate a connection. So does DBCP which has a testOnBorrow parameter that forces a sanity check on every connection before it's used. The default value of this parameter is true, it just needs validationQuery to be set to a non-null string to have any effect. So set the validationQuery and there you go! Check out the documentation.
Check out Oracle's Universal Connection Pool (UCP) libraries. They are fully JDBC 4.0 compliant and implement the isValid() call to check if a connection is live. It's easy to do this check, if false reconnect, then run your query.
Oracle UCP Download Page
While I know you didn't ask about connection pools, you should probably be using one anyway so this will help you twofold.

Categories