PostgreSQL Transaction fails: "there is no transaction in progress" - java

I've got a pretty long transaction I'm trying to execute using JDBC for PostgreSQL. In JDBC I can't use COMMIT and ROLLBACK, so I'm trying to implement my desired behaviour in Java code...
try {
con = DriverManager.getConnection(url, user, password);
con.setAutoCommit(false);
Statement st = con.createStatement();
st.execute(myHugeTransaction);
con.commit();
} catch (SQLException ex) {
try {
con.rollback();
} catch (SQLException ex1) {
// log...
}
// log...
}
For small statements, this works pretty well, but for the large ones with about 10K statements in a single transaction, this fails in the con.commit line with
org.postgresql.util.PSQLException: ERROR: kind mismatch among backends. Possible last query was: "COMMIT" kind details are: 0[C] 1[N: there is no transaction in progress]
The funny thing is, if I catch SQL Warnings with st.getWarnings(); I can see that the database is actually processing the whole script I've sent, just when it comes to the commit, it all fails.
btw, the transaction is totally fine. I write an exact copy of it into a file and I can run it without errors by copying it into pgAdmin. Hope you can help me on that one, I've been searching and testing stuff for hours now...
edit
Maybe I didn't get this right, so two questions:
Can I execute multiple statements in one call to Statement.execute()?
If not, what is the right way to run a Script with multiple statements using JDBC (without the need to parse and split it into single statements)?

