Spring OneToMany CRUD项目添加课程遇User空指针及Session异常求助
问题背景
我是Spring新手,正在搭建一个OneToMany关联的CRUD项目,数据库包含user和course表,course表的user_id外键关联user表的id字段。用户登录成功后进入homePage.jsp,页面展示该用户的课程列表;点击“Add Course”按钮跳转至course-form.jsp进行课程添加操作。目前遇到的问题是:添加课程时Spring MVC处理过程中丢失user模型属性,提交addedCourse表单时出现错误。
错误信息
Cannot invoke "com.veg.entity.User.addCourse(com.veg.entity.Course)" because "user" is null
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
代码实现
Controller代码
@PostMapping("/showFormForAdd") public String showFormForAdd(@ModelAttribute("user") User theUser, Model theModel) { Course theCourse = new Course(); theCourse.setUser(theUser); theModel.addAttribute("course", theCourse); theModel.addAttribute("user", theUser); return "course-form"; } @PostMapping("addedCourse") public String addedCourse(@ModelAttribute("course") Course theCourse, Model theModel) { User user = theCourse.getUser(); userService.save(user, theCourse); theModel.addAttribute("user", user); return "homePage"; }
homePage.jsp代码
<p>Welcome ${user.username}</p> <span> <form:form action="showFormForAdd" modelAttribute="user" method="POST"> <form:hidden path="username"/> <form:hidden path="password"/> <input type="submit" value="Add Course" class="add-course"/> </form:form> </span> <table> <tbody> <tr> <th>Course id</th> <th>Course title</th> </tr> <c:forEach var="temp" items="${user.courses}"> <tr> <td> ${temp.id} </td> <td> ${temp.title} </td> </tr> </c:forEach> </tbody> </table>
course-form.jsp代码
<p>Welcome ${user.username}</p> <span> <form:form action="loggedUser" modelAttribute="user" method="POST"> <form:hidden path="username"/> <form:hidden path="password"/> <input type="submit" value="Back to Course List" class="add-course"/> </form:form> </span> <!-- In the below I lost my user modelAttribute because I should use course attribute --> <form:form action="addedCourse" modelAttribute="course" method="POST"> <table> <tbody> <tr> <td> Title </td> <td> <form:input path="title"/> </td> </tr> <tr> <td colspan="2"> <input type="submit" value="Add Course" class="add-course"/> </td> </tr> </tbody> </table> </form:form>
已完成的排查动作
- Service层已添加
@Transactional注解 - DAO层添加了
@Repository注解 - SessionFactory配置无异常
问题修复方案
核心问题分析
- User对象丢失:course-form.jsp中提交course的表单未携带User的标识信息,Spring无法自动将User绑定到Course对象中,导致
theCourse.getUser()返回null。 - 事务Session问题:即使传递了User信息,表单提交的User是脱离Hibernate Session的游离对象,直接操作可能引发Session管理异常。
具体修复步骤
1. 在course表单中添加User的隐藏字段
在course-form.jsp的course提交表单中,加入User的唯一标识字段(优先用id,避免用户名/密码重复问题),确保提交时Spring能将User信息绑定到Course的user属性:
<form:form action="addedCourse" modelAttribute="course" method="POST"> <!-- 添加User的id隐藏字段,用于绑定Course关联的User --> <form:hidden path="user.id"/> <table> <tbody> <tr> <td> Title </td> <td> <form:input path="title"/> </td> </tr> <tr> <td colspan="2"> <input type="submit" value="Add Course" class="add-course"/> </td> </tr> </tbody> </table> </form:form>
2. 修改Controller的addedCourse方法,重新加载托管状态的User
表单传递的User是游离态对象,需要从数据库重新查询获取Hibernate托管的User实例,避免Session异常:
@PostMapping("addedCourse") public String addedCourse(@ModelAttribute("course") Course theCourse, Model theModel) { // 通过User的id从数据库重新加载,确保是Hibernate托管状态 User user = userService.findById(theCourse.getUser().getId()); // 关联课程并保存 user.addCourse(theCourse); userService.save(user); theModel.addAttribute("user", user); return "homePage"; }
3. 配置实体类的级联保存
在User实体类的@OneToMany注解中添加级联配置,确保保存User时自动保存关联的Course:
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List<Course> courses;
4. 确认事务注解驱动配置
确保Spring配置中开启了事务注解支持:
- XML配置:
<tx:annotation-driven transaction-manager="transactionManager"/> - Spring Boot:在配置类上添加
@EnableTransactionManagement注解
内容的提问来源于stack exchange,提问作者ravenharpy

