Spring事务结合Hibernate时子对象未存入数据库问题咨询
哈哈,这个坑我踩过!我来给你捋捋问题出在哪以及怎么解决:
核心原因:Session实例不匹配
你手动用session.beginTransaction()时,全程操作的是同一个Session实例:自己从SessionFactory获取,开启事务,保存父子对象,所以级联配置Cascade=ALL能正常触发。
但用Spring @Transactional时,Spring会自动帮你创建并绑定一个Session到当前事务上下文。如果你的DAO里是用sessionFactory.openSession()获取Session,那你拿到的是全新的、和Spring事务无关的Session——你在这个新Session里操作父对象,而Spring最终提交的是它自己管理的那个Session的事务,两者完全不搭,子对象自然不会被级联保存。
具体解决方案
1. 改用getCurrentSession()获取Session
把DAO里获取Session的代码从sessionFactory.openSession()改成sessionFactory.getCurrentSession()。这个方法会直接拿到Spring事务上下文绑定的Session,确保所有操作都在同一个事务Session里执行:
// 修正后的DAO代码片段 public void saveStudent(Student student) { Session session = sessionFactory.getCurrentSession(); // 关键改动 session.save(student); }
2. 配置Hibernate支持Spring管理的CurrentSession
在你的Hibernate配置文件(比如hibernate.cfg.xml)里添加以下配置,让Hibernate使用Spring的Session上下文管理器:
<property name="hibernate.current_session_context_class"> org.springframework.orm.hibernate5.SpringSessionContext </property>
(如果是Hibernate4,把路径改成org.springframework.orm.hibernate4.SpringSessionContext即可)
3. 确保@Transactional加在业务层方法上
不要把@Transactional直接加在DAO层方法上,Spring的事务代理是基于业务层的,放在DAO层容易出现代理失效(比如内部方法调用不触发事务)。正确的做法是在Service层的业务方法上添加注解:
@Service public class StudentServiceImpl implements StudentService { @Autowired private StudentDao studentDao; @Transactional // 加在这里 @Override public void createStudentWithMarks(Student student, Marks marks) { student.setMarks(marks); // 一定要关联子对象到父对象 marks.setStudent(student); // 双向关联的话也要设置 studentDao.saveStudent(student); } }
4. 检查对象关联是否正确
别漏了把子对象关联到父对象上!比如student.setMarks(marks),如果没有这一步,Hibernate根本不知道父对象关联了子对象,级联保存自然不会触发。
额外排查技巧
- 开启Spring事务日志:把
org.springframework.transaction的日志级别设为DEBUG,能看到事务的开启、提交,以及Session绑定的详细过程,方便定位问题。 - 检查事务管理器配置:确保
HibernateTransactionManager正确注入了SessionFactory,比如:
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
内容的提问来源于stack exchange,提问作者Sayyed Noman