Honestly, if this is a SQL script you are better off doing a shell escape to psql. That is the best way to handle this. In general I have had way too many unpleasant surprises from people trying to parse out SQL code and run it against the db. This way madness lies.
you say "smaller scripts" which leads me to conclude you are doing something like setting up a database (or upgrading one, but that's less likely since there are no queries). Use psql through a shell escape and don't look back. That really is the best way.
I suppose if you have to you could try adding explicit BEGIN and COMMIT to your script.
I am not sure why it seems to be committing the transaction implicitly. you have set autocommit off properly. There are no obvious problems in your code. Is it possible you have an old or buggy JDBC driver? If not, I would recommend filing a bug report with the PostgreSQL JDBC driver project.

Related

Best Practice for AutoCommit statements in Java for MySQL usage via JDBC

Within my Java code I interact with an MySQL database via a JDBC connection. I setup a connection and some statements (for reuse)
connect = DriverManager.getConnection(connectionstring);
statement = connect.createStatement();
statement2 = connect.createStatement();
For most of the code, statement.executeQuery(query) is fine, but some operations are "blocks", so I need to make sure that they either all go through together, or none of them do. I believe the following code achieves this.
connect.setAutoCommit(false);
statement.addBatch(insertquery1);
statement.addBatch(insertquery2);
statement.addBatch(insertquery3);
statement.addBatch(insertquery4);
statement.executeBatch();
connect.commit();
connect.setAutoCommit(true);
As far as I can tell, the connect calls affect the entire connection, so would impact both statement and statement2. Also, I can seemingly set the AutoCommit values after I created the statement object and it still applies to it. Is this correct? And would it be better practice/more efficient to split them and keep a different connection object just for autocommit calls like below?
// objects purely for batch operations
connect = DriverManager.getConnection(connectionstring);
connect.setAutoCommit(false);
statement = connect.createStatement();
// objects for normal operations
connect2 = DriverManager.getConnection(connectionstring);
connect2.setAutoCommit(true);
statement2 = connect2.createStatement();
In my application they represent less than 1% of calls, but that percentage does need to be done in batches. What is best practice?
The whole point of setting auto commit to false is so that multiple SQL statements can all run inside the same transaction. And, should anything go wrong with any of your 4 insert statements, the whole transaction would logically be rolled back, so that you don't end up with inconsistent data in your database. The pattern you are already following is best practice:
connect.setAutoCommit(false);
statement.addBatch(insertquery1);
statement.addBatch(insertquery2);
statement.addBatch(insertquery3);
statement.addBatch(insertquery4);
statement.executeBatch();
connect.commit();
connect.setAutoCommit(true);
That is, it is best practice to return auto commit to being true after you have completed your transaction. This is so that any other part of your code, or perhaps your future code, should someone else inherit it, which still uses the same connection object would receive the default behavior of auto commit being true.

Preventing SQL Injection in JDBC without using Prepared Statements

I am aware that using Prepared Statements is the best way to protect against SQL Injection (and syntax errors due to unescaped characters in unchecked input).
My current situation is that I am writing some Java code to move data from one third party application to another. The destination application uses a proprietary version of Sybase and so whilst I do have the JTDS JDBC driver PreparedStatement fails, as the driver uses temporary stored procedures which aren't supported in this particular flavour of the database. So I only have Statement to work with and I have no control over the user input as it is coming from another application.
There is this similar question but that is focused on fixing the problem where you have a parameter such as a table which cannot be handled via a Prepared Statement. My case is different and hopefully simpler, since I have straightforward SQL statements. I would like to know if there is a best practice for replicating something like the following without using PreparedStatement:
PreparedStatement statement = connection.prepareStatement("UPDATE mytable SET value=? WHERE id=?");
statement.setInt(1, getID());
statement.setString(2,userInput);
statement.executeUpdate();
So I guess the problem is how can I sanitise the user input reliably? I can try to do that myself from scratch but this seems like a bad idea as there is likely to be at least one edge case I'd miss, so I was hoping there was a library out there that would do that for me, but I haven't been able to find one so far.
The ESAPI library has procedures for escaping input for SQL and for developing your own db specific encoders if necessary.
Check out JTDS FAQ - I'm pretty confident that with a combination of properties prepareSQL and maxStatements you could go there (or "could have gone" as you probably completed that task years ago :-) )

Establishing persistent connection to a database in Java

I've ran through several examples over the web, and found that every single time I need something from the DB, I should write the following code:
try
{
// Step 1: Load the JDBC driver.
Class.forName("mysql_driver_name");
// Step 2: Establish the connection to the database.
String url = "jdbc:string_to_mysql_server";
Connection conn = DriverManager.getConnection(url,"user1","password");
// fetch from the DB ...
}
catch (Exception e)
{
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
}
It's very annoying to put up this code every time I want something from the DB, so the question is - is there a way to only once connect entirely all my app to the DB somehow at the very start point, avoiding copy-pasting mentioned code, and then be able to do everything I want with DB?
I've quickly looked through NetBeans's Project menu, but didn't find any clue on how to configurate a persistent connection to a selected DB.
If it's important, i'm writing a purely desktop app, i.e. using Java SE. Also, it's worth mentioning that I'm a kinda beginner in Java.
There are many connection pool options to choose from, I would suggest that you try Apache Common Db Connection Pool http://commons.apache.org/dbcp/.
The connection pool idea is probably the best overal solution. However there is a simpler one in ypur case.
In your code conn goes out of scope in the method itwas created. There is no need to do that. You can create a method that includes all your code up to an including the line that assigns to conn. Then pass that conn variable to other parts of the program and use that for db work.
You can follow this Method of Establishing the Conenction
Create a Singleton class, which helps you to create a connection
In your DAO or any helper Class, Call this Single instance, which has a connection
Once you get the Conenction, write the operations that you want to perform on the database.
Close the connection, that will do to fullfill your connection.
This will avoid the code, what you have written in your query.
and this style will increases the readability and reduce the maintainability.
If you want any sample code let me know, I can provide you that

PL/SQL exception and Java programs

Business logic is coded in pl/sql packages procedures and functions.
Java programs call pl/sql packages procedures and functions to do database work.
pl/sql programs store exceptions into Oracle tables whenever an exception is raised.
How would my java programs get the exceptions since the exception instead of being propagated from pl/sql to java is getting persisted to a oracle table and the procs/functions just return 1 or 0.
Sorry folks i should have added this constraint much earlier and avoided this confusion. As with many legacy projects we don't have the freedom to modify the stored procedures.
Assuming you can't change the PLSQL code, you'll have to monitor the table. And of course, that will only work if the error table stores some sort of session or use identifier.
java.sql.CallableStatement throws java.sql.SQLException. If your stored proc throws an exception, your Java code will know about it. The code that calls the stored proc will have to handle this exception.
You have a choice: you can either have the stored proc throw the exception or have the Java code check the return value and query the table for the exception if the error code is returned.
But the exception isn't "lost" either way. You get it from the JVM or the table.
I vote for the JVM because it's easier. Less PL/SQL code, less Java code.
"is getting persisted to a oracle table"
You could create a trigger on that table that rejects the insert. For example, if the error table contains an 'ERROR_DESCRIPTION' column, you could have a BEFORE INSERT ON error_table FOR EACH ROW trigger which does a RAISE_APPLICATION_ERROR(-20001,:NEW.ERROR_DESCRIPTION)
When the PL/SQL code goes to log the error, that will fail with the replacement error and that will, if you are lucky, get propogated to the Java layer.
It is an ugly hack, but if you truly can't change the code, it may work.
Simply if you use a framework that supports aspects, it would be easy to make an aspect that checks for the exception in the appropriate table. If not, then you could write something similar to this code:
ResultSet exRs = null;
try {
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
statement.execute(normalSql);
exRs = statement.executeQuery(exceptionSql);
exRs.next();
} catch (SQLException e) {
e.printStackTrace();
connection.rollback();
}
if (null != exRs.getString(exceptionColumn))
connection.commit();
else
connection.rollback();
Sorry I couldn't be more specific.

sql server query running slow from java

I have a java program that runs a bunch of queries against an sql server database. The first of these, which queries against a view returns about 750k records. I can run the query via sql server management studio, and I get results in about 30 seconds. however, I kicked off the program to run last night. when I checked on it this morning, this query still had not returned results back to the java program, some 15 hours later.
I have access to the database to do just about anything I want, but I'm really not sure how to begin debugging this. What should one do to figure out what is causing a situation like this? I'm not a dba, and am not intimately familiar with the sql server tool set, so the more detail you can give me on how to do what you might suggest would be appreciated.
heres the code
stmt = connection.createStatement();
clientFeedRS = stmt.executeQuery(StringBuffer.toString());
EDIT1:
Well it's been a while, and this got sidetracked, but this issue is back. I looked into upgrading from jdbc driver v 1.2 to 2.0, but we are stuck on jdk 1.4, and v 2.0 require jdk 1.5 so that's a non starter. Now I'm looking at my connection string properties. I see 2 that might be useful.
SelectMethod=cursor|direct
responseBuffering=adaptive|full
Currently, with the latency issue, I am running with cursor as the selectMethod, and with the default for responseBuffering which is full. Is changing these properties likely to help? if so, what would be the ideal settings? I'm thinking, based on what I can find online, that using a direct select method and adaptive response buffering might solve my issue. any thoughts?
EDIT2:
WEll I ended changing both of these connection string params, using the default select method(direct) and specifying the responseBuffering as adaptive. This ends up working best for me and alleviates the latency issues I was seeing. thanks for all the help.
I had similar problem, with a very simple request (SELECT . FROM . WHERE = .) taking up to 10 seconds to return a single row when using a jdbc connection in Java, while taking only 0.01s in sqlshell. The problem was the same whether i was using the official MS SQL driver or the JTDS driver.
The solution was to setup this property in the jdbc url :
sendStringParametersAsUnicode=false
Full example if you are using MS SQL official driver : jdbc:sqlserver://yourserver;instanceName=yourInstance;databaseName=yourDBName;sendStringParametersAsUnicode=false;
Instructions if using different jdbc drivers and more detailled infos about the problem here : http://emransharif.blogspot.fr/2011/07/performance-issues-with-jdbc-drivers.html
SQL Server differentiates its data types that support Unicode from the ones that just support ASCII. For example, the character data types that support Unicode are nchar, nvarchar, longnvarchar where as their ASCII counter parts are char, varchar and longvarchar respectively. By default, all Microsoft’s JDBC drivers send the strings in Unicode format to the SQL Server, irrespective of whether the datatype of the corresponding column defined in the SQL Server supports Unicode or not. In the case where the data types of the columns support Unicode, everything is smooth. But, in cases where the data types of the columns do not support Unicode, serious performance issues arise especially during data fetches. SQL Server tries to convert non-unicode datatypes in the table to unicode datatypes before doing the comparison. Moreover, if an index exists on the non-unicode column, it will be ignored. This would ultimately lead to a whole table scan during data fetch, thereby slowing down the search queries drastically.
In my case, i had 30M+ records in the table i was searching from. The duration to complete the request went from more than 10 seconds, to approximatively 0.01s after applying the property.
Hope this will help someone !
It appears this may not have applied to your particular situation, but I wanted to provide another possible explanation for someone searching for this problem.
I just had a similar problem where a query executed directly in SQL Server took 1 minute while the same query took 5 minutes through a java prepared statemnent. I tracked it down to the fact that it is was done as a prepared statement.
When you execute a query directly in SQL Server, you are providing it a non-parameterized query, in which it knows all of the search criteria at optimization time. In my case, my search criteria included a date range, and SQL server was able to look at it, decide "that date range is huge, let's not use the date index" and then it chose something much better.
When I execute the same query through a java prepared statement, at the time that SQL Server is optimizing the query, you haven't yet provided it any of the parameter values, so it has to make a guess which index to use. In the case of my date range, if it optimizes for a small range and I give it a large range, it will perform slower than it could. Likewise if it optimizes for a large range and I give it a small one, it's again going to perform slower than it could.
To demonstrate this was indeed the problem, as an experiment I tried giving it hints as to what to optimize for using SQL Server's "OPTIMIZE FOR" option. When I told it to use a tiny date range, my java query (which actually had a wide date range) actually took twice as long as before (10 minutes, as opposed to 5 minutes before, and as opposed to 1 minute in SQL Server). When I told it my exact dates to optimize for, the execution time was identical between the java prepared statement.
So my solution was to hard code the exact dates into the query. This worked for me because this was just a one-off statement. The PreparedStatement was not intended to be reused, but merely to parameterize the values to avoid SQL injection. Since these dates were coming from a java.sql.Date object, I didn't have to worry about my date values containing injection code.
However, for a statement that DOES need to be reused, hard coding the dates wouldn't work. Perhaps a better option for that would be to create multiple prepared statements optimized for different date ranges (one for a day, one for a week, one for a month, one for a year, and one for a decade...or maybe you only need 2 or 3 options...I don't know) and then for each query, execute the one prepared statement whose time range best matches the range in the actual query.
Of course, this only works well if your date ranges are evenly distributed. If 80% of your records were in the last year, and 20% percent spread out over the previous 10 years, then doing the "multiple queries based on range size" thing might not be best. You'd have to optimize you queries based on specific ranges or something. You'd need to figure that out through trial an error.
Be sure that your JDBC driver is configured to use a direct connection and not a cusror based connection. You can post your JDBC connection URL if you are not sure.
Make sure you are using a forward-only, read-only result set (this is the default if you are not setting it).
And make sure you are using updated JDBC drivers.
If all of this is not working, then you should look at the sql profiler and try to capture the sql query as the jdbc driver executes the statement, and run that statement in the management studio and see if there is a difference.
Also, since you are pulling so much data, you should be try to be sure you aren't having any memory/garbage collection slowdowns on the JVM (although in this case that doesn't really explain the time discrepancy).
If the query is parametrized it can be a missing parameter or a parameter that is set with the wrong function, e.g. setLong for string, etc.
Try to run your query with all parameters hardcoded into the query body without any ? to see of this is a problem.
I know this is an old question but since it's one of the first results when searching for this issue I figured I should post what worked for me. I had a query that took less than 10 seconds when I used SQL Server JDBC driver but more than 4 minutes when using jTDS. I tried all suggestions mentioned here and none of it made any difference. The only thing that worked is adding this to the URL ";prepareSQL=1"
See Here for more
I know this is a very old question but since it's one of the first results when searching for this issue I thought that I should post what worked for me.
I had a query that took about 3 seconds when I used SQL Server Management Studio (SSMS) but took 3.5 minutes when running using jTDS JDBC driver via the executeQuery method.
None of the suggestion mentioned above worked for me mainly because I was using just Statement and not Prepared Statement. The only thing that worked for me was to specify the name of the initial or default database in the connection string, to which the connecting user has at least the db_datareader database role membership. Having only the public role is not sufficient.
Here’s the sample connection string:
jdbc:jtds:sqlserver://YourSqlServer.name:1433/DefaultDbName
Please ensure that you have the ending /DefaultDbName specified in the connection string. Here DefaultDbName is the name of the database to which the user ID specified for making the JDBC connection has at least the db_datareader database role. If omitted, SQL Server defaults to using the master database. If the user ID used to make the JDBC connection only has the public role in the master database, the query takes exceptionally long.
I don’t know why this happens. However, I know a different query plan is used in such circumstances. I confirmed this using the SQL Profiler tool.
Environment details:
SQL Server version: 2016
jTDS driver version: 1.3.1
Java version: 11
Pulling back that much data is going to require lots of time. You should probably figure out a way to not require that much data in your application at any given time. Page the data or use lazy loading for example. Without more details on what you're trying to accomplish, it's hard to say.
The fact that it is quick when run from management studio could be due to an incorrectly cached query plan and out of date indexes (say, due to a large import or deletions). Is it returning all 750K records quickly in SSMS?
Try rebuilding your indexes (or if that would take too long, update your statistics); and maybe flushing the procedure cache (use caution if this is a production system...): DBCC FREEPROCCACHE
To start debugging this, it would be good to determine whether the problem area is in the database or in the app. Have you tried changing the query such that it returns a much smaller result? If that doesnt return, I would suggest targeting the way you are accessing the DB from Java.
Try adjusting the fetch size of the Statement and try selectMethod of cursor
http://technet.microsoft.com/en-us/library/aa342344(SQL.90).aspx
We had issues with large result sets using mysql and needed to make it stream the result set as explained in the following link.
http://helpdesk.objects.com.au/java/avoiding-outofmemoryerror-with-mysql-jdbc-driver
Quote from the MS Adaptive buffer guidelines:
Avoid using the connection string property selectMethod=cursor to allow the application to process a very large result set. The adaptive buffering feature allows applications to process very large forward-only, read-only result sets without using a server cursor. Note that when you set selectMethod=cursor, all forward-only, read-only result sets produced by that connection are impacted. In other words, if your application routinely processes short result sets with a few rows, creating, reading, and closing a server cursor for each result set will use more resources on both client-side and server-side than is the case where the selectMethod is not set to cursor.
And
There are some cases where using selectMethod=cursor instead of responseBuffering=adaptive would be more beneficial, such as:
If your application processes a forward-only, read-only result set slowly, such as reading each row after some user input, using selectMethod=cursor instead of responseBuffering=adaptive might help reduce resource usage by SQL Server.
If your application processes two or more forward-only, read-only result sets at the same time on the same connection, using selectMethod=cursor instead of responseBuffering=adaptive might help reduce the memory required by the driver while processing these result sets.
In both cases, you need to consider the overhead of creating, reading, and closing the server cursors.
See more: http://technet.microsoft.com/en-us/library/bb879937.aspx
Sometimes it could be due to the way parameters are binding to the query object.
I found the following code is very slow when executing from java program.
Query query = em().createNativeQuery(queryString)
.setParameter("param", SomeEnum.DELETED.name())
Once I remove the "deleted" parameter and directly append that "DELETED" string to the query, it became super fast. It may be due to that SQL server is expecting to have all the parameters bound to decide the optimized plan.
Two connections instead of two Statements
I had one connection to SQL server and used it for running all queries I needed, creating a new Statement in each method that needed DB interaction.
My application was traversing a master table and, for each record, fetching all related information from other tables, so the first and largest query would be running from beginning to end of the execution while iterating its result set.
Connection conn;
conn = DriverManager.getConnection("jdbc:jtds:sqlserver://myhostname:1433/DB1", user, pasword);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("select * from MASTER + " ;");
// iterating rs will cause the other queries to complete Entities read from MASTER
// ...
Statement st1 = conn.createStatement();
ResultSet rs1 = st1.executeQuery("select * from TABLE1 where id=" + masterId + ";");
// st1.executeQuery() makes rs to be cached
// ...
Statement st2 = conn.createStatement();
ResultSet rs2 = st2.executeQuery("select * from TABLE2 where id=" + masterId + ";");
// ...
This meant that any subsequent queries (to read single records from the other tables) would cause the first result set to be cached entirely and not before that the other queries would run at all.
The solution was running all other queries in a second connection. This let the first query and its result set alone and undisturbed while the rest of the queries run swiftly in the other connection.
Connection conn;
conn = DriverManager.getConnection("jdbc:jtds:sqlserver://myhostname:1433/DB1", user, pasword);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("select * from MASTER + " ;");
// ...
Connection conn2 = DriverManager.getConnection("jdbc:jtds:sqlserver://myhostname:1433/DB1", user, pasword);
Statement st1 = conn2.createStatement();
ResultSet rs1 = st1.executeQuery("select * from TABLE1 where id=" + masterId + ";");
// ...
Statement st2 = conn2.createStatement();
ResultSet rs2 = st2.executeQuery("select * from TABLE2 where id=" + masterId + ";");
// ...
Does it take a similar amount of time with SQLWB? If the Java version is much slower, then I would check a couple of things:
You shoudl get the best performance with a forward-only, read-only ResultSet.
I recall that the older JDBC drivers from MSFT were slow. Make sure you are using the latest-n-greatest. I think there is a generic SQL Server one and one specifically for SQL 2005.

Categories

Resources