Урок 23

Работа с БД с помощью JDBC


1. Утверждения (Statements)

Взаимодействовать с БД мы можем с помощью трёх интерфейсов, которые имплементируются каждым драйвером:

  1. Statement - этот интерфейс используется для доступа к БД для общих целей. Он крайне полезен, когда мы используем статические SQL – выражения во время работы программы. Этот интерфейс не принимает никаких параметров.
  2. PreparedStatement  - этот интерфейс используется в случае, когда мы планируем использовать SQL – выражения множество раз. Он также может принимать параметры во время работы программы.
  3. CallableStatement - этот интерфейс становится полезным в случае, когда мы хотим получить доступ к различным процедурам БД. Он также может принимать параметры во время работы программы.

2. Интерфейс Statement

Создание объекта: 

Statement statement = connection.createStatement();

После этого мы можем использовать наш экземпляр statement для выполнения SQL – запросов. Для этой цели интерфейс Statement имеет три метода, которые реализуются каждой конкретной реализацией JDBC драйвера:

  • boolean execute(String SQL) - позволяет вам выполнить Statement, когда неизвестно заранее, является SQL-строка запросом или обновлением. Метод возвращает true, если команда создала результирующий набор.
  • int executeUpdate(String SQL) используется для выполнения обновлений. Он возвращает количество обновленных строк. Для выполнения операторов INSERT, UPDATE или DELETE.
  • ResultSet executeQuery(String SQL) - используется для выполнения запросов (SELECT). Он возвращает для обработки результирующий набор.

Пример 1. Создание таблицы

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

import static jdbc.ConnectionData.DRIVER;
import static jdbc.ConnectionData.URL;
import static jdbc.ConnectionData.USER;
import static jdbc.ConnectionData.PASSWORD;

