Spring JDBC事务抛出RuntimeException日志显示回滚但数据仍持久化问题
interface DaoLayer { Long createFavouriteWidgetInfo(FavouriteWidgetInfo favouriteWidget); }
class DaoLayerImpl implements DaoLayer @Transactional @Override public Long createFavouriteWidgetInfo(FavouriteWidgetInfo favouriteWidget) { Session session=sessionFactory.getCurrentSession(); Long val= (Long) session.save(favouriteWidget); throw new NullPointerException(); //return val; }
@SpringBootTest @RunWith(SpringJUnit4ClassRunner.class) public class Test { @Autowired private DaoLayer daoLayer; @org.junit.Test public void test()throws Exception { FavouriteWidgetInfo info=new FavouriteWidgetInfo(); info.setGroupId(1); info.setReportId(2); info.setUserId(213); info.setTenantId(21); info.setApplicationId(2); daoLayer.createFavouriteWidgetInfo(info); } }
运行上述测试用例时,因方法主动抛出属于RuntimeException子类的NullPointerException,按照Spring事务规则预期事务应正常回滚。开启Spring事务日志后确实可以看到回滚相关的日志输出,但查询数据库时发现插入的实体数据仍被持久化,并未完成回滚。
备注:当前使用的是Hibernate current_session context的Spring实现类SpringSessionContext,会话的创建完全由Spring框架管理。
按优先级从高到低排查即可解决:
数据库存储引擎不支持事务
这是最常见的原因,比如MySQL的MyISAM引擎本身不支持事务,所有SQL执行后会直接持久化,就算Spring触发了回滚逻辑也不会生效。
修复:执行SQLshow create table 你的表名确认存储引擎,将MyISAM改为支持事务的InnoDB即可。@Transactional注解使用错误
误用了javax.transaction.Transactional注解,该注解的默认回滚规则与Spring原生注解不同,不会自动回滚RuntimeException。
修复:将注解替换为org.springframework.transaction.annotation.Transactional。数据源自动提交配置未关闭
如果数据源的autoCommit属性设置为true,SQL执行后会立刻提交到数据库,后续事务回滚逻辑无法生效。
修复:在数据源配置中将autoCommit设为false,同时确认HibernateTransactionManager已经正确注入对应的SessionFactory实例。事务传播属性配置错误
如果@Transactional配置了Propagation.NOT_SUPPORTED、Propagation.NEVER这类非事务运行的传播属性,方法会直接脱离事务运行,自然不会触发回滚。
修复:确认传播属性为默认的Propagation.REQUIRED即可。
内容的提问来源于stack exchange,提问作者S Manikandan

