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
相关产品推荐
相关产品推荐

