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

Hibernate使用HQL插入单条数据时重复插入多条问题解决方法

问题根因

重复插入的核心原因是你写的HQL逻辑完全错误,和session.close()是否调用、事务配置没有关系:

  • 你用的INSERT INTO ... SELECT ... FROM Product语法是批量复制语法,因为SELECT段没有加任何WHERE条件,每次执行都会把Product表中现存的所有记录全量查询出来,再重新插入到同一张表中。只要表里有N条数据,执行一次就会新增N条重复数据,表内数据量会指数级翻倍,和你单次调用只传1条参数的预期完全不符。
  • 整个方法里你根本没有使用传入的product参数,不管前端传什么商品信息,执行的都是全表复制逻辑,等于你写的插入方法完全没有用到入参。
  • 你现在用的result>1就抛异常的临时方案本质是“先执行插入、插入多了再回滚”,并没有从根源阻止错误SQL的执行,而且异常类型用NullPointerException完全不符合语义,会给后续问题排查造成干扰。
  • 代码没有加异常捕获和finally块,一旦SQL执行报错,事务不会主动回滚,Session也可能无法正常关闭,会残留数据库连接。
修复方案

业务要求不使用Hibernate原生save()方法的前提下,直接修改HQL为参数绑定的单条插入语法即可,不要用SELECT子查询拉取全表数据。

修复后代码
@Override
public Product save(Product product) {
    Transaction transaction = null;
    Session session = null;
    try {
        session = HibernateUtil.getSessionFactory().openSession();
        transaction = session.beginTransaction();

        // 替换原全表复制HQL,直接绑定入参做单条插入
        String hql = "INSERT INTO Product (productname, productbrand, productprice, productdescription, productquantity, productexpirationdate) " +
                "VALUES (:productname, :productbrand, :productprice, :productdescription, :productquantity, :productexpirationdate)";
        Query query = session.createQuery(hql);
        // 绑定传入的product对象属性作为插入值
        query.setParameter("productname", product.getProductname());
        query.setParameter("productbrand", product.getProductbrand());
        query.setParameter("productprice", product.getProductprice());
        query.setParameter("productdescription", product.getProductdescription());
        query.setParameter("productquantity", product.getProductquantity());
        query.setParameter("productexpirationdate", product.getProductexpirationdate());
        
        int affectRows = query.executeUpdate();
        // 单条插入正常返回1,不符合预期直接回滚
        if (affectRows != 1) {
            transaction.rollback();
            throw new RuntimeException("商品插入失败,影响行数异常");
        }

        transaction.commit();
        return product;
    } catch (Exception e) {
        if (transaction != null) {
            transaction.rollback();
        }
        throw new RuntimeException("商品插入流程异常", e);
    } finally {
        // 无论执行成功失败,都保证Session正常关闭
        if (session != null && session.isOpen()) {
            session.close();
        }
    }
}
兜底优化建议
  • 目前你的tb_product表只有主键自增的唯一约束,没有业务层面的防重限制,建议给能唯一确定商品的字段组合(比如商品名称+品牌+保质期)加联合唯一索引,从数据库层面彻底挡住重复数据插入,哪怕代码逻辑出问题也不会生成重复脏数据。
  • 不要用NullPointerException做业务流程控制,这类异常是JVM用来标记空指针错误的,业务校验失败应该抛对应的业务异常,避免排查问题时被误导。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 10:09:18