Java - how to batch database inserts and updates - java

I want to batch up multiple types of database calls in one PreparedStatement. Is this possible?
Is there anyway to do something like
PreparedStatement pstmt = connection.prepareStatement("?");
where the ? can either be INSERT INTO MY_TABLE VALUES(1,2,3,4) or it could be UPDATE MY_TABLE, SET MY_VAL='1' WHERE MY_VAL IS NULL
Or do I always need to specify a table and action for my prepared statement?

Java will not allow you add only ? in preparedstatement string parameter, as it expects the ? for the place holder only for the parameters to the give SQL.
For your case, you may have to have 2 prepared statement objects, and in loop through, you can make a decision which one to call. So it would be something like below:
PreparedStatement insertPstmt = connection.prepareStatement("INSERT INTO MY_TABLE VALUES(?,?,?,?)");
PreparedStatement updatePstmt = connection.prepareStatement("UPDATE MY_TABLE, SET MY_VAL=? WHERE MY_VAL IS NULL");
While (<condition>) {
If (<insert condition>) {
// use insert pstmt and add batch
} else {
// use update pstmt and add batch
}
}
insertPstmt.executeBatch();
updatePstmt.executeBatch();
if you have any insert , which has dependency on the update, you might execute the batches accordingly. This will make sure that the update will work correctly. I would think of executing insert first, as they might not depend on update.

On a PreparedStatement, after binding the variables for the first execution, call
pstmt.addBatch();
then bind the variables for the next, and each time calling addBatch().
Then, when you're done adding batches you execute the bacth by alling
pstmt.executeBatch();
See :
http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html#addBatch%28%29
and
http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#executeBatch%28%29
BTW : injecting the entire statment into a variable won't work. This batch mechanism exists to reuse the same statement binding different variables each time.

Insert and Update commands don't return any data that has to be processed. If you want to do just things like in your examples, you can simply run a non executing query command and provide a concatenated string of all your sql strings separated by a semicolon.
"INSERT INTO MY_TABLE VALUES(1,2,3,4)" + ";" +"UPDATE MY_TABLE, SET MY_VAL='1' WHERE MY_VAL IS NULL" + ";" +...
You don't need to prepare the statement in that case and also wouldn't receive any performance gain by doing so.

Related

HSQLDB over JDBC: execute SQL statements in batch

I need to initialize a database from my Java application. For reasons of code maintainability, I would like to maintain the SQL code separately from the Java code (it is currently in a separate source file).
The first few lines of the file are as follows:
-- 1 - Countries - COUNTRIES.DAT;
drop table Countries if exists;
create table Countries(
CID integer,
ECC varchar(2),
CCD varchar(1),
NAME varchar(50));
I read the SQL code from the file and store it in a string. Then I do:
PreparedStatement stmt = dbConnection.prepareStatement(sqlString);
This fails with the following exception:
java.sql.SQLSyntaxErrorException: unexpected token: CREATE : line: 2
This looks as if JDBC doesn't like multiple SQL statements in a single PreparedStatement. I have also tried CallableStatement and prepareCall(), with the same result.
Does JDBC provide a way to pass the entire SQL script in one go?
The JDBC standard (and the SQL standard for that matter) assumes a single statement per execute. Some drivers have an option to allow execution of multiple statements in one execute, but technically that option violates the JDBC standard. There is nothing in JDBC itself that supports multi-statement script execution.
You need to separate the statements yourself (on the ;), and execute them individually, or find a third-party tool that does this for you (eg MyBatis ScriptRunner).
You might also want to look at something like flyway or liquibase.
To run a hardcoded / loaded from file queries you can use execute like:
Statement stmt = null;
String query = "drop table Countries if exists;
create table Countries(
CID integer,
ECC varchar(2),
CCD varchar(1),
NAME varchar(50));";
try {
stmt = con.createStatement();
stmt.execute(query);
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
If you want to run dynamic queries for example to append values you have to use PreparedStatement. For running a query from a file it's not recommended to put dynamic queries in it.

How to use a prepared statement correctly with Spring JDBCTemplate?

I have to do a select query on a database which I will execute often. It works completely fine with a normal statement:
List<Invoice> l = this.jdbcTemplate.query(
"SELECT id, name, stuff FROM example WHERE amount > ?",
new Object[] { "100" }, new RowMapper<Invoice>() {...} );
I execute the above statement very frequently, so for performance reason I want to use a prepared statement. However I'm now unsure how I correctly would use the spring API to achieve this.
I'm confused why spring would like me to give an instance of a PreparedStatementCreator as an argument for query? I thought that it's exactly the point that I do not create a new prepared statement every time I use the query method. So I think something along the line of the following snippet would be absolutely pointless as the prepared statement is created newly every time I call the query-method:
List<Invoice> l = this.jdbcTemplate.query(
new PreparedStatementCreator() {
String query = "SELECT id, name, stuff FROM example WHERE amount > ?";
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
return connection.prepareStatement(query);
}
},
new PreparedStatementSetter() {...},
new RowMapper<Invoice>() {...} );
Do I have to create my own ReusablePreparedStatementCreator? Which would only create a new method on the first call of createPreparedStatement.
Could the PreparedStatementCreatorFactory help me?
So to rephrase, how would I correctly create a select query that uses a prepared statement with Spring JDBCTemplate to really gain a performance advantage without losing the advantage of the RowMapper?
First of all, I'm not sure you would get a significant performance increase by not recreating the prepared statement every time, because the JDBC driver/database often caches prepared statements.
But if you want to execute multiple queries with a single prepared statement, simply use one of the execute() methods of JdbcTemplate, which creates a prepared statement from a SQL query (or lets you create it), and then executes a callback taking the prepared statement as argument. You can loop inside this callback, and execute the statement as many times you want.

