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

   jdbc.update(

     "insert into Taco_Ingredients (taco, ingredient) " +

     "values (?, ?)",

     tacoId, ingredient.getId()

   );

 }

}

Как вы можете видеть, метод save() начинается с вызова private метода saveTacoInfo(), а затем использует ID taco, возвращенный из этого метода, для вызова saveIngredientToTaco(), который сохраняет каждый ингредиент. Дьявол кроется в деталях saveTacoInfo().

Когда вы вставляете строку в Taco, вам нужно знать идентификатор, сгенерированный базой данных, чтобы вы могли ссылаться на него в каждом из ингредиентов.  Метод update(), используемый при сохранении данных ингредиента, не помогает вам получить сгенерированный идентификатор, поэтому вам нужен другой метод update().

Метод update () принимает PreparedStatementCreator и KeyHolder. Это KeyHolder, который предоставит сгенерированный идентификатор taco. Но чтобы использовать его, необходимо также создать PreparedStatementCreator.

Как видно из листинга 3.10, создание PreparedStatementCreator нетривиально. Начните с создания PreparedStatementCreatorFactory, предоставив ему SQL, который вы хотите выполнить, а также типы каждого параметра запроса. Затем вызовите newPreparedStatementCreator() на этой фабрике, передав значения, необходимые в параметрах запроса для создания PreparedStatementCreator.

С PreparedStatementCreator на руках, вы можете вызвать update(), передав в PreparedStatementCreator и KeyHolder (в этом случае экземпляр GeneratedKeyHolder).  Как только update() закончено, можно возвратить ID taco, возвращая keyHolder.getKey().longValue().

Возвращаясь к save(), в цикле для каждого каждый Ingredient в Taco, вызывается saveIngredientToTaco(). В saveIngredientToTaco() методе используется простая форма update() для сохранения списка ингредиентв таблицу Taco_Ingredients.

Все, что осталось сделать с TacoRepository, - это внедрить его в DesignTacoController и использовать его при сохранении Taco. В следующем листинге показаны изменения, необходимые для внедрения репозитория.

Листинг 3.11 Внедрение и использование TacoRepository

@Controller

@RequestMapping("/design")

@SessionAttributes("order")

public class DesignTacoController {

 private final IngredientRepository ingredientRepo;

 private TacoRepository designRepo;

 @Autowired

 public DesignTacoController( IngredientRepository ingredientRepo, TacoRepository designRepo) {

   this.ingredientRepo = ingredientRepo;

   this.designRepo = designRepo;

 }

 …

}

Как вы можете видеть, конструктор принимает как IngredientRepository, так и TacoRepository. Он назначает переменные экземпляра, чтобы их можно было использовать в методах showDesignForm() и processDesign().

Говоря о методе processDesign(), его изменения немного более обширны, чем изменения, которые вы сделали в showDesignForm(). В следующем списке показан новый метод processDesign().

Листинг 3.12 сохранение состава taco и их привязка к заказам

@Controller

@RequestMapping("/design")

@SessionAttributes("order")

public class DesignTacoController {

 @ModelAttribute(name = "order")

 public Order order() {

   return new Order();

 }

 @ModelAttribute(name = "taco")

 public Taco taco() {

   return new Taco();

 }

 @PostMapping

 public String processDesign( @Valid Taco design, Errors errors, @ModelAttribute Order order) {

   if (errors.hasErrors()) {

     return "design";

   }

   Taco saved = designRepo.save(design);

   order.addDesign(saved);

   return "redirect:/orders/current";

 }

 ...

}

Первое, что можно заметить в коде в листинге 3.12, это то, что DesignTacoController теперь аннотируется SessionAttributes("order") и что у него есть новый аннотированный @ModelAttribute метод order(). Как и в случае с методом taco(), аннотация @ModelAttribute для order() гарантирует, что объект Order будет создан в модели. Но в отличие от объекта Taco в сеансе необходимо, чтобы order присутствовал в нескольких запросах, чтобы можно было создать несколько тако и добавить их в заказ. Аннотация @SessionAttributes на уровне класса задает любые объекты модели, такие как атрибут order, которые должны храниться в сеансе и доступны для нескольких запросов.

Реальная обработка состава taco происходит в методе processDesign(), который теперь принимает объект Order в качестве параметра, в дополнение к объектам Taco и Errors. Параметр Order аннотируется @ModelAttribute, чтобы указать, что его значение должно исходить из модели и что Spring MVC не должен пытаться привязать к нему параметры запроса.

После проверки на наличие ошибок, processDesign() использует внедренный TacoRepository для сохранения taco. Затем он добавляет объект Taco в Order, который сохраняется в сеансе.

Фактически объект Order остается в сеансе и не сохраняется в базе данных до тех пор, пока пользователь не завершит и не отправит форму заказа. В этот момент OrderController  должен вызвать реализацию OrderRepository, чтобы сохранить заказ. Давайте напишем эту реализацию.

ВСТАВКА ДАННЫХ С SempleJdbcInsert

Вы помните, что сохранение тако связано не только с сохранением имени тако и времени создания в таблицу Taco, но и с сохранением ссылки на ингредиенты, связанные с тако, в таблицу Taco_Ingredients. И вы также помните, что это потребовало от вас знать идентификатор Taco, который вы получили с помощью KeyHolder и PreparedStatementCreator.