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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 15:36:08