使用JPA(MySQL)无法删除主键为0的数据库条目
无法通过JPA删除主键为0的Element记录,但直接SQL可执行成功
尝试删除主键为0的Element条目时操作无效果,已删除所有关联表,仅保留element表仍无法删除该条目,其他主键的行可正常删除。在Sqlectron中执行delete from element where id = 0;能成功删除,但用JPA却完成不了删除操作。
实体类代码
@Entity @Table(name = "element") @NamedEntityGraph(name = graph.element.accounts, attributeNodes = @NamedAttributeNode("accounts")) public class Element extends Entry { @Id @Column(name = "id") private int id; @Column(name = "name", nullable = false, unique = true) @NotBlank private String name; @OneToMany(mappedBy = "element", cascade = CascadeType.PERSIST) private List<Account> accounts; // constructor, getters, setters, equals, hashcode }
服务类代码
@Service public class ElementService { private final ElementDAO DAO; @Autowired public ElementService(ElementDAO DAO) { this.DAO = DAO; } public Element find(int id) { return DAO.findById(id).orElse(null); } public void delete(Element element) { DAO.delete(element); } }
DAO接口
@Repository public interface ElementDAO extends PagingAndSortingRepository<Element,Integer> { }
测试代码
@SpringBootTest @ActiveProfiles("test") class ElementServiceTests { @Autowired private ElementService svc; @Test public void test() { svc.delete(new Element(0, "Other")); assertNull(svc.find(0)); } }
测试报错
org.opentest4j.AssertionFailedError: Expected :null Actual :Element{id=0, name='Other'}
数据插入语句
INSERT INTO element (id, name, created_at, created_by) VALUES (1, 'Asset', CURRENT_DATE, 'DBA'), (2, 'Liability', CURRENT_DATE, 'DBA'), (3, 'Equity', CURRENT_DATE, 'DBA'), (4, 'Income', CURRENT_DATE, 'DBA'), (5, 'Expense', CURRENT_DATE, 'DBA'), (0, 'Other', CURRENT_DATE, 'DBA');
问题原因
核心问题在于JPA对int类型主键为0的实体的判断逻辑:
int类型的默认值是0,JPA会把id=0的实体判定为未被持久化的新实体,而非数据库中已存在的实体。调用delete(entity)方法时,JPA只会对已持久化(即存在于持久化上下文或数据库中)的实体执行删除操作,所以这里根本不会触发SQL删除语句。
解决方案
提供三种可行的解决方式,任选其一即可:
1. 将主键类型从int改为Integer
把实体类中的id字段类型替换为Integer,这样0会被视为一个有效的非空主键值,JPA会正确识别这是数据库中已存在的实体:
@Id @Column(name = "id") private Integer id; // 替换int为Integer
2. 先查询实体再删除
修改测试代码,先通过find方法获取数据库中的实体,再执行删除操作,确保JPA操作的是已持久化的实例:
@Test public void test() { Element element = svc.find(0); if (element != null) { svc.delete(element); } assertNull(svc.find(0)); }
3. 使用deleteById方法直接删除
利用Spring Data JPA提供的deleteById方法,直接通过主键ID删除,绕过实体状态判断的问题:
- 首先在ElementService中新增方法:
public void deleteById(int id) { DAO.deleteById(id); }
- 然后修改测试代码:
@Test public void test() { svc.deleteById(0); assertNull(svc.find(0)); }
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

