Getting JdbcBatchUpdateException while testing with EmbeddedDatabase H2 - java

I get strange exception trying to test my code using EmbeddedDatabase H2:
java.lang.RuntimeException:
java.lang.RuntimeException: org.h2.jdbc.JdbcBatchUpdateException: Timeout trying to lock table "CO_SCENARIO_1"; SQL statement:
UPDATE CO_SCENARIO_1 SET VALUE = ? WHERE ATTRIBUTE = ? AND MODIFIER = ? [50200-156]
at de.telekom.skses.test.dao.DatabaseFileConverterTest.fromFile(DatabaseFileConverterTest.java:99)
This is part of code I'm trying to test:
case "overriddenVariables.txt": {
try (Scanner sc = new Scanner(bOut.toString())) {
try (Connection con = dataSource.getConnection()) {
String aQuery = "UPDATE $tableName SET VALUE = ? WHERE ATTRIBUTE = ? AND MODIFIER = ?";
String line = "";
if (sc.hasNext()) {
line = sc.nextLine();
}
while (sc.hasNext()) {
if (line.startsWith("+")) {
String setting = line.substring(1);
try (PreparedStatement ps = con.prepareStatement(aQuery.replace("$tableName", setting))) {
while (sc.hasNext() && !(line = sc.nextLine()).startsWith("+")) {
int del1 = line.indexOf(":");
int del2 = line.indexOf(':', del1 + 1);
String attr = line.substring(0, del1);
String modifier = line.substring(del1 + 1, del2);
String value = line.substring(del2 + 1, line.length());
Clob myClob = con.createClob();
myClob.setString(1, value);
ps.setClob(1, myClob);
ps.setString(2, attr);
ps.setString(3, modifier);
ps.addBatch();
}
ps.executeBatch();
}
}
}
} catch (SQLException e) {
logger.error("Error", e);
throw new RuntimeException(e);
}
} catch (Exception ex) {
logger.error("Error", ex);
throw new RuntimeException(ex);
}
break;
}
StackOverflow says that I should set lock timeout, but I didn't find how to set it for EmbeddedDatabase. Also, in previous version I opened new PreparedStatement for each query and new Connection for each PreparedStatement, everything worked well, and I can't understand why it doesn't now. Could you please explain what I have to do to make it work again?
Sorry if something is incorrect, I'm new to Java EE.

As I remember using H2 in embedded mode, it has problems with concurrency. this is probably why you get the timeout for the lock on the table, since the table is still updated from your last batch when you try to execute the next one. When closing connection and preparedStatement every single time the execution time is way shorter, so you won't get the timeout.
Have you tried adding to the DatabaseURL ;MVCC=true as stated in this post? If this won't solve the problem, is it really necessary to use batches for your update?
EDIT: the post i linked before, links to the h2 website where you can find a description on how to change the lock timeout by adding ;LOCK_TIMEOUT=10000 to your databse URL. This would change it to 10 seconds, standard is 1 second

Related

different results if PreparedStatement is used as resource in try block

Some behaviour I don't understand.
I have a running script like:
ResultSet res = null;
String cmd = new String("SELECT value FROM " +pDS.getValueTableName() + " WHERE itemID=? and propertyID=? ORDER BY checkpointID DESC");
PreparedStatement pstmt= dbconn.prepareStatement(cmd) ;
pstmt.setLong(1,itemID);
pstmt.setLong(2,pDS.getPropertyID());
try {
res = pstmt.executeQuery();
}
catch(Exception e)
{
throw(e);
}
if (!res.next())
{
// other code
}
Here i get the expected values res.next()=true.
No exception is thrown.
I wanted to refactor the code, and use the Autoclose funtionalty of the try block, like:
ResultSet res = null;
String cmd = new String("SELECT value FROM " +pDS.getValueTableName() + " WHERE itemID=? and propertyID=? ORDER BY checkpointID DESC");
try (PreparedStatement pstmt= dbconn.prepareStatement(cmd) ){
pstmt.setLong(1,itemID);
pstmt.setLong(2,pDS.getPropertyID());
res = pstmt.executeQuery();
}
catch(Exception e)
{
LOGGER.error("Error at getLatestPropertyResultSet",e);
throw(e);
}
if (!res.next())
{
// other code
}
However now res.next()=false. The resultset itselve is intialized res!=null.
Why did this modification change the behaviour of the script?
Without seeing where you call res.next() I can only assume that the try-with-resource is doing exactly as advertised and closing the prepared statement once you leave the try block and thus the result is "closed" with it.
update: based on your edit my suspicions are confirmed. You need to move any work related to the resource inside the try block.

