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.