保存实体时 spring jpa 中的约束冲突

问题描述 投票:0回答:1

在 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));
        }
    }
java spring hibernate spring-mvc spring-data-jpa
1个回答
0
投票

从你的问题中调试问题并不容易。哪一行代码引发了相关异常?

您可以尝试的一件事:当您使用

@Transactional
注释方法时,您不需要显式调用
repository.save()
方法。相反,只需将
TacoOrder
添加到
User
中,然后让
@Transactional
完成其余的工作,而不调用任何
.save()
方法。然而,这需要
CascadeType.ALL
User
之间的
TacoOrder
,但这感觉很合理。

© www.soinside.com 2019 - 2024. All rights reserved.