使用@Transactional时HQL/SQL无法更新Oracle数据库记录问题
你遇到的核心问题是:在@Transactional注解下,Criteria更新能正常生效,但HQL/SQL更新仅打印语句却不实际修改数据库,手动添加事务管理又触发“不支持嵌套事务”的错误。这本质上是Spring事务管理与Hibernate Session的绑定逻辑,以及事务提交机制的问题。
1. 确认Spring事务配置的正确性
首先要确保你的Spring上下文已经正确配置了事务支持:
- 配置合适的事务管理器(如果用纯Hibernate,推荐
HibernateTransactionManager):@Bean public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) { HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(sessionFactory); return txManager; } - 开启事务注解驱动:
@EnableTransactionManagement // 注解方式 <!-- 或者XML配置方式 --> <tx:annotation-driven transaction-manager="transactionManager"/>
如果这两步缺失,@Transactional注解会直接失效,导致HQL更新的事务无法正常提交。
2. 检查HQL语句与实体类的映射一致性
控制台能打印SQL语句,说明Hibernate已经解析了你的HQL,但数据库没变化,大概率是HQL的属性名与实体类不匹配:
- 确认
SystemUserNotification实体类中,是否存在名为seen的属性?比如如果实体类里的属性是isSeen(对应getter为isSeen()),那HQL应该写成:update SystemUserNotification set isSeen = 1 where isSeen = 0
HQL是基于实体类属性而非数据库字段名编写的,这是很容易踩的坑。
3. 排查事务是否被意外回滚
@Transactional默认会在RuntimeException或Error发生时自动回滚事务。如果你的setNotificationsAsSeen()方法内部(或者调用链中)有被捕获的RuntimeException,会导致事务静默回滚:
- 检查方法中是否有类似的异常捕获逻辑:
try { // HQL执行代码 } catch (Exception e) { // 仅打印日志但不抛出异常 log.error("更新失败", e); }
这种情况下,事务会因为异常被捕获而无法触发回滚,但也不会提交。你可以在方法末尾添加日志,或者在事务提交后直接查询数据库,确认是否有回滚发生。
4. 移除手动事务管理代码
你手动添加的Transaction tx = getSession().beginTransaction();和tx.commit();是错误的,原因如下:
- Spring的
@Transactional已经为你开启了一个事务,并且将Hibernate Session与该事务绑定。 - Oracle数据库默认不支持嵌套事务,所以手动开启第二个事务会直接报错。
直接删除这部分代码,让Spring全权管理事务即可。
5. 强制Flush(可选测试方案)
虽然HQL的executeUpdate()是直接执行数据库操作,不需要手动flush,但如果Spring的事务提交时机有延迟,可以尝试在执行后手动同步Session与数据库:
@Override public Number setNotificationsAsSeen() { Query query = createHqlQuery("update SystemUserNotification set seen = 1 where seen = 0"); Number n = (Number) query.executeUpdate(); getSession().flush(); // 强制将操作同步到数据库 return n; }
这一步更多是用来验证问题,如果flush后生效,说明是Session缓存与数据库同步的问题,但正常业务场景下不需要额外添加这行代码。
- 先确认事务配置是否完整,
@Transactional是否被Spring正确代理(比如服务类是否被@Service注解,并且Spring扫描到了该类)。 - 检查HQL的属性名是否与实体类完全一致。
- 移除手动事务代码,运行方法后查看数据库是否有更新。
- 如果还是没生效,在方法前后添加日志,确认事务是否被提交,或者是否有异常抛出。
内容的提问来源于stack exchange,提问作者Allloush

