Spring事务方法内增查异常:插入后查询对象为null的问题咨询
咱们先把事务的工作机制讲明白,再分析你遇到的equalsSameDto为null的问题,最后给你合法的使用方式。
一、方法内嵌套事务的工作逻辑(Spring环境下)
你用的@Transactional是Spring提供的注解,它默认的事务传播行为是REQUIRED——简单来说就是:
- 如果当前已经存在一个事务,被调用的带
@Transactional的方法会直接加入到这个已有的事务中,不会新开独立事务; - 如果当前没有事务,才会开启一个全新的事务。
放到你的代码场景里:
Service层的insert(SameDto same)加了@Transactional,调用Dao层的sameDao.insert(same)时,Dao层的@Transactional并不会新开事务,两者共用同一个事务上下文。
二、为什么equalsSameDto会是null?
按道理说,同一个事务内插入数据后立即查询,应该能拿到结果,但你遇到了null,大概率是这几个原因:
1. 插入操作没有正确返回主键
这是最常见的情况。iBatis默认不会自动返回自增主键,需要在mapper配置里明确设置。如果mapper.insert(other)返回的primaryKey是无效值(比如null、0或者错误数值),那后续用这个主键查询自然会得到null。
2. 事务传播行为被意外修改
如果Dao层的@Transactional被配置了REQUIRES_NEW传播行为,那它会新开一个独立事务,但这个新事务在未提交的情况下,原Service层的事务是看不到新事务里的插入数据的。不过默认是REQUIRED,这个情况概率较低,但可以排查下。
3. 极端隔离级别影响
虽然同一个事务内的修改对自身是可见的,但如果数据库隔离级别被设置为SERIALIZABLE(极端场景),可能会有特殊锁机制影响,但这种情况非常少见,一般默认隔离级别(比如MySQL的REPEATABLE READ)不会出现这个问题。
三、合法的使用方式与解决方案
针对你的场景,给你几个可行的优化方向:
1. 简化事务配置,去掉Dao层的@Transactional
Dao层的方法一般不需要单独加事务,因为Service层已经开启了事务,Dao层方法会自动加入到这个事务中。多余的@Transactional不仅没用,还可能造成混淆。
2. 确保iBatis插入时正确返回主键
检查你的insert mapper配置,以MySQL为例,需要添加useGeneratedKeys和keyProperty属性:
<insert id="insert" parameterType="SameDto" useGeneratedKeys="true" keyProperty="id"> INSERT INTO your_table (col1, col2) VALUES (#{col1}, #{col2}) </insert>
这样插入后,iBatis会把生成的主键值赋值给SameDto对象的id属性,同时mapper.insert(other)也会返回这个主键值(部分版本依赖keyProperty获取)。
3. 验证主键是否正确
在插入后立即打印primaryKey的值,确认它是符合预期的有效主键:
BigDecimal primaryKey= mapper.insert(other); System.out.println("生成的主键:" + primaryKey); // 检查这个值是否正确 SameDto equalsSameDto = mapper.getByPrimaryKey(primaryKey);
4. 避免在事务内调用外部服务
你的代码里在事务内调用了webserice.call(same),如果这个外部服务需要读取刚插入的数据,那在当前事务未提交前,外部服务是查不到的。如果必须调用,建议把外部服务的调用移到事务提交之后,或者使用分布式事务(但分布式事务复杂度较高,非必要不推荐)。
总结
你遇到的equalsSameDto为null,最可能的原因是插入时主键没有正确返回。先排查主键返回的配置和值,再简化事务配置,基本就能解决问题啦。
内容的提问来源于stack exchange,提问作者Jiri Sikora

