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

购物车删除最后一项异常:@GetMapping接口未触发问题排查

问题

删除购物车最后一项商品时,@GetMapping("remove-song-from-cart")接口完全没被调用,但删除之前的商品时,这个接口能正常触发并返回购物车页面,怀疑问题出在这个GET接口的实现上。

相关代码

Cart 实体类

package musicstore.musicselling.Entity;

import java.util.HashSet;
import java.util.Set;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;

@Entity
public class Cart {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long cartId;

    @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
    private Set<CartItem> cartItems = new HashSet<>();

    public Long getCartId() {
        return cartId;
    }

    public Set<CartItem> getCartItems() {
        return cartItems;
    }

    public void setCartItems(Set<CartItem> cartItems) {
        this.cartItems = cartItems;
    }

    public void addCartItems(CartItem item) {
        cartItems.add(item);
        item.setCart(this);
    }

    public void removeCartItems(CartItem item) {
        cartItems.remove(item);
        item.setCart(null);
    }

}

CartItem 实体类

package musicstore.musicselling.Entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;

@Entity
public class CartItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long itemId;

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

    @ManyToOne
    @JoinColumn(name = "songId")
    private Song song;

    @ManyToOne
    @JoinColumn(name = "albumId")
    private Album album;

    public CartItem() {
    }

    public Long getItemId() {
        return itemId;
    }

    public Song getSong() {
        return song;
    }

    public void setSong(Song song) {
        this.song = song;
    }

    public Album getAlbum() {
        return album;
    }

    public void setAlbum(Album album) {
        this.album = album;
    }

    public Cart getCart() {
        return cart;
    }

    public void setCart(Cart cart) {
        this.cart = cart;
    }

}

CartController 控制器

package musicstore.musicselling.Controller;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.ui.Model;
import musicstore.musicselling.Entity.*;
import musicstore.musicselling.Repository.AlbumRepository;
import musicstore.musicselling.Repository.CartItemRepository;
import musicstore.musicselling.Repository.CartRepository;
import musicstore.musicselling.Repository.SongRepository;

@Controller
public class CartController {
    @Autowired
    SongRepository songRepository;

    @Autowired
    AlbumRepository albumRepository;

    @Autowired
    CartRepository cartRepository;

    @Autowired
    private CartItemRepository cartItemRepository;

    private Cart getOrCreateCart() {
        if (cartRepository.findByCartId(1L) == null) {
            Cart cart = new Cart();
            return cartRepository.save(cart);
        }
        return cartRepository.findByCartId(1L);

    }

    // add the song to the cart
    @GetMapping("/add-song-to-cart")
    public String addSongToCart(@RequestParam Long songId) {
        Cart cart = getOrCreateCart();
        Song song = songRepository.findBySongId(songId);
        CartItem item = new CartItem();
        item.setSong(song);
        cart.addCartItems(item);
        cartRepository.save(cart);
        System.out.println("Add success");

        return "redirect:/song-list";
    }

    // push the data of cart list to the cart page
    @GetMapping("/cart")
    public String songCart(Model model) {
        Cart cart = getOrCreateCart();
        double price = 0;
        List<Song> songItems = new ArrayList<>();
        for (CartItem cartItem : cart.getCartItems()) {
            if (cartItem.getSong() != null) {
                songItems.add(cartItem.getSong());
                price = price + cartItem.getSong().getSongPrice();
            }
        }

        String totalPrice = String.format("%.2f", price);
        model.addAttribute("totalPrice", totalPrice);
        model.addAttribute("songItems", songItems);
        System.out.println("cart item size: " + cart.getCartItems().size());
        for (CartItem cartItem : cart.getCartItems()) {
            System.out.println("Song id: " + cartItem.getSong().getSongId());
        }

        return "cart";
    }

    // remove the song from cart
    @GetMapping("remove-song-from-cart")
    public String removeSongFromCart(@RequestParam Long songId) {
        Cart cart = getOrCreateCart();
        Set<CartItem> cartList = cart.getCartItems();
        System.out.println("The fucking cart size: " + cartList.size());
        CartItem itemToRemove = null;

        for (CartItem item : cartList) {
            if (item.getSong().getSongId().equals(songId)) {
                itemToRemove = item;

            }
        }
        if (itemToRemove != null) {
            cart.removeCartItems(itemToRemove);
            cartRepository.save(cart);

        }

        System.out.println("item to remove: " +
                itemToRemove.getSong().getSongName());

        return "redirect:/cart";

    }

}

修复方案

  1. 统一接口路径格式
    把删除接口的映射改成@GetMapping("/remove-song-from-cart")(开头加斜杠),确保和其他接口的路径格式一致,避免前端相对路径解析出错导致请求发不出去。

  2. 修复空指针异常
    删除最后一项时,如果itemToRemove为null(比如购物车已被清空但前端仍显示按钮),直接调用itemToRemove.getSong()会抛出空指针,导致请求失败。修改代码:

    if (itemToRemove != null) {
        cart.removeCartItems(itemToRemove);
        cartRepository.save(cart);
        System.out.println("item to remove: " + itemToRemove.getSong().getSongName());
    } else {
        System.out.println("未找到要删除的商品");
    }
    
  3. 检查前端渲染逻辑
    确认购物车为空时,前端是否还渲染了删除按钮,或者按钮的点击事件是否绑定正常。如果购物车为空时按钮还存在,但实际没有对应的商品ID,请求会无效。

  4. 验证JPA持久化操作
    删除后打印cart.getCartItems().size(),确认集合已为空;同时查看数据库中CartItem表的记录是否被删除,确保orphanRemoval = true配置生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:57:35