使用自定义删除方法的 JPA 级联删除失败
JPA cascading delete fails with custom delete method
我 运行 在 spring 数据 jpa 中使用自定义删除方法时出错。基本上有一个包,里面装着物品,删除包的时候,里面的所有物品都要删除。
实体如下:
@Entity
@Table(name = "bag")
public class Bag {
@Id private Long id;
@Column("uid") private Long uid;
@Column("name") private String name;
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Item> items;
}
@Entity
@Table(name = "item")
public class Item {
@Id private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bid", referencedColumnName = "id")
private Bag bag;
}
和存储库:
@Repository
public interface BagRepository extends JpaRepository<Bag, Long> {
Bag findByUidAndName(Long uid, String name);
@Transactional
@Modifying
@Query(value = "DELETE FROM `bag` WHERE `uid` = :uid AND `name` = :name", nativeQuery = true)
void deleteByUidAndName(@Param("uid") Long uid, @Param("name") String name);
}
当我调用 bagRepository.deleteByUidAndName(uid, name)
时,我从休眠中得到一个与外键约束相关的异常。设置 spring.jpa.show-sql=true
表明它不会在删除包之前先尝试删除项目。
但是,如果我调用 Bag bag = bagRepository.findByUidAndName(uid, name)
然后 bagRepository.deleteById(bag.getId())
一切都很好。
我想知道自定义此删除方法有什么问题以及如何修复它。
如果通过 bagRepository.deleteById(bag.getId())
删除实体,Hibernate 将从父实体删除到子实体,因为您在关系上定义了 cascade = CascadeType.ALL。当我们对目标实体执行某些操作时,相同的操作将应用于关联的实体。
逻辑在 Hibernate 中,不使用数据库级联。
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Item> items;
如果 bagRepository.deleteByUidAndName(uid, name)
您定义了要删除的本机查询。这意味着将忽略 Hibernate 逻辑并执行查询 as-is。在这种情况下,您直接使用数据库并通过本机删除记录 SQL 您需要在数据库级别定义 ON DELETE CASCADE
以具有类似的逻辑。
@Query(value = "DELETE FROM `bag` WHERE `uid` = :uid AND `name` = :name", nativeQuery = true)
void deleteByUidAndName(@Param("uid") Long uid, @Param("name") String name);
解决方法一,@OnDelete(action = OnDeleteAction.CASCADE)
如果您有 auto-generated table,您可以向关系添加 Hibernate-specific 注释 @OnDelete
。在 table 生成期间 ON DELETE CASCADE
将应用于外键约束。
关系定义:
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Item> items;
自动生成的约束:
alter table item
add constraint FK19sn210fxmx43i8r3icevbeup
foreign key (bid)
references bag
on delete cascade
实施:
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "bag")
public class Bag {
@Id
private Long id;
@Column(name = "uid")
private Long uid;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Item> items;
}
方案二,@JoinColumn 注释带外键 ON DELETE CASCADE
用 ON DELETE CASCADE
为 Item
实体指定外键
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bid", referencedColumnName = "id",
foreignKey = @ForeignKey(
name="FK_ITEMS_ID",
foreignKeyDefinition = "FOREIGN KEY (ID) REFERENCES ITEM(BID) ON DELETE CASCADE"))
private Bag bag;
实施:
import javax.persistence.*;
@Entity
@Table(name = "item")
public class Item {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bid", referencedColumnName = "id",
foreignKey = @ForeignKey(
name="FK_ITEMS_ID",
foreignKeyDefinition = "FOREIGN KEY (ID) REFERENCES ITEM(BID) ON DELETE CASCADE"))
private Bag bag;
}
方案三,不使用原生查询
在这种情况下,将应用 Hibernate 逻辑。
像这样定义存储库:
@Repository
public interface BagRepository extends JpaRepository<Bag, Long> {
Bag findByUidAndName(Long uid, String name);
@Transactional
@Modifying
void deleteByUidAndName(@Param("uid") Long uid, @Param("name") String name);
}
方案四,手动添加ON DELETE CASCADE到数据库
如果您的 table 不是 auto-generated,您可以手动将 ON DELETE CASCADE
添加到数据库。
alter table item
add constraint FK_BAG_BID
foreign key (bid)
references bag
on delete cascade
我 运行 在 spring 数据 jpa 中使用自定义删除方法时出错。基本上有一个包,里面装着物品,删除包的时候,里面的所有物品都要删除。
实体如下:
@Entity
@Table(name = "bag")
public class Bag {
@Id private Long id;
@Column("uid") private Long uid;
@Column("name") private String name;
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Item> items;
}
@Entity
@Table(name = "item")
public class Item {
@Id private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bid", referencedColumnName = "id")
private Bag bag;
}
和存储库:
@Repository
public interface BagRepository extends JpaRepository<Bag, Long> {
Bag findByUidAndName(Long uid, String name);
@Transactional
@Modifying
@Query(value = "DELETE FROM `bag` WHERE `uid` = :uid AND `name` = :name", nativeQuery = true)
void deleteByUidAndName(@Param("uid") Long uid, @Param("name") String name);
}
当我调用 bagRepository.deleteByUidAndName(uid, name)
时,我从休眠中得到一个与外键约束相关的异常。设置 spring.jpa.show-sql=true
表明它不会在删除包之前先尝试删除项目。
但是,如果我调用 Bag bag = bagRepository.findByUidAndName(uid, name)
然后 bagRepository.deleteById(bag.getId())
一切都很好。
我想知道自定义此删除方法有什么问题以及如何修复它。
如果通过 bagRepository.deleteById(bag.getId())
删除实体,Hibernate 将从父实体删除到子实体,因为您在关系上定义了 cascade = CascadeType.ALL。当我们对目标实体执行某些操作时,相同的操作将应用于关联的实体。
逻辑在 Hibernate 中,不使用数据库级联。
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Item> items;
如果 bagRepository.deleteByUidAndName(uid, name)
您定义了要删除的本机查询。这意味着将忽略 Hibernate 逻辑并执行查询 as-is。在这种情况下,您直接使用数据库并通过本机删除记录 SQL 您需要在数据库级别定义 ON DELETE CASCADE
以具有类似的逻辑。
@Query(value = "DELETE FROM `bag` WHERE `uid` = :uid AND `name` = :name", nativeQuery = true)
void deleteByUidAndName(@Param("uid") Long uid, @Param("name") String name);
解决方法一,@OnDelete(action = OnDeleteAction.CASCADE)
如果您有 auto-generated table,您可以向关系添加 Hibernate-specific 注释 @OnDelete
。在 table 生成期间 ON DELETE CASCADE
将应用于外键约束。
关系定义:
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Item> items;
自动生成的约束:
alter table item
add constraint FK19sn210fxmx43i8r3icevbeup
foreign key (bid)
references bag
on delete cascade
实施:
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "bag")
public class Bag {
@Id
private Long id;
@Column(name = "uid")
private Long uid;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "bag", cascade = CascadeType.ALL, orphanRemoval = true)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Item> items;
}
方案二,@JoinColumn 注释带外键 ON DELETE CASCADE
用 ON DELETE CASCADE
为 Item
实体指定外键
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bid", referencedColumnName = "id",
foreignKey = @ForeignKey(
name="FK_ITEMS_ID",
foreignKeyDefinition = "FOREIGN KEY (ID) REFERENCES ITEM(BID) ON DELETE CASCADE"))
private Bag bag;
实施:
import javax.persistence.*;
@Entity
@Table(name = "item")
public class Item {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bid", referencedColumnName = "id",
foreignKey = @ForeignKey(
name="FK_ITEMS_ID",
foreignKeyDefinition = "FOREIGN KEY (ID) REFERENCES ITEM(BID) ON DELETE CASCADE"))
private Bag bag;
}
方案三,不使用原生查询
在这种情况下,将应用 Hibernate 逻辑。
像这样定义存储库:
@Repository
public interface BagRepository extends JpaRepository<Bag, Long> {
Bag findByUidAndName(Long uid, String name);
@Transactional
@Modifying
void deleteByUidAndName(@Param("uid") Long uid, @Param("name") String name);
}
方案四,手动添加ON DELETE CASCADE到数据库
如果您的 table 不是 auto-generated,您可以手动将 ON DELETE CASCADE
添加到数据库。
alter table item
add constraint FK_BAG_BID
foreign key (bid)
references bag
on delete cascade