1. Введение
Java Database Connectivity (JDBC) — это Java API, используемый для взаимодействия с базами данных. Пакетная обработка группирует несколько запросов в один блок и передает его в базе данных за одно сетевое обращение.
В этой статье мы узнаем, как можно использовать JDBC для пакетной обработки запросов SQL.
Чтобы узнать больше о JDBC, вы можете прочитать нашу вводную статью здесь .
2. Почему пакетная обработка?
Производительность и согласованность данных являются основными мотивами для пакетной обработки.
2.1. Улучшенная производительность
В некоторых случаях использования требуется вставить большой объем данных в таблицу базы данных. При использовании JDBC одним из способов добиться этого без пакетной обработки является последовательное выполнение нескольких запросов.
Давайте посмотрим на пример последовательных запросов, отправляемых в базу данных:
statement.execute("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
+ "VALUES ('1','EmployeeName1','Designation1')");
statement.execute("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
+ "VALUES ('2','EmployeeName2','Designation2')");
Эти последовательные вызовы увеличат количество сетевых обращений к базе данных, что приведет к снижению производительности.
При использовании пакетной обработки эти запросы можно отправлять в базу данных за один вызов, что повышает производительность.
2.2. Согласованность данных
В определенных обстоятельствах данные необходимо помещать в несколько таблиц. Это приводит к взаимосвязанной транзакции, в которой важна последовательность отправляемых запросов.
Любые ошибки, возникающие во время выполнения, должны приводить к откату данных, переданных предыдущими запросами, если таковые имеются.
Давайте посмотрим на пример добавления данных в несколько таблиц:
statement.execute("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
+ "VALUES ('1','EmployeeName1','Designation1')");
statement.execute("INSERT INTO EMP_ADDRESS(ID, EMP_ID, ADDRESS) "
+ "VALUES ('10','1','Address')");
Типичная проблема в описанном выше подходе возникает, когда первая инструкция выполняется успешно, а вторая не выполняется. В этой ситуации нет отката данных, вставленных первым оператором, что приводит к несогласованности данных.
Мы можем добиться согласованности данных, разделив транзакцию на несколько вставок/обновлений, а затем зафиксировав транзакцию в конце или выполнив откат в случае исключений, но в этом случае мы по-прежнему повторно обращаемся к базе данных для каждого оператора.
3. Как сделать пакетную обработку
JDBC предоставляет два класса, Statement
и PreparedStatement
, для выполнения запросов к базе данных. Оба класса имеют собственную реализацию методов addBatch()
и executeBatch()
, которые предоставляют нам функциональность пакетной обработки.
3.1. Пакетная обработка с использованием инструкции
В JDBC самый простой способ выполнения запросов к базе данных — через объект Statement
.
Во- первых, с помощью addBatch()
мы можем добавить все SQL-запросы в пакет, а затем выполнить эти SQL-запросы с помощью executeBatch()
.
Тип возвращаемого значения executeBatch()
представляет собой массив int
, указывающий, сколько записей было затронуто выполнением каждого оператора SQL.
Давайте посмотрим на пример создания и выполнения пакета с использованием оператора:
Statement statement = connection.createStatement();
statement.addBatch("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
+ "VALUES ('1','EmployeeName','Designation')");
statement.addBatch("INSERT INTO EMP_ADDRESS(ID, EMP_ID, ADDRESS) "
+ "VALUES ('10','1','Address')");
statement.executeBatch();
В приведенном выше примере мы пытаемся вставить записи в таблицы EMPLOYEE
и EMP_ADDRESS
с помощью Statement
. Мы видим, как SQL-запросы добавляются в пакет для выполнения.
3.2. Пакетная обработка с использованием PreparedStatement
PreparedStatement
— еще один класс, используемый для выполнения SQL-запросов .
Это позволяет повторно использовать операторы SQL и требует от нас установки новых параметров для каждого обновления/вставки.
Давайте посмотрим на пример с использованием PreparedStatement.
Во-первых, мы настраиваем оператор, используя SQL-запрос, закодированный как String:
String[] EMPLOYEES = new String[]{"Zuck","Mike","Larry","Musk","Steve"};
String[] DESIGNATIONS = new String[]{"CFO","CSO","CTO","CEO","CMO"};
String insertEmployeeSQL = "INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
+ "VALUES (?,?,?)";
PreparedStatement employeeStmt = connection.prepareStatement(insertEmployeeSQL);
Затем мы перебираем массив значений String
и добавляем в пакет только что настроенный запрос.
После завершения цикла мы выполняем пакет:
for(int i = 0; i < EMPLOYEES.length; i++){
String employeeId = UUID.randomUUID().toString();
employeeStmt.setString(1,employeeId);
employeeStmt.setString(2,EMPLOYEES[i]);
employeeStmt.setString(3,DESIGNATIONS[i]);
employeeStmt.addBatch();
}
employeeStmt.executeBatch();
В приведенном выше примере мы вставляем записи в таблицу EMPLOYEE с помощью
PreparedStatement.
Мы можем видеть, как значения для вставки задаются в запросе, а затем добавляются в пакет для выполнения.
4. Вывод
В этой статье мы увидели, насколько важна пакетная обработка SQL-запросов при взаимодействии с базами данных с использованием JDBC.
Как всегда, код, относящийся к этой статье, можно найти на Github .