public class CreatingTable {
    private static final String CREATE_TABLE_QUERY =
            "CREATE TABLE users "
                    + "(id INT(5) NOT NULL AUTO_INCREMENT,"
                    + " username VARCHAR(50), "
                    + "PRIMARY KEY(id));";

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName(DRIVER);
        try (Connection connection =
                     DriverManager.getConnection(URL, USER, PASSWORD);
             Statement statement = connection.createStatement()) {
            statement.executeUpdate(CREATE_TABLE_QUERY);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ConnectionData {
    public static final String DRIVER = "com.mysql.jdbc.Driver";
    public static final String DB = "catalog";
    public static final String URL = "jdbc:mysql://localhost:3306/" + DB;
    public static final String USER = "root";
    public static final String PASSWORD = "admin";
}

3. Интерфейс ResultSet

Этот интерфейс представляет результирующий набор базы данных. Он обеспечивает приложению построчный доступ к результатам запросов в базе данных.

Во время обработки запроса ResultSet поддерживает указатель на текущую обрабатываемую строку. Приложение последовательно перемещается по результатам, пока они не будут все обработаны или не будет закрыт ResultSet.

Основные методы интерфейса ResultSet:

  1. public boolean absolute(int row) throws SQLException - метод перемещает курсор на заданное число строк от начала, если число положительно, и от конца - если отрицательно.
  2. public void afterLast() throws SQLException - этот метод перемещает курсор в конец результирующего набора за последнюю строку.
  3. public void beforeFirst() throws SQLException - этот метод перемещает курсор в начало результирующего набора перед первой строкой.
  4. public void deleteRow() throws SQLException - удаляет текущую строку из результирующего набора и базы данных.
  5. public ResultSetMetaData getMetaData() throws SQLException - предоставляет объект метаданных для данного ResultSet. Класс ResultSetMetaData содержит информацию о результирующей таблице, такую как количество столбцов, их заголовок и т.д.
  6. public int getRow() throws SQLException - возвращает номер текущей строки.
  7. public Statement getStatement() throws SQLException - возвращает экземпляр Statement, который произвел данный результирующий набор.
  8. public boolean next() throws SQLException, public boolean previous() throws SQLException - эти методы позволяют переместиться в результирующем наборе на одну строку вперед или назад. Во вновь созданном результирующем наборе курсор устанавливается перед первой строкой, поэтому первое обращение к методу next() влечет позиционирование на первую строку. Эти методы возвращают true, если остается строка для дальнейшего перемещения. Если строк для обработки больше нет, возвращается false.
  9. public void close() throws SQLException - осуществляет немедленное закрытие ResultSet вручную. Обычно этого не требуется, так как закрытие Statement, связанного с ResultSet, автоматически закрывает ResultSet.

Пример 2. Использование интерфейса ResultSet

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import static jdbc.ConnectionData.DRIVER;
import static jdbc.ConnectionData.URL;
import static jdbc.ConnectionData.USER;
import static jdbc.ConnectionData.PASSWORD;

public class RetrieveData {
    private static final String SELECT_QUERY = "SELECT * FROM users;";

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName(DRIVER);
        try (Connection connection =
                     DriverManager.getConnection(URL, USER, PASSWORD);
             Statement statement = connection.createStatement()) {

            ResultSet resultSet = statement.executeQuery(SELECT_QUERY);
            System.out.printf("%-20s%s%n", "id", "username");
            System.out.println("-------------------------------");
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("username");
                System.out.printf("%-20d%s%n", id, name);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4. Пакетное выполнение запросов

Для выполнения набора из нескольких запросов на обновление данных в интерфейс Statement были добавлены методы:

void addBatch(String)
int[] executeBatch()‏

Пакетное выполнение запросов уменьшает трафик между клиентом и СУБД и может привести к существенному повышению производительности.

Пример 3. Пакетное выполнение запросов

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

import static jdbc.ConnectionData.DRIVER;
import static jdbc.ConnectionData.URL;
import static jdbc.ConnectionData.USER;
import static jdbc.ConnectionData.PASSWORD;

public class InsertBatchData {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName(DRIVER);
        try (Connection connection =
                     DriverManager.getConnection(URL, USER, PASSWORD);
             Statement statement = connection.createStatement()) {

            statement.addBatch("INSERT INTO users (username) VALUES ('sidorov');");
            statement.addBatch("INSERT INTO users (username) VALUES ('petrov');");
            statement.addBatch("INSERT INTO users (username) VALUES ('kozlov');");

            statement.executeBatch();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. Интерфейс PreparedStatement

Особенностью SQL-выражений в PreparedStatement является то, что они могут иметь параметры. Параметризованное выражение содержит знаки вопроса в своем тексте. Например:

SELECT name FROM persons WHERE age=?

Перед выполнением запроса значение каждого вопросительного знака явно устанавливается методами setXxx()‏, например:

ps.setInt(1, 30);

Использование PreparedStatement приводит к более быстрому выполнению запросов при их многократном вызове с различными параметрами.

Пример 4. Использование интерфейса PreparedStatement

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import static jdbc.ConnectionData.DRIVER;
import static jdbc.ConnectionData.URL;
import static jdbc.ConnectionData.USER;
import static jdbc.ConnectionData.PASSWORD;

public class RetrieveDataPreparedStatement {
    private static final String INSERT_QUERY =
            "SELECT * FROM users WHERE id>? AND username LIKE ?";

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName(DRIVER);
        try (Connection connection =
                     DriverManager.getConnection(URL, USER, PASSWORD);
             PreparedStatement preparedStatement =
                     connection.prepareStatement(INSERT_QUERY)) {

            preparedStatement.setInt(1, 2);
            preparedStatement.setString(2, "P%");
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                System.out.printf("%d%23s%n", resultSet.getInt("id"), resultSet.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Пример 5. Использование интерфейса PreparedStatement

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

import static jdbc.ConnectionData.DRIVER;
import static jdbc.ConnectionData.URL;
import static jdbc.ConnectionData.USER;
import static jdbc.ConnectionData.PASSWORD;

public class InsertDataPreparedStatement {
    private static final String INSERT_QUERY =
            "INSERT INTO users (username) VALUES (?);";

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName(DRIVER);
        try (Connection connection =
                     DriverManager.getConnection(URL, USER, PASSWORD);
             PreparedStatement preparedStatement =
                     connection.prepareStatement(INSERT_QUERY)) {

            preparedStatement.setString(1, "misha");
            preparedStatement.addBatch();

            preparedStatement.setString(1, "grisha");
            preparedStatement.addBatch();
            preparedStatement.executeBatch();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6. Использование properties файлов

Пример 6. Содержимое database.properties файла

db.driver = com.mysql.jdbc.Driver
db.name = catalog
db.url = jdbc:mysql://localhost:3306/
db.user = root
db.password = admin

Пример 7. Использование ResourceBundle для чтения данных для аутентификации

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ResourceBundle;

public class ConnectorDB {
    public static Connection getConnection() throws SQLException {
        ResourceBundle resource = ResourceBundle.getBundle("database");
        String url = resource.getString("db.url");
        String user = resource.getString("db.user");
        String pass = resource.getString("db.password");
        String dbName = resource.getString("db.name");
        String driver = resource.getString("db.driver");
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return DriverManager.getConnection(url + dbName, user, pass);
    }
}

7. Data access object (DAO)

В программном обеспечении data access object (DAO) — это объект, который предоставляет абстрактный интерфейс к какому-либо типу базы данных или механизму хранения. DAO может использоваться для разных видов доступа к БД (JDBC, JPA).

Пример 8. Абстрактный класс DAO

import java.util.List;

public abstract class AbstractDAO<K extends Number, T> {
    public abstract List<T> findAll();

    public abstract T findEntityById(K id);

    public abstract boolean delete(K id);

    public abstract boolean delete(T entity);

    public abstract boolean create(T entity);

    public abstract T update(T entity);
}

Пример 9. Абстрактный класс DAO

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class UserDAO extends AbstractDAO<Integer, User> {
    public static final String SQL_SELECT_ALL_USERS = "SELECT * FROM users";
    public static final String SQL_SELECT_USER_ID =
            "SELECT * FROM users WHERE id=?";

    @Override
    public List<User> findAll() {
        List<User> users = new ArrayList<>();
        try (Connection connection = ConnectorDB.getConnection();
             Statement statement = connection.createStatement()) {
            ResultSet rs = statement.executeQuery(SQL_SELECT_ALL_USERS);
            while (rs.next()) {
                int id = rs.getInt(1);
                String name = rs.getString(2);
                users.add(new User(id, name));
            }
        } catch (SQLException e) {
            System.err.println("SQL Exeption (request or table failed):" + e);
        }
        return users;
    }

    @Override
    public User findEntityById(Integer id) {
        User user = null;
        try (Connection connection = ConnectorDB.getConnection();
             PreparedStatement statement =
                     connection.prepareStatement(SQL_SELECT_USER_ID)) {
            statement.setInt(1, id);
            ResultSet rs = statement.executeQuery();
            if (rs.next()) {
                id = rs.getInt(1);
                String name = rs.getString(2);
                user = new User(id, name);
            }
        } catch (SQLException e) {
            System.err.println("SQL Exeption (request or table failed):" + e);
        }
        return user;
    }

    @Override
    public boolean delete(Integer id) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean delete(User entity) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean create(User entity) {
        throw new UnsupportedOperationException();
    }

    @Override
    public User update(User entity) {
        throw new UnsupportedOperationException();
    }
}
public class MainDAO {
    public static void main(String[] args) {
        UserDAO userDAO = new UserDAO();
        System.out.println(userDAO.findAll());
        System.out.println(userDAO.findEntityById(1));
    }
}


0 comments
Leave your comment: