You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot购物车无法保存Item列表至数据库问题排查

问题:Spring Boot中Cart的Item列表无法保存到数据库

开发首个Spring Boot应用,需求是向购物车(Cart)添加Item列表,但Item列表无法保存到数据库。添加test字符串字段测试,该字段可正常更新,说明问题仅针对Item列表。控制台日志显示Item已添加到Cart对象,但数据库无对应记录。

相关代码

Account类

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Account {
    @GeneratedValue
    @Id
    private Long id;
    @Column(nullable = false)
    private String name;
    @Column(unique = true, nullable = false)
    private String email;
    @Column(length = 1000, nullable = false)
    private String password;

    private LocalDateTime createdOn;
    {
        createdOn = LocalDateTime.now();
    }
    private LocalDateTime lastUpdateOn;

    @JsonManagedReference
    @OneToOne(cascade = CascadeType.PERSIST)
    private Cart cart;

    // Constructor for Seeder
    public Account(String name, String email, String password) {
        this.cart = new Cart();
        this.name = name;
        this.email = email;
        this.password = password;
        this.createdOn = LocalDateTime.now();
    }
}

Cart类

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Cart {
    @GeneratedValue
    @Id
    private Long id;

    @JsonBackReference
    @OneToOne
    private Account account;

    private LocalDateTime createdOn;
    {
        createdOn = LocalDateTime.now();
    }
    private LocalDateTime lastUpdateOn;

    @JsonManagedReference
    @OneToMany(mappedBy = "cart")
    private List<Item> items;

    private String test;
}

Item类

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Item {
    @GeneratedValue
    @Id
    private Long id;

    private LocalDateTime createdOn;
    {
        createdOn = LocalDateTime.now();
    }
    private LocalDateTime lastUpdateOn;

    @JsonBackReference
    @ManyToOne
    @JoinColumn(name = "cart_id")
    private Cart cart;

    @ManyToOne
    @JoinColumn(name = "game_id")
    private Game game;
    private Integer quantity;

    public Item(Game game, Integer quantity) {
        this.game = game;
        this.quantity = quantity;
    }
}

Cart控制器

@RequestMapping("/carts")
@RestController
public class CartController {
    @Autowired
    CartService cartService;

    @GetMapping
    public List<Cart> getAllCarts() {
        return cartService.getCarts();
    }

    @GetMapping("/{id}")
    public Cart getCartById(@PathVariable Long id) {
        return cartService.getCartById(id);
    }

    @PutMapping("/add/{id}")
    public ResponseEntity<Cart> addItemsToCart(@PathVariable Long id, @RequestBody List<Item> items) {
            if (cartService.updateCartContents(id, items)) {
                return ResponseEntity.status(HttpStatus.OK).build();
            } else {
                return ResponseEntity.status(HttpStatus.CONFLICT).build();
            }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Cart> deleteCart(@PathVariable Long id) {
        if (cartService.deleteCart(id)) {
            return ResponseEntity.status(HttpStatus.OK).build();
        } else {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }
    }

    @PutMapping("clearcart/{id}")
    public ResponseEntity<Cart> clearCart(@PathVariable Long id){
        if (cartService.clearCart(id)){
            return ResponseEntity.status(HttpStatus.OK).build();
        } else {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }
    }

    @PutMapping("removeitem/{id}")
    public ResponseEntity<Cart> removeCartItems(@PathVariable Long id, @RequestBody Item item){
        if (cartService.removeCartItem(id,item)){
            return ResponseEntity.status(HttpStatus.OK).build();
        } else {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }
    }

    // 结算:将购物车转为订单并清空购物车
    @PutMapping("checkout/{id}")
    public ResponseEntity<Cart> checkout(@PathVariable Long id){
        try{
            cartService.checkout(id);
            return ResponseEntity.status(HttpStatus.OK).build();
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }
    }
}

Cart服务层

@Service
public class CartService {
    @Autowired
    CartRepository cartRepository;
    @Autowired
    ItemRepository itemRepository;
    @Autowired
    PurchaseRepository purchaseRepository;

    public List<Cart> getCarts(){
        return cartRepository.findAll();
    }

    public Cart getCartById(Long id){
        return cartRepository.findById(id).orElseThrow();
    }

    public boolean updateCartContents(Long id, List<Item> items){
        Optional<Cart> optionalCart = cartRepository.findById(id);
        if (optionalCart.isPresent()) {
            Cart cart = optionalCart.get();
            cart.getItems().addAll(items);
            System.out.println("cart items before saving: " + cart.getItems());
            cartRepository.save(cart);
            System.out.println("cart items after saving: " + cart.getItems());
            return true;
        } else {
            return false;
        }
    }

