Java操作MySQL更新表前判断指定日期是否存在的问题求解
问题排查与解决
两个版本的核心问题
第一版代码问题
- COUNT聚合查询无论有没有匹配记录,都会返回一行结果,所以
rs.next()永远返回true,!rs.next()永远为false,逻辑完全和实际是否存在数据无关。
第二版代码问题
- MySQL JDBC默认返回的
ResultSet是TYPE_FORWARD_ONLY类型,仅支持顺序向下遍历,调用rs.first()会直接抛出SQLException,你代码中只在catch里打印错误没有做业务处理,异常触发后直接跳过插入逻辑,执行后续的重定向返回,所以永远不会触发插入操作。
正确实现方案
方案1:修复现有JDBC逻辑
修改ResultSet读取部分的代码,同时补全资源自动关闭逻辑,代码如下:
@PostMapping("/add") public Object processMenuAddForm(@ModelAttribute @Valid Menu menu, Errors errors, HttpServletRequest request, Model model) throws ParseException, SQLException { User user = getUserFromSession(request.getSession()); if (errors.hasErrors()) { model.addAttribute("title", "Add"); return "/menu/add"; } // Connection也放进try-with-resources自动关闭,避免连接泄漏 try (Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/family_organizer", "family_organizer", "LiftOff2021"); PreparedStatement checkDateExists = con.prepareStatement( "SELECT COUNT(date) AS count FROM menu WHERE user_id = ? AND date = ?")) { checkDateExists.setInt(1, user.getId()); checkDateExists.setString(2, menu.getDate()); try (ResultSet rs = checkDateExists.executeQuery()) { // 去掉rs.first(),COUNT查询一定有一行,直接调用rs.next()移动指针到第一行即可 if (rs.next() && rs.getInt("count") == 0) { Menu newMenu = new Menu(menu.getDate(), menu.getMainCourse(), menu.getVegetable(), menu.getMainSide(), menu.getAdditionalSide(), menu.getDessert(), user); menuRepository.save(newMenu); } } catch (SQLException err) { // 建议打印完整堆栈方便排查,不要只打印错误信息 err.printStackTrace(); } return "redirect:/menu/view"; } }
方案2:更符合Spring生态的实现(推荐)
你已经使用了Spring Data JPA的menuRepository,完全不需要自己手写JDBC代码,直接在Repository接口中添加统计方法即可:
- 给MenuRepository添加查询方法:
public interface MenuRepository extends JpaRepository<Menu, Integer> { long countByUserIdAndDate(Integer userId, String date); }
- 简化业务层代码:
@PostMapping("/add") public Object processMenuAddForm(@ModelAttribute @Valid Menu menu, Errors errors, HttpServletRequest request, Model model) { User user = getUserFromSession(request.getSession()); if (errors.hasErrors()) { model.addAttribute("title", "Add"); return "/menu/add"; } if (menuRepository.countByUserIdAndDate(user.getId(), menu.getDate()) == 0) { Menu newMenu = new Menu(menu.getDate(), menu.getMainCourse(), menu.getVegetable(), menu.getMainSide(), menu.getAdditionalSide(), menu.getDessert(), user); menuRepository.save(newMenu); } return "redirect:/menu/view"; }
额外优化建议
- 可以直接给
menu表添加user_id + date的唯一联合索引,数据库层面即可避免重复数据插入,业务代码可补充唯一索引冲突的处理逻辑,实现双重保障。 - 不要硬编码数据库连接配置,放到Spring的application配置文件中,通过DataSource注入获取连接,更易维护也能避免连接泄漏。
内容的提问来源于stack exchange,提问作者Coder2021
相关产品推荐
相关产品推荐

