Руководство по JDBC. Транзакции.

Когда мы работаем с JDBC, то по умолчанию наше соединение работает в режиме auto-commit, это означает, что каждый SQL – запрос будет выполнен и результаты будут сохранены в таблице нашей базы данных (далее – БД).

Для простых приложений это крайне удобно. Но, если мы хотим увеличить производительность, использовать распределённые транзакции, либо интегрировать бизнес-логику, то нам необходимо выключить режим auto-commit для управления нашими транзакциями.

Транзакцию дают нам возможность контролировать когда и где сохранять изменения в БД. Благодаря этому мы, например, можем объединить группу SQL – запросов в одну логическую группу и, если один из запросов не пройдёт – мы отменяем всю транзакцию.

Для того чтобы получить доступ к управлению транзакциями, нам необходимо использовать метод setAutoCommit().

В коде это выглядит следующим образом:


connection.setAutoCommit(false);


Выполнение и откат (Commit and Rollback)

После того, как мы выполнили необходимые нам изменения, мы должны вызвать метод commit() таким образом:


connection.commit();

Если же мы хотим выполнить откат изменений, то нам необходимо вызвать метод rollback():


connection.rollback();


Точки сохранения (Savepoints)

Начиниая со спецификации JDBC 3.0 интерфейс Savepoint даёт нам ещё больший контроль над транзакциями.
Когда мы используем savepoint, мы определяем точку, до которой произойдёт откат в случае, если нам понадобится отменить изменения.
Для управления этой функцией существует два метода:

  • setSavepoint (String savepointName)
    Определяет новую точку сохранения и возвращает экземпляр Savepoint.
  • releaseSavepoint (String savepointName)
    Этот метод удаляет точку сохранения. В качестве параметра этот метод принимает экземпляр Savepoint.

Для понимания того, как это работает на практике рассмотрим пример простого приложения.

Пример:


import java.sql.*;

public class SavepointDemo {
    /**
     * JDBC Driver and database url
     */
    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    static final String DATABASE_URL = "jdbc:mysql://localhost/PROSELYTE_TUTORIALS";

    /**
     * User and Password
     */
    static final String USER = "ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ";
    static final String PASSWORD = "ВАШ_ПАРОЛЬ";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Connection connection = null;
        Statement statement = null;

        Class.forName("com.mysql.jdbc.Driver");

        connection = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);
        connection.setAutoCommit(false);

        statement = connection.createStatement();

        String SQL;
        SQL = "SELECT * FROM developers";

        ResultSet resultSet = statement.executeQuery(SQL);

        System.out.println("Retrieving data from database...");
        System.out.println("\nDevelopers:");
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String specialty = resultSet.getString("specialty");
            int salary = resultSet.getInt("salary");

            System.out.println("\n================\n");
            System.out.println("id: " + id);
            System.out.println("Name: " + name);
            System.out.println("Specialty: " + specialty);
            System.out.println("Salary: $" + salary);
        }

        System.out.println("\n===========================\n");
        System.out.println("Creating savepoint...");
        Savepoint savepointOne = connection.setSavepoint("SavepointOne");

        try {
            SQL = "INSERT INTO developer VALUES (6, 'John','C#', 2200)";
            statement.executeUpdate(SQL);

            SQL = "INSERT INTO developers VALUES (7, 'Ron', 'Ruby', 1900)";
            statement.executeUpdate(SQL);

            connection.commit();
        } catch (SQLException e) {
            System.out.println("SQLException. Executing rollback to savepoint...");
            connection.rollback(savepointOne);
        }
        SQL = "SELECT * FROM developers";
        resultSet = statement.executeQuery(SQL);
        System.out.println("Retrieving data from database...");
        System.out.println("\nDevelopers:");
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String specialty = resultSet.getString("specialty");
            int salary = resultSet.getInt("salary");

            System.out.println("id: " + id);
            System.out.println("Name: " + name);
            System.out.println("Specialty: " + specialty);
            System.out.println("Salary: $" + salary);
            System.out.println("\n================\n");
        }

        System.out.println("Closing connection and releasing resources...");
        resultSet.close();
        statement.close();
        connection.close();
    }
}

В результате работы программы мы получим, примерно, следующий результат:


/*Some System Messages*/

Retrieving data from database...

Developers:

================

id: 1
Name: Proselyte
Specialty: Java
Salary: $3000

================

id: 2
Name: Peter
Specialty: C++
Salary: $4000

================

id: 3
Name: AsyaSmile
Specialty: UI/UX
Salary: $3000

================

id: 4
Name: Eugene
Specialty: Java
Salary: $4000

================

id: 5
Name: Mike
Specialty: PHP
Salary: $2500

===========================

Как мы видим, в этом приложении мы добавляем две записи с помощью двух SQL – запросов. Первый запрос корректен и выполняется без проблем. Второй же запрос содержит синтаксические ошибки и программа выдаёт нам SQLException.

Но метод commit() мы вызываем только после второго запроса. Поэтому, после выполнения отката, мы получаем таблицу в исходном виде, так как первый запрос также был отменён.

В этом уроке мы изучили основы управления транзакциями и рассмотрели пример с применением методов commit(), rollback() и использованием точки сохранения (savepoint).

В следующем уроке мы изучим исключения и их обработку при работе с JDBC.