在 processOrder 方法中,当我尝试保存 tacoOrder 实体时,它给了我这个错误
jakarta.validation.ConstraintViolationException: Validation failed for classes [com.example.tacohouse.entities.Taco] during persist time for groups [jakarta.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='You must choose at least 1 ingredient', propertyPath=ingredients, rootBeanClass=class com.example.tacohouse.entities.Taco, messageTemplate='You must choose at least 1 ingredient'}
我设置了调试日志,发现来自方法参数中表单绑定的对象非常好,并且包含正确的玉米卷和配料。
这是processOrder方法
@Transactional
@PostMapping("/current")
public String processOrder(@ModelAttribute("order") @Valid TacoOrder order, Errors errors,
@AuthenticationPrincipal User user, RedirectAttributes redirectAttributes) {
if(errors.hasErrors()){
return "orderForm";
}
order.setUser(user);
order.setPlacedAt(new Date());
log.info(" {} ", sessionScopedTacoOrderManager.getTacoOrder()); //fine
log.info(" {} ", order); //fine
TacoOrder tacoOrder = orderRepository.save(order); //problem in this
user.addTacoOrder(tacoOrder);
userRepository.save(user);
log.info("Order submitted: {}", tacoOrder);
return "redirect:/orders";
}
这些是后续日志
2024-03-18 19:26:01.932 [http-nio-8080-exec-9] INFO c.e.t.controllers.OrderController -TacoOrder(id=94ef1789-4757-41db-8196-bf88117b498e, placedAt=Mon Mar 18 19:26:01 IST 2024, deliveryName=a, deliveryStreet=a, deliveryCity=a, deliveryState=a, deliveryZip=a, ccNumber=4417123456789113, ccExpiration=11/2024, ccCVV=111, tacos=[Taco(id=null, createdAt=Mon Mar 18 19:25:45 IST 2024, name=firstTaco, ingredients=[Ingredient(id=CHED, name=Cheddar, type=CHEESE)])])
2024-03-18 19:26:01.932 [http-nio-8080-exec-9] INFO c.e.t.controllers.OrderController - TacoOrder(id=94ef1789-4757-41db-8196-bf88117b498e, placedAt=Mon Mar 18 19:26:01 IST 2024, deliveryName=a, deliveryStreet=a, deliveryCity=a, deliveryState=a, deliveryZip=a, ccNumber=4417123456789113, ccExpiration=11/2024, ccCVV=111, tacos=[Taco(id=null, createdAt=Mon Mar 18 19:25:45 IST 2024, name=firstTaco, ingredients=[Ingredient(id=CHED, name=Cheddar, type=CHEESE)])])
2024-03-18 19:26:01.932 [http-nio-8080-exec-9] INFO c.e.t.controllers.OrderController - Order submitted: TacoOrder(id=94ef1789-4757-41db-8196-bf88117b498e, placedAt=2024-03-18 19:26:01.908, deliveryName=a, deliveryStreet=a, deliveryCity=a, deliveryState=a, deliveryZip=a, ccNumber=4417123456789113, ccExpiration=11/2024, ccCVV=111, tacos=[Taco(id=null, createdAt=null, name=null, ingredients=[])])
根据日志,当前会话订单和表单绑定订单,两者都可以使用,并且都已正确填写。
如果你需要的话,这是加工Tacos的方法
@PostMapping
public String processTaco(@Valid Taco taco,Errors errors) {
if(errors.hasErrors()){
return "designTaco";
}
taco.setCreatedAt(new Date());
TacoOrder tacoOrder = sessionScopedTacoOrderManager.getTacoOrder();
tacoOrder.addTaco(taco);
log.info("Processing taco: {}", taco);
return "redirect:/orders/current";
}
其日志
2024-03-19 15:05:19.335 [http-nio-8080-exec-7] INFO c.e.t.c.DesignTacoController - Processing taco: Taco(id=null, createdAt=Tue Mar 19 15:05:19 IST 2024, name=Ansh Anand, ingredients=[Ingredient(id=CHED, name=Cheddar, type=CHEESE)])
这些是实体
@Data
@Entity
@Getter
@Setter
public class TacoOrder{
@Id //@GeneratedValue(strategy = GenerationType.IDENTITY)
private final String id = UUID.randomUUID().toString();
private Date placedAt;
@NotBlank(message = "Delivery name is required")
private String deliveryName;
@NotBlank
private String deliveryStreet;
@NotBlank(message = "City is required")
private String deliveryCity;
@NotBlank(message = "State is required")
private String deliveryState;
@NotBlank(message = "Zip Code is required")
private String deliveryZip;
@CreditCardNumber(message = "Invalid Credit Card Number")
private String ccNumber;
@Pattern(regexp = "^(0[1-9]|1[0-2])/20(2[3-9]|[3-9][0-9])$",message = "Must be of format MM/YYYY")
private String ccExpiration;
@NotNull
@Digits(integer=3, fraction=0, message="Invalid CVV")
private String ccCVV;
@ManyToOne
@ToString.Exclude
private User user;
@OneToMany(mappedBy = "tacoOrder", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Taco> tacos = new ArrayList<>();
public void addTaco(Taco taco) {
taco.setTacoOrder(this);
this.tacos.add(taco);
}
}
@Data
@Entity
@NoArgsConstructor //if we make a cunstructor @data wont make a noArgConstructor iteelf, so we define it
public class Taco{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date createdAt;
@NotBlank
@Size(min = 5, message = "Name must be at least 5 characters long")
private String name;
@ManyToMany
@JoinTable(
name = "ingredient_ref",
joinColumns = @JoinColumn(name = "taco_id"),
inverseJoinColumns = @JoinColumn(name = "ingredient_id"))
@NotNull
@Size(min=1, message="You must choose at least 1 ingredient")
private List<Ingredient> ingredients = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "taco_order_id")
@ToString.Exclude
private TacoOrder tacoOrder;
public Taco(Date createdAt, String name, List<Ingredient> ingredients) {
this.createdAt = createdAt;
this.name = name;
this.ingredients = ingredients;
}
}
@Data
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED,force = true)
@RequiredArgsConstructor
public class Ingredient {
@Id
private final String id;
private final String name;
@Enumerated(EnumType.STRING)
private final Type type;
public enum Type {
WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE, EXTRA
}
}
为什么保存 tacoOrder 时,taco 列表变为空? (我删除了炸玉米饼列表上的级联以测试是否发生任何不同的情况,它在保存时没有给出错误,因为它没有保存炸玉米饼子实体,但保存的 TacoOrder 在 tacoList 字段中没有任何炸玉米饼) 为什么 save() 方法会使 TacoOrder 对象的 taco 列表无效?
我试图拯救 tacoOrder 及其后续的炸玉米饼。但是,在保存时,它给了我验证错误,即使不应该出现这样的验证错误,因为它不为空。我认为存储库的 save() 方法正在使 Taco Order 对象的 Taco 列表无效。 当该方法尝试将 tacoOrder 保存到订单存储库中时,会出现错误。 问题是 jpa 正在创建一个包含空条目的新 taco 列表,而不是添加数据库中 TacoORder 对象中当前存在的 taco 列表。 我找不到它实际上在哪里做到这一点
这就是保存成分的方式,以防有帮助:(我在应用程序启动之前使用成分存储库预加载成分数据库)
@ModelAttribute
public void addIngredientsToModel(Model model){
Iterable<Ingredient> tempo = ingredientRepository.findAll();
List<Ingredient> ingredients = new ArrayList<>();
tempo.forEach(ingredients::add);
Ingredient.Type[] types = Ingredient.Type.values();
for(Ingredient.Type type: types){
model.addAttribute(type.toString().toLowerCase(),filterByType(ingredients , type));
}
}
从你的问题中调试问题并不容易。哪一行代码引发了相关异常?
您可以尝试的一件事:当您使用
@Transactional
注释方法时,您不需要显式调用 repository.save()
方法。相反,只需将 TacoOrder
添加到 User
中,然后让 @Transactional
完成其余的工作,而不调用任何 .save()
方法。然而,这需要 CascadeType.ALL
和 User
之间的 TacoOrder
,但这感觉很合理。