    public boolean deleteCart(Long id){
        if(cartRepository.findById(id).isPresent()){
            cartRepository.deleteById(id);
            return true;
        }else{
            return false;
        }
    }

    public boolean clearCart(Long id){
        if(cartRepository.findById(id).isPresent()){
            Cart cart = cartRepository.findById(id).orElseThrow();
            cart.getItems().clear();
            cartRepository.save(cart);
            return true;
        }else{
            return false;
        }
    }

    public boolean removeCartItem(Long id, Item itemToRemove){
        if(cartRepository.findById(id).isPresent()){
            Cart cart = cartRepository.findById(id).orElseThrow();
            cart.getItems().remove(itemRepository.findById(itemToRemove.getId()).orElseThrow());
            cartRepository.save(cart);
            return true;
        }else{
            return false;
        }
    }

    public void checkout(Long id) throws IllegalArgumentException{
        Cart cart = cartRepository.findById(id).orElseThrow(IllegalArgumentException::new);
        //Purchase purchase = new Purchase();
        //purchase.setPurchasedItems(cart.getItems());
        //purchaseRepository.save(purchase);
        clearCart(id);
    }
}

控制台日志

cart items before saving: [Item{id=1, createdOn=2024-05-05T12:49:57.343516400, lastUpdateOn=null, cart=null, game=null, quantity=null}, Item{id=2, createdOn=2024-05-05T12:49:57.344485400, lastUpdateOn=null, cart=null, game=null, quantity=null}, Item{id=3, createdOn=2024-05-05T12:49:57.344485400, lastUpdateOn=null, cart=null, game=null, quantity=null}]
cart items after saving: [Item{id=1, createdOn=2024-05-05T12:49:53.806481, lastUpdateOn=null, cart=null, game=com.united.Gamestore.Game.Game@2f5cee8, quantity=1}, Item{id=2, createdOn=2024-05-05T12:49:53.809481, lastUpdateOn=null, cart=null, game=com.united.Gamestore.Game.Game@7cedf75c, quantity=1}, Item{id=3, createdOn=2024-05-05T12:49:53.811481, lastUpdateOn=null, cart=null, game=com.united.Gamestore.Game.Game@6f5d6a00, quantity=1}]

问题原因

  1. 关联维护端限制:Cart类中@OneToMany(mappedBy = "cart")指定Item是双向关联的维护端,只有维护端(Item的cart属性)的变更才会同步到数据库,仅更新Cart的items列表不会触发数据库操作。
  2. 未建立Item与Cart的关联:从日志可见,添加的Item对象cart属性为null,数据库无法建立外键关联,导致Item无法被持久化。
  3. 缺少级联配置:Cart保存时未配置级联操作,无法自动处理关联Item的持久化逻辑。

解决方案

1. 修改Cart类的关联注解

给items字段添加级联配置并初始化列表,避免空指针:

@JsonManagedReference
@OneToMany(mappedBy = "cart", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true)
private List<Item> items = new ArrayList<>();
  • cascade = {CascadeType.PERSIST, CascadeType.MERGE}:让Cart的保存/更新操作级联到关联的Item
  • orphanRemoval = true:当Item从Cart的items列表中移除时,自动删除数据库中对应记录
  • 初始化items为new ArrayList<>():避免调用addAll时出现NullPointerException

2. 修改CartService的updateCartContents方法

添加Item到Cart时,手动设置每个Item的cart属性为当前Cart:

public boolean updateCartContents(Long id, List<Item> items){
    Optional<Cart> optionalCart = cartRepository.findById(id);
    if (optionalCart.isPresent()) {
        Cart cart = optionalCart.get();
        items.forEach(item -> {
            item.setCart(cart); // 建立Item与Cart的关联
            cart.getItems().add(item);
        });
        System.out.println("cart items before saving: " + cart.getItems());
        cartRepository.save(cart);
        System.out.println("cart items after saving: " + cart.getItems());
        return true;
    } else {
        return false;
    }
}

3. 可选方案:直接保存Item

若不想使用级联,可直接通过ItemRepository保存Item并设置关联的Cart:

public boolean updateCartContents(Long id, List<Item> items){
    Optional<Cart> optionalCart = cartRepository.findById(id);
    if (optionalCart.isPresent()) {
        Cart cart = optionalCart.get();
        items.forEach(item -> {
            item.setCart(cart);
            itemRepository.save(item); // 直接保存Item
        });
        return true;
    } else {
        return false;
    }
}

内容的提问来源于stack exchange,提问作者jade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:50:56