java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

作者:Meta39

MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,需在需要动态数据(如Oracle序列、存储过程主键)时禁用缓存,避免缓存干扰查询结果

问题

mybatis有如下代码获取序列作为主键

IdMapper.java

    @Select("SELECT TABLE.NEXTVAL FROM DUAL")
    String getId();

同一事务循环调用查询伪代码

	@Transactional(rollbackFor = Exception.class)
	public QueryMedicalFeeRes queryMedicalFee(QueryMedicalFeeReq req) {
		for (Obj o : objs) {
			Long id = idMapper.getId();
			log.info("id{}",id);
		}
	}

输出如下:id都是1

id1
id1
id1
id1

原因

因为MyBatis的一级缓存导致的。而且MyBatis默认开启一级缓存,这样就会导致循环调用查询方法的时候,直接从缓存获取,不会查询数据库,从而获取到的数据都是缓存数据。

但请注意,并不是所有查询都不使用一级缓存!而是当你需要查询的内容是需要变化的时候才要禁用一级缓存。如:查询Oracle的序列、查询存储过程获取主键值等情况才需要禁用一级缓存。

解决办法

禁用一级缓存

<select id="getId" resultType="java.lang.Long" useCache="false" flushCache="true">
    SELECT TABLE.NEXTVAL AS ID FROM DUAL
</select>
    @Select("SELECT TABLE.NEXTVAL FROM DUAL")
    @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE)
    String getId();

如果是存储过程

<select id="getId" resultType="java.lang.Long" statementType="CALLABLE" useCache="false" flushCache="true">
    {call GET_NEXT_ID(#{identity,mode=IN,jdbcType=VARCHAR},#{tableName,mode=IN,jdbcType=VARCHAR},#{count,mode=IN,jdbcType=INTEGER},#{currentNo,mode=OUT,jdbcType=INTEGER})}
</select>
```java
    @Select("{call GET_NEXT_ID(#{identity,mode=IN,jdbcType=VARCHAR},#{tableName,mode=IN,jdbcType=VARCHAR},#{count,mode=IN,jdbcType=INTEGER},#{currentNo,mode=OUT,jdbcType=INTEGER})}")
    @Options(statementType = StatementType.CALLABLE, useCache = false, flushCache = Options.FlushCachePolicy.TRUE)
    String getId();

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文