Spring JDBC

Spring JDBC

1. обзор

В этой статье мы рассмотрим практические примеры использования модуля Spring JDBC.

Все классы в Spring JDBC разделены на четыре отдельных пакета:

  • core - основная функциональность JDBC. Некоторые из важных классов в этом пакете включаютJdbcTemplate,SimpleJdbcInsert,SimpleJdbcCall иNamedParameterJdbcTemplate.

  • datasource - служебные классы для доступа к источнику данных. Он также имеет различные реализации источника данных для тестирования кода JDBC вне контейнера Java EE.

  • object - объектно-ориентированный доступ к БД. Это позволяет выполнять запросы и возвращать результаты как бизнес-объект. Он также отображает результаты запроса между столбцами и свойствами бизнес-объектов.

  • support - классы поддержки для классов из пакетовcore иobject. E.g. обеспечивает функциональность переводаSQLException.

    == Дальнейшее чтение:

Spring Security: изучение аутентификации JDBC

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

Read more

Введение в Spring Data JPA

Введение в Spring Data JPA с Spring 4 - конфигурация Spring, DAO, ручные и сгенерированные запросы и управление транзакциями.

Read more

2. конфигурация

Для начала давайте начнем с простой настройки источника данных (в этом примере мы будем использовать базу данных MySQL):

@Configuration
@ComponentScan("com.example.jdbc")
public class SpringJdbcConfig {
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");

        return dataSource;
    }
}

В качестве альтернативы, мы также можем эффективно использовать встроенную базу данных для разработки или тестирования - вот быстрая конфигурация, которая создает экземпляр встроенной базы данных H2 и предварительно заполняет его простыми сценариями SQL:

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.H2)
      .addScript("classpath:jdbc/schema.sql")
      .addScript("classpath:jdbc/test-data.sql").build();
}

Наконец, то же самое, конечно, можно сделать с помощью настройки XML дляdatasource:


    
    
    
    

3. JdbcTemplate и выполняющиеся запросы

3.1. Основные запросы

Шаблон JDBC - это основной API, через который мы получаем доступ к большинству интересующих нас функций:

  • создание и закрытие соединений

  • выполнение операторов и вызовов хранимых процедур

  • переборResultSet и возврат результатов

Во-первых, давайте начнем с простого примера, чтобы увидеть, что может делатьJdbcTemplate:

int result = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM EMPLOYEE", Integer.class);

а также простой INSERT:

public int addEmplyee(int id) {
    return jdbcTemplate.update(
      "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill", "Gates", "USA");
}

Обратите внимание на стандартный синтаксис предоставления параметров - с использованием символа?. Далее - давайте посмотрим на альтернативу этому синтаксису.

3.2. Запросы с именованными параметрами

Чтобы получитьsupport for named parameters, мы будем использовать другой шаблон JDBC, предоставляемый фреймворком -NamedParameterJdbcTemplate.

Кроме того, это обертываетJbdcTemplate и предоставляет альтернативу традиционному синтаксису с использованием «?» для указания параметров. Под капотом он заменяет именованные параметры на JDBC «?» заполнитель и делегирует обернутомуJDCTemplate для выполнения запросов:

SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", 1);
return namedParameterJdbcTemplate.queryForObject(
  "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);

Обратите внимание, как мы используемMapSqlParameterSource для предоставления значений для названных параметров.

Например, давайте посмотрим на нижеприведенный пример, в котором свойства компонента используются для определения именованных параметров:

Employee employee = new Employee();
employee.setFirstName("James");

String SELECT_BY_ID = "SELECT COUNT(*) FROM EMPLOYEE WHERE FIRST_NAME = :firstName";

SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(employee);
return namedParameterJdbcTemplate.queryForObject(
  SELECT_BY_ID, namedParameters, Integer.class);

Обратите внимание, как мы теперь используем реализацииBeanPropertySqlParameterSource вместо того, чтобы указывать именованные параметры вручную, как раньше.

3.3. Сопоставление результатов запроса с объектом Java

Еще одна очень полезная функция - это возможность отображать результаты запроса на объекты Java - путем реализации интерфейсаthe RowMapper.

Например - для каждой строки, возвращаемой запросом, Spring использует преобразователь строк для заполнения Java-бина:

public class EmployeeRowMapper implements RowMapper {
    @Override
    public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();

        employee.setId(rs.getInt("ID"));
        employee.setFirstName(rs.getString("FIRST_NAME"));
        employee.setLastName(rs.getString("LAST_NAME"));
        employee.setAddress(rs.getString("ADDRESS"));

        return employee;
    }
}

Впоследствии мы можем теперь передать преобразователь строк в API запросов и получить полностью заполненные объекты Java:

String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
List employees = jdbcTemplate.queryForObject(
  query, new Object[] { id }, new EmployeeRowMapper());

4. Исключительный перевод

Spring поставляется со своей собственной иерархией исключений данных из коробки - сDataAccessException в качестве корневого исключения - и транслирует в него все базовые необработанные исключения.

И поэтому мы сохраняем здравомыслие, не имея необходимости обрабатывать исключения персистентности низкого уровня, и извлекаем выгоду из того факта, что Spring оборачивает исключения низкого уровня вDataAccessException или один из его подклассов.

Кроме того, это сохраняет механизм обработки исключений независимым от используемой нами базы данных.

Помимо значения по умолчаниюSQLErrorCodeSQLExceptionTranslator, мы также можем предоставить нашу собственную реализациюSQLExceptionTranslator.

Вот быстрый пример пользовательской реализации, настраивающей сообщение об ошибке, когда есть повторяющееся нарушение ключа, что приводит кerror code 23505 при использовании H2:

public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException
      customTranslate(String task, String sql, SQLException sqlException) {
        if (sqlException.getErrorCode() == 23505) {
          return new DuplicateKeyException(
            "Custom Exception translator - Integrity constraint violation.", sqlException);
        }
        return null;
    }
}

Чтобы использовать этот настраиваемый транслятор исключений, нам нужно передать егоJdbcTemplate, вызвав методsetExceptionTranslator():

CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator =
  new CustomSQLErrorCodeTranslator();
jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);

5. Операции JDBC с использованием классов SimpleJdbc

КлассыSimpleJdbc предоставляют простой способ настройки и выполнения операторов SQL. Эти классы используют метаданные базы данных для построения базовых запросов. КлассыSimpleJdbcInsert иSimpleJdbcCall обеспечивают более простой способ выполнения вызовов вставки и хранимых процедур.

5.1. SimpleJdbcInsertс

Давайте посмотрим на выполнение простых операторов вставки с минимальной конфигурацией.

The INSERT statement is generated based on the configuration of SimpleJdbcInsert, и все, что нам нужно, это указать имя таблицы, имена столбцов и значения.

Сначала создадимSimpleJdbcInsert:

SimpleJdbcInsert simpleJdbcInsert =
  new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");

Затем давайте предоставим имена и значения столбцов и выполним операцию:

public int addEmplyee(Employee emp) {
    Map parameters = new HashMap();
    parameters.put("ID", emp.getId());
    parameters.put("FIRST_NAME", emp.getFirstName());
    parameters.put("LAST_NAME", emp.getLastName());
    parameters.put("ADDRESS", emp.getAddress());

    return simpleJdbcInsert.execute(parameters);
}

Кроме того, чтобы разрешитьdatabase to generate the primary key, мы можем использовать APIexecuteAndReturnKey(); нам также необходимо настроить фактический столбец, который создается автоматически:

SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                        .withTableName("EMPLOYEE")
                                        .usingGeneratedKeyColumns("ID");

Number id = simpleJdbcInsert.executeAndReturnKey(parameters);
System.out.println("Generated id - " + id.longValue());

Наконец, мы также можем передать эти данные, используяBeanPropertySqlParameterSource иMapSqlParameterSource.

5.2. Хранимые процедуры сSimpleJdbcCall

Также давайте посмотрим на выполнение хранимых процедур - мы воспользуемся абстракциейSimpleJdbcCall:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)
                             .withProcedureName("READ_EMPLOYEE");
public Employee getEmployeeUsingSimpleJdbcCall(int id) {
    SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);
    Map out = simpleJdbcCall.execute(in);

    Employee emp = new Employee();
    emp.setFirstName((String) out.get("FIRST_NAME"));
    emp.setLastName((String) out.get("LAST_NAME"));

    return emp;
}

6. Пакетные операции

Еще один простой вариант использования - пакетирование нескольких операций вместе.

6.1. Основные пакетные операции с использованиемJdbcTemplate

ИспользованиеJdbcTemplate, Batch Operations может быть выполнено через APIbatchUpdate().

Интересная часть здесь - это краткая, но очень полезная реализацияBatchPreparedStatementSetter:

public int[] batchUpdateUsingJdbcTemplate(List employees) {
    return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",
        new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1, employees.get(i).getId());
                ps.setString(2, employees.get(i).getFirstName());
                ps.setString(3, employees.get(i).getLastName());
                ps.setString(4, employees.get(i).getAddress();
            }
            @Override
            public int getBatchSize() {
                return 50;
            }
        });
}

6.2. Пакетные операции с использованиемNamedParameterJdbcTemplate

У нас также есть возможность пакетных операций с APINamedParameterJdbcTemplate -batchUpdate().

Этот API проще, чем предыдущий - не нужно реализовывать какие-либо дополнительные интерфейсы для установки параметров, так как он имеет внутренний подготовленный оператор установки для установки значений параметров.

Вместо этого значения параметров можно передать методуbatchUpdate() в виде массиваSqlParameterSource.

SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
    "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);
return updateCounts;

7. Spring JDBC с загрузкой Spring

Spring Boot предоставляет стартовыйspring-boot-starter-jdbc для использования JDBC с реляционными базами данных.

Как и при каждом запуске Spring Boot, этот также помогает нам быстро запустить и запустить наше приложение.

7.1. Maven Dependency

Нам понадобится зависимостьspring-boot-starter-jdbc как основная, а также зависимость для базы данных, которую мы будем использовать. В нашем случае этоMySQL:


    org.springframework.boot
    spring-boot-starter-jdbc


    mysql
    mysql-connector-java
    runtime

7.2. конфигурация

Spring Boot автоматически настраивает источник данных для нас. Нам просто нужно указать свойства в файлеproperties:

spring.datasource.url=jdbc:mysql://localhost:3306/springjdbc
spring.datasource.username=guest_user
spring.datasource.password=guest_password

Вот и все, просто выполняя только эти конфигурации, наше приложение запускается и работает, и мы можем использовать его для других операций с базой данных.

Явная конфигурация, которую мы видели в предыдущем разделе для стандартного приложения Spring, теперь включена как часть автоконфигурации Spring Boot.

8. Заключение

В этой статье мы рассмотрели абстракцию JDBC в Spring Framework, охватывающую различные возможности, предоставляемые Spring JDBC, с практическими примерами.

Кроме того, мы рассмотрели, как быстро начать работу с Spring JDBC, используя стартовый пакет Spring Boot JDBC.

Исходный код примеров доступенover on GitHub.