场景
我们平常工作中一般都会有这样一个场景,我往数据库插入一条记录,如果该条记录已经存在,那么只需要更新;如果不存在,则直接插入。通常的做法就是,在插入这条数据之前,我先去数据库里查下该记录是否存在,然后再决定我是做insert操作还是update操作。这样一来,数据库就操作了两次,有木有更好的办法?
我接到一个任务,将一个list集合里面的数据一次性插入到A表里,并且如果A表有重复记录则更新;反之,则插入。很显然,这要用到批处理,并且我需要拿到list里的所有元素去跟数据库比对,刷选出哪些是重复,哪些是新增。这样实现的结果代码不简洁,性能也不高。
merge into 用法
有了merge into之后,事情就变得简单多了。
<insert id="insertOrUpdate" parameterType="cn.com.ft.meta.bean.NfxMetaYwb">
MERGE INTO Tab_A T1
USING (SELECT #{ywbId,jdbcType=VARCHAR} YWB_ID,
#{ywbms,jdbcType=VARCHAR} YWBMS,
#{cjmc,jdbcType=VARCHAR} CJMC,
#{fxdxlb,jdbcType=VARCHAR} FXDXLB,
#{cjId,jdbcType=VARCHAR} CJ_ID,
#{yxbz,jdbcType=VARCHAR} YXBZ
FROM dual) T2
on (T1.YWB_ID = T2.YWB_ID)
WHEN MATCHED THEN
UPDATE
SET T1.YWBMS = T2.YWBMS,
T1.CJMC = T2.CJMC,
T1.FXDXLB = T2.FXDXLB,
T1.CJ_ID = T2.CJ_ID,
T1.YXBZ = T2.YXBZ
WHEN NOT MATCHED THEN
INSERT
(T1.YWB_ID, T1.YWBMS, T1.CJMC, T1.FXDXLB, T1.CJ_ID, T1.YXBZ)
VALUES
(T2.YWB_ID, T2.YWBMS, T2.CJMC, T2.FXDXLB, T2.CJ_ID, T2.YXBZ)
</insert>
select语句就是查询list集合里的元素,采用 FROM dual 虚拟表的形式保证能从数据库查出一条记录来,其实查出来的记录就是参数list集合里的一个元素(T2来自list集合数据组成的临时表,T1是真实需要插入或更新的表)。通过on将两个表关联起来,后面如果重复,会执行update,将T2的数据更新到T1;如果是新记录,则执行insert,将T2的数据插入到T1。这样一来,我们不需要再去过多关注sql语句是insert还是update,只需要注重后台的调用即可。
后台批处理运用
public int saveFxys(String ywbId, List<FxysDto> fxysList) {
//需要在获得session的时候指定批处理模式
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
NfxMetaFxysMapper fxysMapper = session.getMapper(NfxMetaFxysMapper.class);
try {
if (!CollectionUtils.isEmpty(fxysList)) {
//每遍历一条记录,就组装一个insert语句
for (FxysDto fxysDto : fxysList) {
NfxMetaFxys nfxMetaFxys = new NfxMetaFxys();
nfxMetaFxys.setYsid(fxysDto.getId());
nfxMetaFxys.setYwbId(ywbId);
nfxMetaFxys.setYsdm(fxysDto.getCode());
... ...
//这里每遍历一条记录,就赋值给 merge into 那里的T2
fxysMapper.insertOrUpdate(nfxMetaFxys);
}
//最终一并提交事务
session.commit();
return 1;
}
} catch (Exception e) {
log.error(e.getMessage(), e);
e.printStackTrace();
session.rollback();
} finally {
if (session != null) {
session.close();
}
}
return 0;
}