Update to MySQL returns 1, no errors/exception, but no updates actually take place

The below method is supposed to update MySql DB with the company info passed to it.
I have other methods that insert and delete and work fine, however this method runs without exceptions, and always returns 1.
The general_log file shows that it received the update string but there are still no changes.
The only time I can get it to work is if I run the code in the MySql workplace directly.
If you need more info to figure this out, please let me know.
I gave you all I thought was needed.
Thanks.
// SQL update string received from the program in the log file
// UPDATE couponsprojectdb.company SET Email = 'admin#MyCompany.org', Password = 'pass' WHERE ID = 3
public void updateCompanyById(Company c, long id) throws SQLException
{
Connection conn = pool.getConnection(); // Gets an available connection from pool
// Prepared statement string
String sql = ("UPDATE company SET Email = ?, Password = ? WHERE ID = ?");
PreparedStatement p = conn.prepareStatement(sql);
p.setString(1, c.getEmail());
p.setString(2, c.getPassword());
p.setLong(3, id);
int i = p.executeUpdate();
System.out.println("changes: " + i);
pool.releaseConnection(conn);
}
You never called conn.commit() after doing the executeUpdate(). The reason the Java code returns 1 is because it succeeded, but the database rolled back the UPDATE immediately after the transaction ended.
You also need to close your connections. Change your code to this:
try {
Connection conn = pool.getConnection();
String sql = ("UPDATE company SET Email = ?, Password = ? WHERE ID = ?");
PreparedStatement p = conn.prepareStatement(sql);
p.setString(1, c.getEmail());
p.setString(2, c.getPassword());
p.setLong(3, id);
int i = p.executeUpdate();
conn.commit(); // <-- MAKE SURE TO COMMIT THE TRANSACTION TO THE DATABASE!!!
System.out.println("changes: " + i);
pool.releaseConnection(conn);
} catch(Exception e) {
// handle errors here
} finally {
try { if (p != null) p.close(); } catch (Exception e) {};
try { if (conn != null) pool.releaseConnection(conn); } catch (Exception e) {};
}
Big hat tip to this SO post which got me thinking about your problem.

Execute a SQL query in a loop

