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

Hibernate中Cart实体cart_id无默认值报错及关联问题排查

Hibernate+Spring Data JPA 保存Cart实体的系列问题与解决

问题1:保存Cart时提示Field 'cart_id' doesn't have a default value

调用cartRepository.save(someNewCart)保存新Cart实体时触发java.sql.SQLException,提示cart_id字段无默认值。但Cart实体的id字段已标注@Id和@GeneratedValue(strategy = GenerationType.IDENTITY),数据库carts表的cart_id是AUTO_INCREMENT主键。尝试过设置字段默认值、重建数据库、调整生成策略等方法,均未解决。

问题2:修改CartItem后出现Column 'cart_id' cannot be null,双向关联触发栈溢出

为CartItem实体添加@JoinColumn(name = "cart_id")注解后,出现java.sql.SQLIntegrityConstraintViolationException,提示cart_id不能为空。按照双向关联方案设置Cart与CartItem的关联关系时,触发StackOverflowError,错误栈显示Cart与CartItem的hashCode方法循环调用。


相关代码

Cart实体

@Entity
@Table(name = "carts")
@Data
public class Cart {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cart_id")
    private Long id;

    @Column(name = "orderTrackingNumber")
    private String orderTrackingNumber;

    @Column(name = "package_price")
    private BigDecimal package_price;

    @Column(name = "party_size")
    private int party_size;

    @Column(name = "status")
    private StatusType status;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
    private Set<CartItem> cartItems;

    @Column(name = "create_date")
    private Date createdAt;
    @Column(name = "last_update")
    private Date lastUpdate;
}

CartRepository

@RepositoryRestResource(collectionResourceRel = "cart", path = "cart")
@CrossOrigin("http://localhost:4200")
public interface CartRepository extends JpaRepository<Cart, Long> {
    @Query("SELECT c FROM Cart c WHERE c.id = (SELECT MAX(cc.id) FROM Cart cc)")
    Cart findCartWithHighestId();
}

数据库carts表创建语句

CREATE TABLE `carts` (
    `cart_id` bigint NOT NULL AUTO_INCREMENT,
    -- 其他字段
    PRIMARY KEY (`cart_id`)
);

修改后的CartItem实体

@Entity
@Table(name = "cart_items")
@Data
public class CartItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cart_item_id")
    private Long id;

    @OneToOne
    private Vacation vacation;

    @ManyToMany
    private Set<Excursion> excursions;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "cart_id") // 新增该行
    private Cart cart;
}

双向关联代码

for (CartItem item : cartItemsFromPurchase) {
    cart.addItem(item);
    item.setCart(cart);
}

栈溢出错误栈

java.lang.StackOverflowError: null
    at java.base/java.util.HashSet.iterator(HashSet.java:174) ~[na:na]
    at java.base/java.util.AbstractSet.hashCode(AbstractSet.java:120) ~[na:na]
    at com.example.myproj.entity.Customer.hashCode(Customer.java:22) ~[classes/:na]
    at com.example.myproj.entity.Cart.hashCode(Cart.java:26) ~[classes/:na]
    at com.example.myproj.entity.CartItem.hashCode(CartItem.java:21) ~[classes/:na]
    at java.base/java.util.AbstractSet.hashCode(AbstractSet.java:124) ~[na:na]
    at com.example.myproj.entity.Cart.hashCode(Cart.java:26) ~[classes/:na]
    at com.example.myproj.entity.CartItem.hashCode(CartItem.java:21) ~[classes/:na]

解决方案

1. 解决cart_id无默认值问题

  • 检查Hibernate方言配置:确保配置文件中使用对应数据库的正确方言,例如MySQL 8.x需配置spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect。方言不匹配会导致IDENTITY生成策略失效,Hibernate无法识别数据库的自增特性。
  • 移除CartItem的级联配置:CartItem的@ManyToOne注解中添加了cascade = CascadeType.ALL,会导致保存CartItem时尝试级联保存未生成id的Cart,进而触发cart_id为空错误。将其改为@ManyToOne(去掉cascade),因为Cart的@OneToMany已经配置了cascade = CascadeType.ALL,足以处理级联保存逻辑。
  • 验证数据库自增特性:手动插入一条数据到carts表,确认cart_id字段能自动生成值,排除数据库层面的配置问题。

2. 解决双向关联栈溢出问题

  • 问题根源:使用Lombok的@Data注解会自动生成包含关联对象的hashCode、equals和toString方法,当Cart和CartItem互相引用时,会触发循环调用导致栈溢出。
  • 修复方式:
    • 替换@Data为@Getter+@Setter,并手动排除关联字段的toString、hashCode和equals计算。
    • 在Cart实体的cartItems字段添加@ToString.Exclude和@EqualsAndHashCode.Exclude注解;在CartItem实体的cart字段添加同样的注解,避免循环引用。

修改后的示例代码:

Cart实体

@Entity
@Table(name = "carts")
@Getter
@Setter
@ToString
public class Cart {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cart_id")
    private Long id;

    // 其他字段保持不变...

    @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    private Set<CartItem> cartItems;

    // 规范的关联添加方法
    public void addItem(CartItem item) {
        if (cartItems == null) {
            cartItems = new HashSet<>();
        }
        cartItems.add(item);
        item.setCart(this);
    }
}

CartItem实体

@Entity
@Table(name = "cart_items")
@Getter
@Setter
@ToString
public class CartItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cart_item_id")
    private Long id;

    // 其他字段保持不变...

    @ManyToOne
    @JoinColumn(name = "cart_id")
    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    private Cart cart;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 10:21:00