Let’s see how we can perform batch insert in Java using JDBC APIs. Although you might already knew this, I will try to explain the basic to a bit complex scenarios.
In this note, we will see how we can use JDBC APIs like Statement
and PreparedStatement
to insert data in any database in batches. Also we will try to explore scenarios where we can run out of memory and how to optimize the batch operation.
So first, the basic API to Insert data in database in batches using Java JDBC.
I am calling this a simple batch. The requirement is simple. Execute a list of inserts in batch. Instead of hitting database once for each insert statement, we will using JDBC batch operation and optimize the performance.
Consider the following code:
String [] queries = {
"insert into employee (name, city, phone) values ('A', 'X', '123')",
"insert into employee (name, city, phone) values ('B', 'Y', '234')",
"insert into employee (name, city, phone) values ('C', 'Z', '345')",
};
Connection connection = new getConnection();
Statement statement = connection.createStatement();
for (String query : queries) {
statement.execute(query);
}
statement.close();
connection.close();
Code language: Java (java)
This is the BAD code. You are executing each query separately. This hits the database for each insert statement. Consider if you want to insert 1000 records. This is not a good idea.
We’ll below is the basic code to perform batch insert. Check it out:
Connection connection = new getConnection();
Statement statement = connection.createStatement();
for (String query : queries) {
statement.addBatch(query);
}
statement.executeBatch();
statement.close();
connection.close();
Code language: Java (java)
Note how we used addBatch()
method of Statement, instead of directly executing the query. And after adding all the queries we executed them in one go using statement.executeBatch()
method. Nothing fancy, just a simple batch insert.
Note that we have taken the queries from a String array. Instead you may want to make it dynamically. For example:
import java.sql.Connection;
import java.sql.Statement;
//...
Connection connection = new getConnection();
Statement statement = connection.createStatement();
for (Employee employee: employees) {
String query = "insert into employee (name, city) values('"
+ employee.getName() + "','" + employee.getCity + "')";
statement.addBatch(query);
}
statement.executeBatch();
statement.close();
connection.close();
Code language: Java (java)
Note how we are creating query dynamically using data from Employee object and adding it in batch to insert in one go. Perfect! isn’t it?
wait.. You must be thinking what about SQL Injection? Creating queries like this dynamically is very prone to SQL injection. And also the insert query has to be compiled each time.
Why not to use PreparedStatement
instead of simple Statement
. Yes, that can be the solution. Check out the below SQL Injection Safe Batch.
Consider the following code:
import java.sql.Connection;
import java.sql.PreparedStatement;
//...
String sql = "insert into employee (name, city, phone) values (?, ?, ?)";
Connection connection = new getConnection();
PreparedStatement ps = connection.prepareStatement(sql);
for (Employee employee: employees) {
ps.setString(1, employee.getName());
ps.setString(2, employee.getCity());
ps.setString(3, employee.getPhone());
ps.addBatch();
}
ps.executeBatch();
ps.close();
connection.close();
Code language: Java (java)
Checkout the above code. Beautiful. We used java.sql.PreparedStatement
and added insert query in the batch. This is the solution you must implement in your batch insert logic, instead of above Statement
one.
Still there is one problem with this solution. Consider a scenario where you want to insert half million records into database using batch. Well, that may generate OutOfMemoryError:
java.lang.OutOfMemoryError: Java heap space
com.mysql.jdbc.ServerPreparedStatement$BatchedBindValues.<init>(ServerPreparedStatement.java:72)
com.mysql.jdbc.ServerPreparedStatement.addBatch(ServerPreparedStatement.java:330)
org.apache.commons.dbcp.DelegatingPreparedStatement.addBatch(DelegatingPreparedStatement.java:171)
Code language: HTML, XML (xml)
This is because you are trying to add everything in one batch and inserting once. Best idea would be to execute batch itself in batch. Check out the below solution.
This is a simplest solution. Consider a batch size like 1000 and insert queries in the batches of 1000 queries at a time.
String sql = "insert into employee (name, city, phone) values (?, ?, ?)";
Connection connection = new getConnection();
PreparedStatement ps = connection.prepareStatement(sql);
final int batchSize = 1000;
int count = 0;
for (Employee employee: employees) {
ps.setString(1, employee.getName());
ps.setString(2, employee.getCity());
ps.setString(3, employee.getPhone());
ps.addBatch();
if(++count % batchSize == 0) {
ps.executeBatch();
}
}
ps.executeBatch(); // insert remaining records
ps.close();
connection.close();
Code language: Java (java)
This would be the ideal solution. This avoids SQL Injection and also takes care of out of memory issue. Check how we have incremented a counter count
and once it reaches batchSize
which is 1000, we call executeBatch()
.
Hope this helps.
Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…
Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…
Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…
1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…
GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…
1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…
View Comments
Well this is great.
But what about managing exception that could happen in some of the inserting records?
I would obviously want to pass correct record and catch only records with some problem.
Thanks
What if one of the columns is to be populated with sequence values??
For example..
String [] queries = {
"insert into employee (id,name, city, phone) values (seq.nextval,'A', 'X', '123')",
"insert into employee (id,name, city, phone) values (seq.nextval,'B', 'Y', '234')",
"insert into employee (id,name, city, phone) values (seq.nextval,'C', 'Z', '345')", };
when i execute the above query using batch.. it is inserting same id value in all the rows. How to solve this problem?? I do not want to use Triggers.
Although you execute these queries in batch, Oracle should resolve the sequences properly and should update its values for each row. Let me know if the problem is not resolved. It seems to me some diff issue.
Hi, i declare one variable type int and in X, in construtor execute select * MAX(ID) from tableexample.
X=select * MAX(ID) from tableexample.
this next ID.
insert into table (ID, name) values (x, "Michel");
this way you do not need to put the autoincrement ID or serial,
always in the constructor, will a int x = select * MAX (id) +1, when you insert the same id to next item in the database
insert into table (x, "orange")
insert into table (x, "apple")
insert into table (x, "pineapple")
so I decided to point of sale, I have helped
Superb :)
Awesome Tutorial. Really Great.
It has a problem, only works for the same DML, I can not to do an Update and an Insert at the same time
same problem I have faced. Did you find the solution for it.
Excellent tutorial. Concise and explained very well
Really this good... for learners
Very concise and to the point. Thanks for this link.
Thanks Dhiraj :)
how to create this kind of website plz.... suggest me..:)
very interesting and to the point i will get all idea how to work out
in batch insert
good idea... thanx