1. Обзор
В этом руководстве мы рассмотрим практические варианты использования модуля Spring JDBC.
Все классы в Spring JDBC разделены на четыре отдельных пакета:
core
— основная функциональность JDBC. Некоторые из важных классов этого пакета включаютJdbcTemplate
,SimpleJdbcInsert
,SimpleJdbcCall
иNamedParameterJdbcTemplate
.datasource
— служебные классы для доступа к источнику данных. Он также имеет различные реализации источников данных для тестирования кода JDBC вне контейнера Jakarta EE.object
— доступ к БД объектно-ориентированным способом. Это позволяет выполнять запросы и возвращать результаты в виде бизнес-объекта. Он также сопоставляет результаты запроса между столбцами и свойствами бизнес-объектов.поддержка
— классы поддержки для классов восновных
иобъектных
пакетах, например, обеспечивает функциональность переводаSQLException
2. Конфигурация
Начнем с простой настройки источника данных.
Мы будем использовать базу данных MySQL:
@Configuration
@ComponentScan("com.foreach.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 для источника данных
:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/springjdbc"/>
<property name="username" value="guest_user"/>
<property name="password" value="guest_password"/>
</bean>
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. Запросы с именованными параметрами
Чтобы получить поддержку именованных параметров , мы будем использовать другой шаблон 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
для предоставления значений именованных параметров.
Давайте рассмотрим использование свойств bean-компонента для определения именованных параметров:
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 путем реализации интерфейса RowMapper
.
Например, для каждой строки, возвращаемой запросом, Spring использует средство сопоставления строк для заполнения java-бина:
public class EmployeeRowMapper implements RowMapper<Employee> {
@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 = ?";
Employee employee = jdbcTemplate.queryForObject(
query, new Object[] { id }, new EmployeeRowMapper());
4. Преобразование исключений
Spring поставляется со своей собственной иерархией исключений данных из коробки — с DataAccessException
в качестве корневого исключения — и транслирует в нее все базовые необработанные исключения.
Итак, мы сохраняем здравомыслие, не обрабатывая низкоуровневые персистентные исключения. Мы также выигрываем от того, что Spring оборачивает низкоуровневые исключения в DataAccessException
или один из его подклассов.
Это также делает механизм обработки исключений независимым от базовой базы данных, которую мы используем.
Помимо SQLErrorCodeSQLExceptionTranslator
по умолчанию , мы также можем предоставить собственную реализацию SQLExceptionTranslator
.
Вот краткий пример пользовательской реализации — настройка сообщения об ошибке при нарушении двойного ключа, что приводит к коду ошибки 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
Давайте рассмотрим запуск простых операторов вставки с минимальной конфигурацией.
Оператор INSERT создается на основе конфигурации SimpleJdbcInsert
. Все, что нам нужно, это указать имя таблицы, имена столбцов и значения.
Во-первых, давайте создадим SimpleJdbcInsert
:
SimpleJdbcInsert simpleJdbcInsert =
new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");
Затем давайте укажем имена и значения столбцов и запустим операцию:
public int addEmplyee(Employee emp) {
Map<String, Object> parameters = new HashMap<String, Object>();
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);
}
Кроме того, мы можем использовать API executeAndReturnKey()
, чтобы позволить базе данных сгенерировать первичный ключ . Нам также нужно настроить фактический автоматически сгенерированный столбец:
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<String, Object> 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
, пакетные операции
можно запускать через API batchUpdate()
.
Интересной частью здесь является краткая, но очень полезная реализация BatchPreparedStatementSetter
:
public int[] batchUpdateUsingJdbcTemplate(List<Employee> 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
У нас также есть возможность пакетных операций с API NamedParameterJdbcTemplate
— 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 Boot
Spring Boot предоставляет стартер spring-boot-starter-jdbc
для использования JDBC с реляционными базами данных.
Как и в случае с каждым стартером Spring Boot, этот помогает нам быстро запустить наше приложение.
7.1. Зависимость от Maven
Нам понадобится зависимость spring-boot-starter-jdbc
в качестве основной. Нам также понадобится зависимость для базы данных, которую мы будем использовать. В нашем случае это MySQL
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
7.2. Конфигурация
Spring Boot автоматически настраивает источник данных для нас. Нам просто нужно указать свойства в файле свойств :
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.
Исходный код примеров доступен на GitHub .