Inserting data into Multiple tables

String insert1 = "INSERT INTO Table1(Col1, col2, col3)"
+ "VALUES(?,?,?)";
String insert2 = "INSERT INTO Table2(Colx, coly)"
+ "VALUES(?,?)";
Connection conn = aConn;
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(insert1);
// ps.addBatch(insert2);
I'm trying to Insert Data into multiple tables at a time, and it seems like addBatch(String sql) is not defined for PreparedStatement.
Is there any alternate way?
First of all, a PreparedStatement is used to cache a single SQL statement. This has the advantage that the driver/database might optimize the statement since it is expecting many of them and since it is a parameterized statement. If you want to use it for two different SQL statements you need two PreparedStatements.
In order to add rows to the statement you need to set your parameters using set*(1,...), set*(2,...), set*(3,...), etc. and then call addBatch() (no arguments!). Finally you submit the batch of statements using executeBatch().

Execute more than one statement at once in JDBC

I am using MySQL Database. The following piece creates a record and gets the id from the created record:
insertStmt = connection
.prepareStatement("INSERT INTO bugs (summary, status, report_date) VALUES (?, ?, ? )");
//...
insertStmt.executeUpdate();
idQuery = connection.prepareStatement("SELECT LAST_INSERT_ID()");
rs = idQuery.executeQuery();
if (rs != null) {
rs.next();
return new Long(rs.getLong(1)).toString();
}
Now, if two threads execute this and their execution is interleaved, say, the first thread inserts the record followed by the insertion by the second thread, after which the first thread calls last_insert_id() which will be incorrect for this thread as the second thread has already inserted a record.
This might be overcome using synchronization, however. Is there a way we can execute the two statements in a single database call?
LAST_INSERT_ID works per-connection, and as your question states you can have a race condition if two statements in two threads use the same connection.
You have two ways around this:
1: Use a separate connection per thread (not easy, but this is really the best option for scaling and sense; use connection pooling)
2: Use the form of executeUpdate that records the auto-generated key in the same API call, allowing you to read it back later using getGeneratedKeys so that you don't have to use LAST_INSERT_ID in a second query, so avoiding the race condition. There's a similar form of prepareStatement that you can use in prepared statements.
Option 2 is probably what you want in the short term. The link in option 2 goes straight to that API. This link is a MySQL article outlining how to use it.
According to https://dev.mysql.com/doc/refman/5.7/en/connector-j-reference-configuration-properties.html, you should be able to add ?allowMultiQueries=true to your JDBC connection string. Then you would be able to pass multiple statements, separated by semicolons, in Statement#execute(String sql) calls.
Edit: or, use a stored procedure that does what you want. Or, as you said, synchronize the Java code.
You can try using a Multiquery, combined the Insert and the Select Last_INSERT_ID() in the same string.
1) prepare the connection for using the multiquery:
"jdbc:mysql://"+host+"/"+database+"?allowMultiQueries=true"
2) Combine The Insert Query with the Select:
multiQuerySqlString="INSERT INTO bugs (summary, status, report_date) VALUES (1, 2, 3 ); SELECT LAST_INSERT_ID()"
3) esecute the query and expecting multiple result sets:
boolean isResultSet = statement.execute();
ResultSet res = statement.getResultSet();
if isResultSet = statement.getMoreResults();
// Second ReulstSet object
res = cs.getResultSet();
I hope it works
If you have to do this all on a single connection you can ask the driver to return the generated ID:
insertStmt = connection.prepareStatement("...",PreparedStatement.RETURN_GENERATED_KEYS );
insertStmt.executeUpdate();
ResultSet rs = insertStatement.getGeneratedKeys();
Long id = null;
if (rs != null)
{
rs.next();
id = rs.getLong(1);
}
connection.commit();
return id;
Depending on the driver you might need a different prepareStatement() call that takes the column names as the second parameter:
insertStmt = connection.prepareStatement("INSERT ", new String[] {"ID"});
But even in with the above code you should be doing the concurrent inserts on different physical connections to be able to properly control your transactions.

Insert value into sql table - selected column. Using Parameters like c#

Hi at now i am programming with Java and wanting to insert some value to specific column choosen. Also, i want to add it in parameter similar with C#:
example:
SqlCommand cmdb = new SqlCommand("insert into Assignment1(textname,person,date,text)
values (#textName,#person,#date,#text)", con);
cmdb.Parameters.AddWithValue("#textname", textPathLabel.Text);
cmdb.Parameters.AddWithValue("#person", personNameTB.Text);
cmdb.Parameters.AddWithValue("#date", DateTime.Now);
cmdb.Parameters.AddWithValue("#text", theBytes);
What you need is to use PreparedStatement in Java. With the example that you've, the corresponding PreparedStatement would look something like this:
PreparedStatement ps = connection.prepareStatement("insert into Assignment1(textname,person,date,text) values(?,?,?,?)");
You would then use the appropriate ps.setXX() methods to set the appropriate values for the parameters that you've defined and then call ps.executeUpdate() to execute the call to the database.
The "JDBC(TM) Database Access" would be a good place to start learning about how to use or execute common SQL statements, and perform other objectives common to database applications.

Categories