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

Когда дело доходит до сохранения заказов, существует аналогичное обстоятельство. Необходимо не только сохранить данные заказа в таблице Taco_Order, но также и ссылки на каждый taco в заказе в таблице Taco_Order_Tacos. Но вместо того, чтобы использовать громоздкий PreparedStatementCreator, позвольте мне представить вам SimpleJdbcInsert, объект, который обертывает JdbcTemplate, чтобы упростить вставку данных в таблицу.

Начнем с создания JDBCOrderRepository, implementation OrderRepository.  Но прежде чем писать реализацию метода save(), давайте сосредоточимся на конструкторе, где вы создадите пару экземпляров SimpleJdbcInsert для вставки значений в таблицы Taco_Order и Taco_Order_Tacos.  В следующем листинге показан JDBCOrderRepository (без метода save ()).

Листинг 3.13 создание SimpleJdbcInsert из JdbcTemplate

package tacos.data;

import java.util.Date;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.jdbc.core.simple.SimpleJdbcInsert;

import org.springframework.stereotype.Repository;

import com.fasterxml.jackson.databind.ObjectMapper;

import tacos.Taco;

import tacos.Order;

@Repository

public class JdbcOrderRepository implements OrderRepository {

 private SimpleJdbcInsert orderInserter;

 private SimpleJdbcInsert orderTacoInserter;

 private ObjectMapper objectMapper;

 @Autowired

 public JdbcOrderRepository(JdbcTemplate jdbc) {

   this.orderInserter = new SimpleJdbcInsert(jdbc)

     .withTableName("Taco_Order")

     .usingGeneratedKeyColumns("id");

   this.orderTacoInserter = new SimpleJdbcInsert(jdbc)

     .withTableName("Taco_Order_Tacos");

   this.objectMapper = new ObjectMapper();

 }

 ...

}

Как и JdbcTacoRepository, JdbcOrderRepository inject-ид JdbcTemplate через его конструктор. Но вместо назначения JdbcTemplate непосредственно переменной экземпляра конструктор использует его для создания нескольких экземпляров SimpleJdbcInsert.

Первый экземпляр, который назначен переменной экземпляра orderInserter, настроен для работы с таблицей Taco_Order и предполагает, что свойство id будет предоставлено или сгенерировано базой данных. Второй экземпляр, назначенный orderTacoInserter, настроен для работы с таблицей Taco_Order_Tacos, но не содержит никаких инструкций о том, как ID будут генерироваться в этой таблице.

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

Теперь давайте посмотрим, как метод save() использует экземпляры SimpleJdbcInsert. В следующем списке показан метод save(), а также несколько частных методов save(), которые делегируются для реальной работы.

Листинг 3.14 использование SimpleJdbcInsert для вставки данных

@Override

public Order save(Order order) {

 order.setPlacedAt(new Date());

 long orderId = saveOrderDetails(order);

 order.setId(orderId);

 List<Taco> tacos = order.getTacos(); 

  for (Taco taco : tacos) {

   saveTacoToOrder(taco, orderId);

 }

 return order;

}

private long saveOrderDetails(Order order) {

 @SuppressWarnings("unchecked")

 Map<String, Object> values = objectMapper.convertValue(order, Map.class);

 values.put("placedAt", order.getPlacedAt());

 long orderId =orderInserter

   .executeAndReturnKey(values)

   .longValue();

 return orderId;

}

private void saveTacoToOrder(Taco taco, long orderId) {

 Map<String, Object> values = new HashMap<>();

 values.put("tacoOrder", orderId);

 values.put("taco", taco.getId());

 orderTacoInserter.execute(values);

}

Метод save() ничего не сохраняет. Он определяет поток для сохранения Order и связанных с ним объектов Taco и делегирует работу saveOrderDetails() и saveTacoToOrder().

SimpleJdbcInsert имеет несколько полезных методов для выполнения вставкиt: execute() и executeAndReturnKey(). Оба принимают Map<String, Object>, где map-ключи соответствуют именам столбцов в таблице, в которую вставляются данные. Map-значения вставляются в эти столбцы.

Такую Map легко создать, скопировав значения из Order в записи Map. Но у Order есть несколько свойств, и все они имеют одинаковое имя со столбцами, в которые они входят.  Из-за этого в saveOrderDetails() я решил использовать Jackson ObjectMapper и его метод convertValue() для преобразования Order в Map(Я признаю, что это хакерское использование ObjectMapper, но у вас уже есть Jackson в classpath; Spring Boot’s web starter включает его. Кроме того, использование ObjectMapper для сопоставления объекта с Map намного проще, чем копирование каждого свойства из объекта в Map. Не стесняйтесь заменить использование ObjectMapper любым кодом, который вы предпочитаете, который строит Map, который вы передадите на вставку объектов.). После создания Map задайте для записи placedAt значение свойства placedAt объекта Order.  Это необходимо, поскольку в противном случае ObjectMapper преобразует свойство Date в long, что несовместимо с полем placedAt в таблице Taco_Order.