mybatis批量插入、批量更新常规写法,及升级写法
null value in column “xxx” violates not-null constraint mybatis批量操作报错问题处理。
常规写法:
<insert id="insertUser" parameterType="com.test.UserEntity">
insert into t_com_user(user_name, age, gender)
values
<foreach collection ="list" item="item" index="index" open="(" close= ")" separator= "),(">
#{item.userName, jdbcType=VARCHAR},
#{item.age, jdbcType=INTEGER},
#{item.gender, jdbcType=INTEGER}
foreach>
insert>
假如我们在批量插入数据的时候,还想做一下关联查询,该怎么办?
如下写法,免去了先查询数据,然后遍历list赋值,再批量插入的操作
<insert id="insertUser" parameterType="com.test.UserEntity">
insert into t_com_user(user_name, age, gender, dept_name)
select
t1.user_name, t1.age, t1.gender, t2.dept_name
from (
VALUES
<foreach collection ="list" item="item" index="index" open="(" close= ")" separator= "),(">
#{item.userName, jdbcType=VARCHAR},
#{item.age, jdbcType=INTEGER},
#{item.gender, jdbcType=INTEGER},
#{item.deptId, jdbcType=INTEGER}
foreach>
) as t1 (user_name, age, gender, dept_id)
left_join t_com_dept t2 on t1.dept_id = t2.id
insert>
常规写法
这种写法实际执行过程是单条执行,即使使用的是id查找数据,但是效率较差。
<update id="updateUser" parameterType="com.test.UserEntity">
<foreach collection ="list" item="item" index="index" separator= ";">
update t_com_user set
user_name = #{item.userName, jdbcType=VARCHAR},
age = #{item.age, jdbcType=INTEGER},
gender = #{item.gender, jdbcType=INTEGER}
where id = #{item.id, jdbcType=INTEGER}
foreach>
update>
升级写法:参考批量插入操作,使用连表更新的语法
<update id="updateUser" parameterType="com.test.UserEntity">
update t_com_user t1 set
user_name = t2.user_name,
age = t2.age,
gender = t2.gender
from (
values
<foreach collection ="list" item="item" index="index" open="(" close= ")" separator= "),(">
#{item.id, jdbcType=INTEGER},
#{item.userName, jdbcType=VARCHAR},
#{item.age, jdbcType=INTEGER},
#{item.gender, jdbcType=INTEGER}
foreach>
) as t2 (id, user_name, age, gender)
where t1.id = t2.id
update>
mybatis的批量操作过程,传入的list对象集合中,对象的某个属性难免会为null,如果数据库表该列恰好有not-null限制,则会报错;处理办法,本文以pgsql的批量更新为例,亲测可行:
方法一:
使用COALESCE(#{item.userName, jdbcType=VARCHAR}, ‘默认名称’)
方式二:user_name = COALESCE(t2.user_name, ‘默认名称’)
二选一即可
<update id="updateUser" parameterType="com.test.UserEntity">
update t_com_user t1 set
user_name = COALESCE(t2.user_name, '默认名称'),
age = t2.age,
gender = t2.gender
from (
values
<foreach collection ="list" item="item" index="index" open="(" close= ")" separator= "),(">
#{item.id, jdbcType=INTEGER},
COALESCE(#{item.userName, jdbcType=VARCHAR}, '默认名称'),
#{item.age, jdbcType=INTEGER},
#{item.gender, jdbcType=INTEGER}
foreach>
) as t2 (id, user_name, age, gender)
where t1.id = t2.id
update>
mysql 使用IFNULL函数 pgsql 使用COALESCE函数
方法二:
在foreach中使用if标签判断
<if test= "item.userName != null">
#{item.userName, jdbcType=VARCHAR}
if>
<if test= "item.userName == null">
'默认名称'
if>
Oracle mysql下,不支持 from (value (),()) as t 的写法;可以参考
select * from (
select 1, '张三' from dual union
select 2, '李四' from dual
) t
当mybatis做批量插入时,插入的字段可能没值,此时不做处理的话,mybatis会报异常,执行失败
根据mybatis的官网介绍,此时需要添加对应的jdbcType类型映射,以处理null值 ,具体的映射关系参考:jdbc映射关系表
此时,在所有可能为空的字段取值中添加jdbcType=XXX(一般全部添加即可)
1.使用union all 来串连每个values,其中jdbcType的设置可以使null值也输入进去
<insert id="saveList" parameterType="java.util.List">
INSERT INTO DDZHPT.CMS_SCHEDUAL_DETIAL
(
DEPT_ID,
SCHEDUAL_DATE,
CMS_SCHEDUAL_TYPE_ID,
CMS_SCHEDUAL_TEAM_ID,
CMS_SCHEDUAL_TYPE_PERIOD_ID,
CMS_SCHEDUAL_TIME_ID,
SYS_POST_ID,
POINT_ID,
PERSON_ID
)
<foreach collection="list" item="item" index="index" separator="union all">
SELECT
#{item.deptId,jdbcType=DECIMAL},
#{item.schedualDate,jdbcType=TIMESTAMP},
#{item.cmsSchedualTypeId,jdbcType=VARCHAR},
#{item.cmsSchedualTeamId,jdbcType=VARCHAR},
#{item.cmsSchedualTypePeriodId,jdbcType=VARCHAR},
#{item.cmsSchedualTimeId,jdbcType=VARCHAR},
#{item.sysPostId,jdbcType=VARCHAR},
#{item.pointId,jdbcType=VARCHAR},
#{item.personId,jdbcType=VARCHAR}
FROM DUAL
foreach>
insert>
2.纯粹使用foreach
<insert id="saveList" parameterType="java.util.List">
<foreach collection="list" item="item" index="index" separator="union all">
INSERT INTO DDZHPT.CMS_SCHEDUAL_DETIAL
(
DEPT_ID,
SCHEDUAL_DATE,
CMS_SCHEDUAL_TYPE_ID,
CMS_SCHEDUAL_TEAM_ID,
CMS_SCHEDUAL_TYPE_PERIOD_ID,
CMS_SCHEDUAL_TIME_ID,
SYS_POST_ID,
POINT_ID,
PERSON_ID
)VALUES(
#{item.deptId,jdbcType=DECIMAL},
#{item.schedualDate,jdbcType=TIMESTAMP},
#{item.cmsSchedualTypeId,jdbcType=VARCHAR},
#{item.cmsSchedualTeamId,jdbcType=VARCHAR},
#{item.cmsSchedualTypePeriodId,jdbcType=VARCHAR},
#{item.cmsSchedualTimeId,jdbcType=VARCHAR},
#{item.sysPostId,jdbcType=VARCHAR},
#{item.pointId,jdbcType=VARCHAR},
#{item.personId,jdbcType=VARCHAR}
)
foreach>
insert>
推荐使用第一种方式,数据库语句只有一条,减少数据库执行语句的负担
<sql id="insertAllCol">
<trim prefix="(" suffix=")" suffixOverrides=",">
FPH,
EFFECTIVE_TAX_AMOUNT,
PURCHASER_TAXNO,
INVOICE_STATE,
DEDUCTIBLE_MODE,
AMOUNT,
OVERDUE_CHECK_MARK,
ABNORMAL_TYPE,
NSRSBH,
ANTI_FAKE_CODE,
UPDATE_TIME,
DEDUCTIBLE_PERIOD,
AGENCY_DRAWBACK,
RESALE_CERTIFICATE_NUMBER,
INVOICE_NO,
CREATE_TIME,
INV_ISSUE_DATE,
TAX,
AUDIT_STATE,
DEDUCTIBLE_TYPE,
DEDUCTIBLE_DATE,
MANAGEMENT_STATUS,
SALES_TAXNAME,
DEDUCTIBLE_STATE,
FLOW_ID,
INVOICE_CATAGORY,
SALES_TAXNO,
INVOICE_CODE,
ORIGINAL_PERIOD,
INFO_SOURCES,
trim>
sql>
<sql id="insertAllValueWithItem" databaseId="oracle">
<trim prefix=" SELECT " suffix=" FROM dual " suffixOverrides=",">
#{item.fph,jdbcType=VARCHAR},
#{item.effectiveTaxAmount,jdbcType=NUMERIC},
#{item.purchaserTaxno,jdbcType=VARCHAR},
#{item.invoiceState,jdbcType=DATE},
#{item.deductibleMode,jdbcType=VARCHAR},
#{item.amount,jdbcType=NUMERIC},
#{item.overdueCheckMark,jdbcType=VARCHAR},
#{item.abnormalType,jdbcType=VARCHAR},
#{item.nsrsbh,jdbcType=VARCHAR},
#{item.antiFakeCode,jdbcType=VARCHAR},
#{item.updateTime,jdbcType=DATE},
#{item.deductiblePeriod,jdbcType=VARCHAR},
#{item.agencyDrawback,jdbcType=VARCHAR},
#{item.resaleCertificateNumber,jdbcType=VARCHAR},
#{item.invoiceNo,jdbcType=VARCHAR},
#{item.createTime,jdbcType=DATE},
#{item.invIssueDate,jdbcType=VARCHAR},
#{item.tax,jdbcType=NUMERIC},
#{item.auditState,jdbcType=NUMERIC},
#{item.deductibleType,jdbcType=VARCHAR},
#{item.deductibleDate,jdbcType=VARCHAR},
#{item.managementStatus,jdbcType=VARCHAR},
#{item.salesTaxname,jdbcType=VARCHAR},
#{item.deductibleState,jdbcType=VARCHAR},
#{item.flowId,jdbcType=NUMERIC},
#{item.invoiceCatagory,jdbcType=VARCHAR},
#{item.salesTaxno,jdbcType=VARCHAR},
#{item.invoiceCode,jdbcType=VARCHAR},
#{item.originalPeriod,jdbcType=VARCHAR},
#{item.infoSources,jdbcType=VARCHAR},
trim>
sql>
注:union all和union的区别
union all连接查询,结果不去重
union做连接查询结果去重