我们有个功能大意是这样的:
有个实体,对应的表中有个ID 列是从 SEQUENCE 里取值的,本地页面操作导致数据的插入就从 SEQUENCE 取值赋给 ID。如果是第三方通过 REST 操作本应用资源导致的数据插入(此时的数据的ID列是有值的,但是能够保证绝不会和本地的 ID 值重复。),接口应当也要满足。即需要持久层对应用透明。
我们使用的是 MyBatis 做 ORM 的,所以 insert 语句使用了 <selectKey>,写法如下:
<selectKey keyProperty="id" order="BEFORE" resultType="long">
select ADVANCED_VARIABLE_SEQ.nextval from dual
</selectKey>
我们就在 selectKey 块里面加上判断,如果 ID 有值,就直接使用,不从 SEQUENCE 里拿。
<insert id="insert" parameterType="com.hebta.vinci.model.AdvancedVariable">
<selectKey keyProperty="id" order="BEFORE" resultType="long">
<choose>
<when test="id == null">
select ADVANCED_VARIABLE_SEQ.nextval from dual
</when>
<when test="id != null">
select #{id,jdbcType=BIGINT} from dual
</when>
</choose>
</selectKey>
insert into ADVANCED_VARIABLE (ID, ADVANCED_ANALYSIS_ID, VAR_NAME,
VAR_TYPE, QUERY, NOTE)
values (#{id,jdbcType=BIGINT}, #{advancedAnalysisId,jdbcType=BIGINT}, #{varName,jdbcType=VARCHAR},
#{varType,jdbcType=VARCHAR}, #{query,jdbcType=VARCHAR}, #{note,jdbcType=VARCHAR})
</insert>
这个对单个的 insert 语句是管用的。但是对于 batchInsert 语句,其写法和原理如 mybatis使用foreach批次插入,解决sequence只查询一次的问题 所说,我们无法在 <foreach> 块外获得当前的 ID 列值。带有 SEQUENCE 的 batchInsert 的简单写法如下也是一样。
<insert id="insertAll" parameterType="java.util.List">
insert into COHORT_VARIABLE
select COHORT_VAR_SEQUENCE.NEXTVAL, A.* from (
<foreach collection="list" index="index" item="item" separator="UNION">
SELECT
#{item.advancedAnalysisId,jdbcType=BIGINT},
#{item.varName,jdbcType=VARCHAR},
#{item.varType,jdbcType=VARCHAR},
#{item.query,jdbcType=VARCHAR},
#{item.note,jdbcType=VARCHAR}
from dual
</foreach> ) A
</insert>
有个解决办法,就是在业务层判断一下List 里面的对象的 ID 是否有值,没有就使用 SEQUENCE 插入,否则就用普通的 batchInsert 语句。