I have this scenario. I will trigger a job in the server and as soon as the job is triggered an entry will be made into the job table with Execution_status_code as 1. I need to wait for some time say 5 mins and recheck the Execution_status_code value. As soon as the value is changed to 2, I need to proceed further.
I am using an existing connection for connecting to the database. I need to execute the SQL and if the SQL output is In progress, I need to wait for some time and then again execute the statement. Do this until the SQL output is success, until then keep waiting.
Below is the code I have tried.
Thread t = new Thread();
java.sql.Connection conn_javaComp = (java.sql.Connection)globalMap.get("conn_tNetezzaConnection_1");
java.sql.Statement st = null;
java.sql.ResultSet rs = null;
String check = null;
String dbquery_javaComp = "select case when EXECUTION_STATUS_CODE = 2 then 'Success' when EXECUTION_STATUS_CODE = 1 then 'In progress' else 'Failure' end as EXECUTION_STATUS_CODE from JOB_BKUP_NCR where JOB_TYPE_CODE="+context.JobTypeCode+" and Load_id = (select max(load_id) from JOB_BKUP_NCR where job_type_code="+context.JobTypeCode+") and START_DATETIME = (select max(START_DATETIME) from JOB_BKUP_NCR where job_type_Code="+context.JobTypeCode+")";
try
{
do
{
st = conn_javaComp.createStatement();
rs = st.executeQuery(dbquery_javaComp);
if(rs.next())
{
check = rs.getString(1);
System.out.println(check);
if (check.equalsIgnoreCase("In Progress"))
{
t.sleep(1000);
System.out.println("thread executed1");
System.out.println(dbquery_javaComp);
System.out.println(check);
}
}
else {
System.out.println(" No data found");
}
}while (!"Success".equals(check));
}
catch (Exception e) {
e.printStackTrace();
} finally {
try {
if( rs != null)
rs.close();
if( st!= null)
st.close();
}
catch (Exception e1) {
e1.printStackTrace();
}
}
The output i am getting is 'In Progress'. The loop is struck at In progress even after i change the value in the database. I am not sure where i am doing wrong. Any suggestions?
You are creating a new statement and a new resultset inside the loop, and so, they should be close inside the loop. I am thinking that your connection got corrupted with multiple statements and resultset without closing them. Please try to close them and see if that work.
The data that you are seeing will be cached.
Try closing and re-opening your DB connection. This may not even be good enough if you are using DB pooling.
There are many things I can foresee going wrong with your code. For once most DBMS will either lock the rows until you commit / close the connection or give you a snapshot of the data instead, hence you don't see the updated value or the transaction that supposed to update it wouldn't go through. Try comitting or close/reopen the transaction per loop iteration.
I would also doubt if this is a good code design as you are doing "polling". Consider if you can find other method of getting notified of the event.
try
{
//declare here your statement and resultset
st = conn_javaComp.createStatement();
rs = st.executeQuery(dbquery_javaComp);
do
{
if(rs.next())
{
check = rs.getString(1);
System.out.println(check);
if (check.equalsIgnoreCase("In Progress"))
{
t.sleep(1000);
System.out.println("thread executed1");
System.out.println(dbquery_javaComp);
System.out.println(check);
}
}
else {
System.out.println(" No data found");
}
}while (!"Success".equals(check));

Java MySQL get last row

So I'm having issues with my program. Basically this far the program is like a MySQL based chat. It stores messages in database and reads them. I'm having problems with the reading. What it does right now is ever 5 seconds re-read all the messages in the database. I tried to make it read only the new messages but that's not working out too well. This is my code:
public static void readChat()
{
try
{
MySQL.sqlConnect();
try
{
Statement st = con.createStatement();
ResultSet res = st.executeQuery("SELECT * FROM table1");
while (res.next())
{
if (lastLine < res.getInt("id"))
{
String message = res.getString("message");
Gui.out.append(message + "\n");
lastLine = res.getInt("id");
}
}
}
catch (SQLException s)
{
System.out.println("SQL code does not execute.");
}
MySQL.sqlDisconnect();
}
catch (Exception e)
{
e.printStackTrace();
}
}
I'm not sure how to make this efficient. It takes way too long to execute that. If the id is 23 I can't even see the messages appear because it takes longer than 5 seconds. I added the
if (lastLine < res.getInt("id"))
and
lastLine = res.getInt("id");
in my effort to make it read only the new messages but it did not work as expected. I think it still executed line by line, just doesn't show it in the chat. There's got to be an easier way.
EDIT: Alright, so I fixed the problem with not seeing messages, (I forgot to remove the part of the code that cleared the chat every 5 seconds). But it still takes a long time to send messages, about 3-4 seconds?
Try this, and you will wonder :)
ResultSet res = st.executeQuery("SELECT * FROM table1 where id > "+lastLine);
instad
ResultSet res = st.executeQuery("SELECT * FROM table1");
and get result without if
while (res.next())
{
String message = res.getString("message");
Gui.out.append(message + "\n");
lastLine = res.getInt("id");
}
Of course make sure index on id field
you can just read the required rows, currently you are reading all the rows try following,
public static void readChat()
{
try
{
MySQL.sqlConnect();
try
{
Statement st = con.Preparestatment("SELECT * FROM table1 where id > ?");
st.setInt(0,lastLine);
ResultSet res = st.executeQuery();
while (res.next())
{
String message = res.getString("message");
Gui.out.append(message + "\n");
lastLine = res.getInt("id");
}
}
catch (SQLException s)
{
System.out.println("SQL code does not execute.");
}
MySQL.sqlDisconnect();
}
catch (Exception e)
{
e.printStackTrace();
}
}
You still iterate through all results. If you have some way of storing the time at which messages are sent i.e creating a time column, you can try to shorten how many results are returned by doing
ResultSet res = st.executeQuery("SELECT * FROM table1 WHERE id = ? AND time > ?");
where the first ? is the id of the user and the 2nd ? is the time of the last read message. This is based off the assumption id is some unique user id.
What about maintaining a list, add new message to that list when you save it to the database and just read the new messages from that list and clear it at the end of readChat()?

