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

