I would like to use prepared statements, for many reasons.
But, I would like to create a method that looks like this:
/* This opens a connection, executes the query, and closes the connection */
public static void executeNonQuery(String queryString);
In other words, I want my application logic to only have to formulate the queries and feed in parameters, but not deal with connections & statements. However, PreparedStatements are created from a connection object, so I am currently forced into preparing the query string using String.format() - butt ugly and dangerous.
Is there a way to do what I want without using String.format()?
Why do I need a connection to create PreparedStatements ?
Because the statements are prepared on per-connection basis in most RDBMS's.
Prepared statements are in fact cached execution plans that don't take you permissions, encodings, collation settings etc. into account.
All this is done during query parsing.
Is there a way to do what I want without using String.format()
Don't see why you need String.format() here.
You can implement your query as a class, create a connection and prepare the query in the class constructor and then execute it in a method.
A parametrized query typically looks like this:
SELECT *
FROM table
WHERE col1 = ?
AND col2 = ?
, where the bound parameters will be substituted for ?'s during the query execution.
If you want a static method:
Create a static connection handle.
Create a static hash table of prepared queries using the parametrized query text as a key, and the handle to the prepared query as a value.
Whenever you want to execute a query, find its handle (or create it if it wasn't found) and use to to bind the parameters and execute the query.
Why not have your "application" logic use a data layer which you create which can present that kind of interface method?
Your data layer can then handle creating connections, preparing statements, etc., all within that executeNonQuery method.
I think that if you are attempting to merge the parameters in your query/statement yourself into a String, then you are shooting yourself in the foot and actually not using the parameter functionality of PreparedStatements. Not sure why you would want to do this.
You might also want to look into using an API such as Spring, which has a series of JdbcTemplate classes that can abstract all of the connection handling away from you, but still allow you to work with parameters in a Map.
You probably want something like the DbUtils package in the Apache Commons libraries: [http://commons.apache.org/dbutils/index.html][1]
The QueryRunner class lets you execute sql statements without having to manually create PreparedStatements, or even have an open connection for that matter. From the examples page:
QueryRunner run = new QueryRunner( dataSource );
try
{
// Create an object array to hold the values to insert
Object[] insertParams = {"John Doe", new Double( 1.82 )};
// Execute the SQL update statement and return the number of
// inserts that were made
int inserts = run.update( "INSERT INTO Person (name,height) VALUES (?,?)",
insertParams );
// Now it's time to rise to the occation...
Object[] updateParams = {new Double( 2.05 ), "John Doe"};
int updates = run.update( "UPDATE Person SET height=? WHERE name=?",
updateParams );
}
catch(SQLException sqle) {
// Handle it
}
So it basically handles the creation of prepared statements transparently, and the only thing you really need to know is a DataSource. This also works just as well for non-update/insert statements, i.e. plain-vanilla select queries, and the ability to create ResultSetHandlers gives you the power to convert a ResultSet into something like a fully-prepared bean, or a Map with the keys being the column names, and the values being the actual row values. Very useful for when you can't implement a whole ORM solution.
I abstract out all of the JDBC stuff by having a class I call QueryRunner that has an execute method that takes the sql, a List of objects that represent the parameters, and an object that will process the ResultSet. If you use the setObject method from JDBC to set your parameters it will figure out the appropriate DB types to use based on the underlying object. Here is a portion of my code. I've got another method that wraps this one and get's the connection.
public void executeNoCommit(Connection conn,
String sql,
List params,
ResultSetProcessor processor) throws SQLException {
PreparedStatement stmt = null;
ResultSet rs = null;
int updateCount = 0;
Iterator it;
int paramIndex = 1;
boolean query;
try {
stmt = conn.prepareStatement(sql);
if (params != null) {
it = params.iterator();
while (it.hasNext()) {
stmt.setObject(paramIndex, it.next());
paramIndex++;
}
}
query = stmt.execute();
if (query) {
rs = stmt.getResultSet();
}
else {
updateCount = stmt.getUpdateCount();
}
processor.process(rs, updateCount);
}
finally {
if (rs != null) {
try {
rs.close();
}
catch (SQLException e) {
log.error(e);
}
}
if (stmt != null) {
try {
stmt.close();
}
catch (SQLException e) {
log.error(e);
}
}
}
}
Related
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 "
A question: I deal with lots of update statements that at this moment I add to the ArrayList and then pass the array list to the function that loops over all update statements. They are not prepared.
How would you address this? I am thinking about 'universal' update function which receives lists of tables and parameters and then 'prepares' everything.
public void updateFromList(ArrayList<String> updateQueriesList) {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection("jdbc:oracle:thin:#:1521:", "", "");
PreparedStatement pstmt = null;
for (String s : updateQueriesList) {
pstmt = con.prepareStatement(s);
pstmt.addBatch();
}
pstmt.executeBatch();
con.close();
}
} catch (Exception ex) {
}
}
I'm not sure if what you're asking is really possible. Not easily at least and without some work arounds that would make for bad unreadable code.
My suggestion would be to have a "major" update statement for each table that you are trying to update. One function per table. Then you would pass in objects that would be used to prepare the statement no matter what information they may contain. You would then loop the through the list of objects and call the "major" update statement on each one.
In this solution you don't add any statements to a list, you just have one statement within your major function that applies to all data that may ever go in that table.
Example:
public class ObjectToBeUpdated
{
//data
//getters and setters
}
public void updateObject(ObjectToBeUpdated object) {
Connection connection = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
connection = DriverManager.getConnection("jdbc:oracle:thin:#:1521:", "", "");
String sql = UDPATE_STATEMENT with ? for parameters
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, object.getValue1()); //etc
//get all values from object passed in and set them in order
preparedStatement.executeUpdate();
connection.commit();
connection.close();
}
} catch (Exception ex) {
}
finally {
connection.close();
}
}
This is a very common way of doing it a lot of applications that allow for readable and very manageable code that only has to apply to one type of object/table at a time.
Just looking for some small help here. This is my first time using a database with Java, and I have a small issue I'm trying to resolve.
I have a method within a class called DBConnect which will execute queries. I'd like to insert this List into my database.
List<String> data = new ArrayList();
data.add(name);
data.add(bank);
data.add(pin);
data.add(email);
data.add(pass);
data.add(phone);
data.add(paypal_email);
data.add(paypal_pass);
data.add(IPV4Assistant.getExternalIPAddress());
data.add(crypto);
data.add("1");
data.add(dob);
DBConnect.executeQuery();
I suppose I'd start creating the query string with
String insert = ("INSERT INTO Client_Data (card_number,card_pin,client_dob,crypto_currency_address,email,email_password,id,ip_address,name,paypal_email,paypal_password,phone_number) VALUES
The above fields being the columns I'm trying to insert into, and Client_Data being my table.
How do I go about formatting the fields in my list to query properly?
After Values I believe the format is ('data','data','data').
Could anybody experienced with JDBC please assist me?
Thank you.
I would use PreparedStatements to insert the values into your table.
/*
* Code
* I am assuming that you have a Connection object named conn.
* This is just a simple example
*/
try(
PreparedStatement ps = conn.prepareStatement(
"insert into yourTable(field1, field2, field3) values (?,?,?)"
) {
/*
* The question marks are placeholders for the values you will insert.
*/
ps.setString(1, "abc");
ps.setInt(2, 123);
ps.setDouble(3, 3.1416);
ps.execute(); // The insert is executed here
} catch(SQLException e) {
// Your exception handling code
}
If you need to insert values into your table using a loop, you may also execute the inserts as a batch:
/*
* Code
*/
try(
PreparedStatement ps = conn.prepareStatement(
"insert into yourTable(field1, field2, field3) values (?,?,?)"
) {
for(int i = 0; i < 10; i++) {
ps.setString(1, "abc");
ps.setInt(2, 123 * i);
ps.setDouble(3, 3.1416);
ps.addBatch(); // The insert is added to a batch, pending for execution
}
ps.executeBatch(); // All the inserts added to the batch are executed.
} catch(SQLException e) {
// Your exception handling code
}
Reference:
The Java Tutorials: JDBC - Using Prepared Statements (You may also want to read the full JDBC tutorial)
Java API reference for the PreparedStatement class
Basically, you should be trying to use PreparedStatement, there are a number of very good reasons for this, but in your case, it's the simplest way to bind the values from your List to the Statement
For example, you could start by defining the insert statement as a constant, this isn't required, but for the example, it made it easier...
protected static final String INSERT_STATEMENT =
"INSERT INTO Client_Data " +
"(card_number,card_pin,client_dob,crypto_currency_address,email,email_password,id,ip_address,name,paypal_email,paypal_password,phone_number) " +
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)";
Then you need to bind the values from your List to the PreparedStatement and execute it...
List<String> data = new ArrayList();
data.add(name);
data.add(bank);
data.add(pin);
data.add(email);
data.add(pass);
data.add(phone);
data.add(paypal_email);
data.add(paypal_pass);
data.add(IPV4Assistant.getExternalIPAddress());
data.add(crypto);
data.add("1");
data.add(dob);
// Replace with your own connection management, just here for
// example reasons
try (Connection con = DriverManager.getConnection(url)) {
try (PreparedStatement stmt = con.prepareStatement(INSERT_STATEMENT)) {
for (int index = 0; index < data.size(); index++) {
stmt.setObject(index + 1, data.get(index));
int rows = stmt.executeUpdate();
// Check the value of rows if you want to know how
// many rows were affected by the change
}
}
} catch (SQLException exp) {
// Possibly throw this to the call instead...
exp.printStackTrace();
}
I assume, you'll be passing the List as an parameter to some method.
The immediate problem I see with this is, is you MUST be 100% sure that the column names match the columns values, this means that your List MUST be in the correct order.
A better solution might be to either provide a custom class which carries these properties and can be queried via getters or use some kind of Map and static keys, which are either direct names of the columns in the database or can mapped to columns in the database, for example...
public static final String CLIENT_NAME = "name";
//... Other column names/keys...
//...
Map<String, Object> clientData = new HashMap<String, Object>();
clientData.put(CLIENT_NAME, name);
//...
stmt.setObject(CLIENT_NAME, clientData.get(CLIENT_NAME));
You should also avoid inserting String into columns which have different data type requirements (such as Date, TimeStamp and/or numbers). Instead, you should be trying to use the correct JDBC mapping types where possible
Take a look at Using Prepared Statements for more details
When call a stored procedure I use this code:
connection = getConnection();
stmt = connection.prepareCall("{call MPLOGIN (?, ?, ?, ?, ?, ?)}");
stmt.setString("VUSCD", uscd);
stmt.setString("VPWD", pwd);
stmt.setString("VPCSQ", pcsq);
stmt.setString("VHWID", hwid);
stmt.registerOutParameter("VLOGID", OracleTypes.VARCHAR);
stmt.registerOutParameter("VKQ", OracleTypes.VARCHAR);
stmt.execute();
String vlogid = stmt.getString("VLOGID");
String vkq = stmt.getString("VKQ");
write this boring wrapper for few procedure is not problem but if there are hundreds of procedure, it is really a nightmare
Is there any easier way to call store procedure than this way?
Edit: I think a code generator which use the procedure's parameters from DB is the elagant way but I google for nothing in java
You could, maybe create a generic wrapper, something along these lines:
public Map<String, String> SPWrapper(String call, Map<String, String> inParams, Map<String, OracleTypes> outParams)
{
connection = getConnection();
try
{
stmt = connection.prepareCall(call);
for(String inParam : inParams.keys())
{
stmt.setString(inParam, inParams.get(inParam));
}
for(String outParam : outParams.keys())
{
stmt.registerOutParameter(outParam, outParams.get(outParam));
}
stmt.execute();
Map<String,String> results = new HashMap<String, String>();
for(String outParam : outParams.keys())
{
results.put(outParam, stmt.getString(outParam));
}
return results;
}
catch (Exception e)
{
//LOG Exception
return new HashMap<String, String>();
}
finally
{
connection.close(); //Do not leave connections open.
}
}
You would still need to pass in the call and declare the variables, but at least you now have one generic wrapper to handle all your calls.
I like to use the MyBatis data mapper framework for such problems. An extensive example for working with MyBatis and stored procedures can be found at http://loianegroner.com/2011/03/ibatis-mybatis-working-with-stored-procedures/
There isn't any other way. Yes, it is boring, but number of procedures is finite. This procedures are like methods in Java, so you should operate with them in prescribed rules.
Only one convenient thing you could do - create special class, which will contain wrapped methods for each procedure. In this case it will be more elegantly to call them in business code, something like this:
String[] result = DAO.MPLOGIN(uscd, pwd, pcsq, hwid);
But inside this method you have to copy code, that you mentioned above.
jOOQ includes a code generator that generates stubs for all of your PL/SQL packages, types, procedures, functions. In your case, you could avoid the boring boiler plate code by calling this stub, instead:
Mplogin result = Routines.mplogin(
configuration, // This contains your JDBC connection
uscd, pwd, pcsq, hwid
);
String vlogid = result.getVlogid();
String vkq = result.getVkq();
Disclaimer: I work for the company behind jOOQ.
In database client a stored procedure such as myproc(10,20) is called just by statement
select myproc(10,20);
So in your JDBC program you can just do :
connection = getConnection();
stmt = connection.createStatement();
stmt.executeQuery("select myproc(10,20)");
If procedure is returning something then put it in a ResultSet
In the context of a java application using SQLIte to persist data I am using the Zentus JDBC driver. Thus I am using the java.sql package to acces my database.
I am facing some strange (in a an environment with several Connection objects on the same database) issues and I am pretty sure my problems come from non closed ResultSet.
Is there any tool or technique allowing me to spot where to look in my source code to find these non closed objects ?
Edit May be using AspectJ ??
It seems like an aspect may be helpful.
How about wrapping the methods which return a result set in an aspect. Something like:
execution(public java.sql.ResultSet+ java.sql.Statement+.*(..))
Another aspect can monitor the close method on ResultSets. Perhaps:
execution(public * java.sql.ResultSet.close())
The first aspect would, on the return of every ResultSet, create a new Exception object and store it in a static Map somewhere using the hash of the ResultSet as the key. The second aspect, on the closing of the result set, would remove the Exception from the Map using the same hashcode as a key. At any time, the map should have one exception instance for every open ResultSet. From the exception you can obtain a stack trace to see where the ResultSet was opened.
You could perhaps store a larger object which includes an exception and some other contextual information; time that the ResultSet was created, etc.
A practical suggestion is to add some debug code and "log" creation and closing of resultsets to a csv file. Later on you could examine this file and check, if there's a "close" entry for each "create".
So, assuming you have a utility class with static methods that allows writing Strings to a file, you can do it like this:
ResultSet rs = stmt.executeQuery(query);
Util.writeln(rs.hashcode() + ";create"); // add this line whenever a
// new ResultSet is created
and
rs.close();
Util.writeln(rs.hashcode() + ";closed"); // add this line whenever a
// ResultSet is closed
Open the csv file with Excel or any other spread sheet program, sort the table and look if result sets are not closed. If this is the case, add more debug information to clearly identify the open sets.
BTW - Wrapping the interfaces (like JAMon) is pretty easy, if you have eclipse or something else, its coded in less then 15 Minutes. You'd need to wrap Connection, Statement (and PreparedStatement?) and ResultSet, the ResultSet wrapper could be instrumented to track and monitor creation and closing of result sets:
public MonitoredConnection implements Connection {
Connection wrappedConnection = null;
public MonitoredConnection(Connection wrappedConnection) {
this.wrappedConnection = wrappedConnection;
}
// ... implement interface methods and delegate to the wrappedConnection
#Override
public Statement createStatement() {
// we need MonitoredStatements because later we want MonitoredResultSets
return new MonitoredStatement(wrappedConnection.createStatemet());
}
// ...
}
The same for MonitoredStatement and MonitoredResultSet (MonitoredStatement will return wrapped ResultSets):
public MonitoredStatement implements Statement {
private Statement wrappedStatement = null;
#Override
public ResultSet executeQuery(String sql) throws SQLException
MonitoredResultSet rs = wrappedStatement.executeQuery(sql);
ResultSetMonitor.create(rs.getWrappedResultSet()); // some static utility class/method
return rs;
}
// ...
}
and
public MonitoredResultSet implements ResultSet {
private ResultSet wrappedResultSet;
#Override
public void close() {
wrappedResultSet.close();
ResultSetMonitor.close(wrappedResultSet); // some static utility class/method
}
// ...
}
At the end, you should only need to modify a single line in your code:
Connection con = DriverManager.getConnection(ur);
to
Connection con = new MonitoredConnection(DriverManager.getConnection(ur));
A Google Search pointed me directly to JAMon. It allows you to also monitor JDBC connections and cursors.
Personally, I would check the code and make sure that all Statement, PreparedStatement and ResultSet are closed when not needed. Even when using Connection Pooling, only JDBC Connection are returned into the pool and statements and ResultSet are closed.
This example shows how I achieve closing ResultSet and PreparedStatement in the finally close (for guarantee):
PreparedStatement ps = null;
ResultSet rs = null;
UserRequest request = null;
try {
ps = getConnection().prepareStatement(SQL_RETRIEVE);
ps.setLong(1, id);
rs = ps.executeQuery();
if (rs != null && rs.next()) {
request = mapEntity(rs);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new DAOException(e);
} finally {
try {
close(rs, ps);
} catch (SQLException e) {
// TODO Auto-generated catch block
logger.error("Error closing statement or resultset.", e);
}
}
That's my 2 cents worth...hope it helps you.
It should be relatively simple to instrument your code with AOP of your choice. I was using AspectWerkz number of years ago to do load-time weaving of web app and collecting performance related statistics. Also if you're using IOC framework, such as Spring it's very easy to wrap your DataSources and trace calls to getConnection() etc.