Программное JDBC-подключение

Содержание раздела
  1. Реализация программного JDBC-подключения к системе
    1. Пример класса для реализации подключения по JDBC
  2. Реализация потокового чтения данных с помощью курсора СУБД
    1. Пример класса для подключения и потокового чтения данных
  3. Реализация чтения данных с помощью параметризованного запроса (prepared statement)
    1. Пример класса для подключения и чтения данных с помощью параметризованного запроса

JDBC-драйвер системы позволяет подключаться программно (без использования SQL-клиента). Вы можете реализовать свое приложение, работающее с системой по JDBC.

Реализация программного JDBC-подключения к системе

Чтобы реализовать программное подключение к системе по JDBC:

  1. Включите библиотеку JDBC-драйвера в свой проект.
  2. В реализации класса вашего приложения, отвечающего за подключение к системе (см. базовый пример в секции ниже):
    1. Импортируйте пакеты Java SQL:
      import java.sql.*;
      
    2. Если вы используете Java версии менее 1.6, загрузите драйвер в память:
      Class.forName("ru.datamart.prostore.jdbc.Driver");
      
    3. (Опционально) Задайте максимальное количество запросов, исполняемых синхронно в одном подключении, с помощью параметра statementConcurrency. По умолчанию значение равно 2.

      Например:

      Properties properties = new Properties();
      properties.setProperty("statementConcurrency", "1");
      
    4. (Опционально) Определите формат передачи данных между драйвером и системой:
      • Задайте формат из следующих:
        • avro — Avro-формат без сжатия (используется по умолчанию);
        • json — JSON-строка;
        • avro-deflate — Avro-формат со сжатием по алгоритму Deflate;
        • avro-snappy — Avro-формат со сжатием по алгоритму Snappy;
        • avro-bzip2 — Avro-формат со сжатием по алгоритму BZip2;
        • avro-xz — Avro-формат со сжатием по алгоритму XZ;
        • avro-zstanstard — Avro-формат со сжатием по алгоритму Zstandard.

      Например:

      properties.setProperty("format", "avro-snappy");
      
      • Если вы используете Avro-формат со сжатием, задайте уровень сжатия данных, например:
      properties.setProperty("compressionLevel", "3");
      

      Доступные уровни сжатия данных для каждого из алгоритмов см. в документации Apache Avro.

      • Если вы используете Avro-формат avro-snappy или avro-xz, подключите нужные зависимости:
      <dependency>
          <groupId>org.xerial.snappy</groupId>
          <artifactId>snappy-java</artifactId>
      </dependency>
      <dependency>
          <groupId>org.tukaani</groupId>
          <artifactId>xz</artifactId>
      </dependency>
      
    5. Установите соединение с системой с помощью метода DriverManager.getConnection() в следующем формате:
      // Имя пользователя и авторизационный токен auth_token (если в системе включена аутентификация)
      properties.setProperty("user", "jwt");
      properties.setProperty("password", "auth_token");
             
      // Адрес подключения
      String url = "jdbc:prostore://ProstoreNodeHost:portNumber/logicalDatabaseName";
             
      // Установка соединения в случае, если аутентификация отключена и используется формат передачи данных по умолчанию (Avro без сжатия)
      Connection connection = DriverManager.getConnection(url);
      // Установка соединения в случае, если аутентификация включена и/или используется формат передачи данных, заданный в параметрах подключения
      Connection connection = DriverManager.getConnection(url, properties);
      

      Где:

      • prostoreNodeHost — IP-адрес или имя хоста с нодой Prostore;
      • portNumber — номер порта для подключения;
      • (опционально) logicalDatabaseName — имя логической базы данных, используемой по умолчанию.
    6. Выполните вызов нужных запросов SQL+ и по завершении обработки их результатов закройте подключение.

Пример класса для реализации подключения по JDBC

В примере ниже показана базовая реализация класса SimpleDtmJDBCExample, который задает формат передачи данных (avro-snappy), устанавливает соединение с системой по указанному адресу и закрывает соединение.

import java.sql.*;
import java.util.Properties;
public class SimpleProstoreJDBCExample {
    public static void main(String[] args) {
        String url = "jdbc:prostore://10.92.3.3:9090/marketing";
        Properties properties = new Properties();
        properties.setProperty("user", "jwt");
        properties.setProperty("password", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
        properties.setProperty("statementConcurrency", "1");
        properties.setProperty("format", "avro-snappy");
        properties.setProperty("compressionLevel", "3");
        try (Connection connection = DriverManager.getConnection(url, properties)) {
            System.out.println("Connected");
            // Добавьте логику работы с подключением
        } catch (SQLException error) {
            //  Добавьте логику обработки SQL-ошибок
            error.printStackTrace();
        }
    }
}

Реализация потокового чтения данных с помощью курсора СУБД

Чтобы после подключения к системе выбрать данные порциями, используя курсор СУБД:

  1. Перед вызовом SELECT-запроса задайте размер порций данных с помощью метода Statement.setFetchSize.
  2. (Опционально) Если необходимо, задайте дополнительные параметры чтения данных:
    • максимальное количество записей, возвращаемых по запросу с учетом всех порций данных, — с помощью метода Statement.setMaxRows;
    • максимальное время (в секундах) ожидания ответа по каждой порции данных — с помощью метода Statement.setQueryTimeout.
  3. Откройте курсор с помощью метода Statement.executeQuery.
  4. Реализуйте передвижение курсора с помощью метода ResultSet.next.
  5. После обработки результатов SELECT-запроса закройте курсор с помощью метода close интерфейса ResultSet, Statement или Connection.

Ниже показан пример с разделением данных на порции по 100 записей, открытием курсора, его сдвигом и закрытием, а также опциональными ограничениями — на максимальное количество записей по запросу (5000 записей) и на максимальное время ожидания каждого ответа с порцией данных (300 секунд = 5 минут).

Полный пример базовой реализации класса см. в секции Пример класса для подключения и потокового чтения данных по JDBC.

Statement statement = connection.createStatement();

// Включение потокового чтения данных порциями указанного размера
statement.setFetchSize(100);
// Максимальное количество записей по запросу. Опциональный параметр, по умолчанию количество не ограничено
statement.setMaxRows(5000);
//Максимальное время исполнения запроса в секундах. Опциональный параметр, по умолчанию время не ограничено
statement.setQueryTimeout(300);

ResultSet resultset = statement.executeQuery("SELECT * FROM sales");
while (resultset.next()) {
  // Добавьте требуемую обработку полученных строк
  } 
resultset.close();

Потоковое чтение данных по JDBC не поддерживает одновременный возврат результатов по нескольким запросам. Это означает, что, если в реализации класса сделан один вызов statement.executeQuery с несколькими запросами, перечисленными через точку с запятой, результаты каждого следующего запроса вернуться только после возврата всех результатов по предыдущему запросу.

Пример класса для подключения и потокового чтения данных

В примере ниже показана базовая реализация класса StreamingExample, который устанавливает соединение с системой по заданному адресу, исполняет SELECT-запрос с использованием курсора и затем закрывает курсор и соединение.

import java.sql.*;
public class StreamingExample {
    public static void main(String[] args) {
        String url = "jdbc:prostore://10.92.3.3:9090/marketing";
        try (Connection connection = DriverManager.getConnection(url)) {
            System.out.println("Connected");
            try (Statement statement = connection.createStatement()) {
                // Включение потокового чтения данных порциями по 100 записей
                statement.setFetchSize(100);
                statement.setMaxRows(5000);
                statement.setQueryTimeout(300);
                try (ResultSet resultset = statement.executeQuery("SELECT * FROM sales")) {
                    while (resultset.next()) {
                        // Добавьте логику обработки полученных строк
                    }
                }
            } catch (SQLException error) {
                // Добавьте логику обработки SQL-ошибок
                error.printStackTrace();
            }
        }
    }      
}

Реализация чтения данных с помощью параметризованного запроса (prepared statement)

Чтобы после подключения к системе выбрать данные с помощью параметризованного запроса:

  1. Создайте параметризованный запрос с помощью метода Connection.preparedStatement.
  2. Задайте параметры запроса с помощью методов семейства PreparedStatement.set.
  3. Исполните запрос с помощью метода PreparedStatement.executeQuery.
  4. Обработайте результат запроса нужным вам образом.

Пример класса для подключения и чтения данных с помощью параметризованного запроса

В примере ниже показана базовая реализация класса PreparedStatementExample, который устанавливает соединение с системой по заданному адресу, исполняет параметризованный SELECT-запрос и затем закрывает соединение.

import java.sql.*;
public class PreparedStatementExample {
    public static void main(String[] args) {
        String url = "jdbc:prostore://10.92.3.3:9090/marketing";
        ResultSet resultSet = null;
        try (Connection connection = DriverManager.getConnection(url)) {
            System.out.println("Connected");
            // Создание параметризованного SELECT-запроса
            String query = "SELECT * FROM sales " +
                           "FOR SYSTEM_TIME AS OF ? " +
                           "WHERE id > ?;";
            try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
                // Установка значений для запроса
                preparedStatement.setTimestamp(1, Timestamp.valueOf("2024-05-10 13:12:09")); // Установка метки времени 10 мая 2024 13:12:09 для FOR SYSTEM_TIME AS OF
                preparedStatement.setInt(2, 70); // Установка значения 70 для id
                // Выполнение запроса
                resultSet = preparedStatement.executeQuery();
                while (resultset.next()) {
                    // Добавьте логику обработки полученных строк
                }
            } catch (SQLException error) {
                // Добавьте логику обработки SQL-ошибок
                error.printStackTrace();
            }
        }
    }
}