exhausted resultset error when i get the name of the columns

i'm trying to get the type and the name of the result and when enter in the loop, excuting somo instructions about the metadata the resulset.next changed from true to false, and give the error java.sql.SqlExcepcion exhausted resultset. Any ideas? i really dont know how solved it because i read the post with the solution of this problem and validate if the resultset it's null before begin the loop. I'm called this method with a scheduler of quartz. I'm using this in a j2ee aplication and the example it's this
try
{
InitialContext ctx = new InitialContext();
WrapperDataSource wrapperDataSource = (WrapperDataSource)ctx.lookup(systemLogger.getConfigurationParameters().getDataSource());
conn = wrapperDataSource.getConnection();
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver").newInstance();
conn = DriverManager.getConnection(url,login,password);
if (conn != null)
{
stmt = conn.createStatement();
res = stmt.executeQuery(query);
if (res != null)
{
while (res.next())
{
for (int i = 0; i < columnlength; i++)
{
String columnName = metadata.getColumnName(i+1);
if (metadata.getColumnName(i+1).equalsIgnoreCase(systemLogger.getColumnStatus()))
{
columnStatusType = metadata.getColumnType(i+1);
}
else if (metadata.getColumnName(i+1).equalsIgnoreCase(systemLogger.getColumnDocumentId()))
{
columnDocumentIdType = metadata.getColumnType(i+1);
}
else if (metadata.getColumnName(i+1).equalsIgnoreCase(systemLogger.getColumnTimer()))
{
columnTimerType = metadata.getColumnType(i+1);
}
}
}
}
else
{
__log.error("No results found for the query");
throw new PtmServiceException("No se encontraron resultados para el query");
}
}
else
{
__log.error("Could not create the connection");
throw new PtmServiceException("No se pudo crear la conexion");
}
}
catch(Exception e)
{
__log.error("Error in the execution of the query");
throw new PtmServiceException("Error ejecutando la busqueda");
}
finally
{
res.close();
stmt.close();
conn.close();
}
The variable columnlength seems to hold a value larger than the number of columns returned by the query. Try with a smaller columnlength.
finally, i see the problem, while i'm debugging the code with ecplise in the view of the expressions i added the follow expression res.next(), then each sentence that i pass for the step into bring the consequence that expression that evaluate if the resultset has more rows, be evaluated again. In some point the resultset has evaluated all the rows for each step into that i made in the process of debugging. The only thing that i have to do was eliminate the expression and works fine...
The problem might not be with the code but instead could be the database. Double check that the TABLE IS NOT EMPTY. You get this error if the table is empty. Keep in mind that databases like Oracle require a commit after all your insert, update, alter statements .Your changes might not be visible outside the database till you run a commit over the your db, I was having this problem for quite a long time. I kept on checking the table with select statement but the problem with my oracle db was that I had not issued a commit over my db.

Categories

Resources