Выбрать главу

       resultSet.getString("id"),

       resultSet.getString("name"),

       Ingredient.Type.valueOf(resultSet.getString("type")));

   }

   return ingredient;

 } catch (SQLException e) {

 // ??? Что нужно сделать в этом случае ???

 } finally {

   if (resultSet != null) {

     try {

       resultSet.close();

     } catch (SQLException e) {}

   }

   if (statement != null) {

     try {

       statement.close();

     } catch (SQLException e) {}

   }

   if (connection != null) {

     try {

       connection.close();

    } catch (SQLException e) {}

   }

 }

 return null;

}

Уверяю вас, что где-то в листинге 3.1 есть пара строк, которые запрашивают в базе данных ингредиенты. Но бьюсь об заклад, вам было трудно обнаружить иголку запроса в стоге сена JDBC. Он окружен кодом, который создает соединение, создает оператор и очищает, закрывая соединение, оператор и результирующий набор.

Что еще хуже, что угодно всего может пойти не так при создании соединения или инструкции, или при выполнении запроса.  Это требует, чтобы вы поймали SQLException, который может или не может быть полезен в выяснении того, что пошло не так или как решить проблему.

SQLException является проверяемым исключением, которое требует обработки в блоке catch. Но наиболее распространенные проблемы, такие как неспособность создать соединение с базой данных или неправильно набранный запрос, не могут быть решены в блоке catch и, вероятно, будут перепрофилированы для обработки в восходящем направлении. Теперь посмотрим чем отличается код с использованием JdbcTemplate.

листинг 3.2 Запрос к базе данных с JdbcTemplate

private JdbcTemplate jdbc;

@Override

public Ingredient findOne(String id) {

 return jdbc.queryForObject(

   "select id, name, type from Ingredient where id=?",

   this::mapRowToIngredient, id);

 }

private Ingredient mapRowToIngredient(ResultSet rs, int rowNum)

 throws SQLException {

   return new Ingredient(

     rs.getString("id"),

     rs.getString("name"),

     Ingredient.Type.valueOf(rs.getString("type")));

}

Код в листинге 3.2 явно намного проще, чем исходный пример JDBC в листинге 3.1; никаких инструкций или соединений не создается. И, после того, как метод закончен, нет никакой очистки объектов. Наконец, нет никакой обработки исключений, которые не могут быть должным образом обработаны в блоке catch. То, что осталось - это код, который сосредоточился исключительно на выполнении запроса (вызова Jdbctemplate метода queryForObject()) и отображение результатов в объекте ингредиент (в mapRowToIngredient() методе).

Код в листинге 3.2 представляет собой фрагмент того, что необходимо сделать для использования JdbcTemplate для сохранения и чтения данных в приложении Taco Cloud. Давайте предпримем следующие шаги, необходимые для улучшения приложения с сохранением JDBC. Начнем с нескольких настроек объектов домена.

3.1.1 адаптация домена для сохранения.

При сохранении объектов в базе данных обычно рекомендуется иметь одно поле, которое однозначно идентифицирует объект. В вашем классе ингредиентов уже есть поле id, но вам нужно добавить поля id как в Taco, так и в Order.

Кроме того, может быть полезно знать, когда создан Taco и когда создан Order. Кроме того, к каждому объекту необходимо добавить поле для записи даты и времени сохранения объектов.  В следующем списке показаны новые поля id и createdAt, необходимые в классе Taco.

Листинг 3.3 Добавление ID и поля времени в класс Taco

@Data

public class Taco {

 private Long id;

 private Date createdAt;

 ...

}

Поскольку Lombok используется для автоматического создания методов доступа во время выполнения, нет необходимости делать что-либо большее, чем объявлять свойства id и createdAt. Они будут иметь соответствующие методы getter и setter по мере необходимости во время выполнения. Подобные изменения требуются и в классе Order, как показано здесь:

@Data

public class Order {

 private Long id;

 private Date placedAt;

 ...

}

Опять же, Lombok автоматически генерирует методы метода доступа, поэтому это единственные изменения, необходимые в Order. (Если по какой-то причине вы решили не использовать Lombok, вам нужно будет написать эти методы самостоятельно.)

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

3.1.2 Работа с JdbcTemplate

Перед началом использования JdbcTemplate необходимо добавить его в путь к классам проекта. Это можно легко сделать, добавив в сборку зависимость JDBC starter от Spring Boot:

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-jdbc</artifactId>