mybatis批量插入,批量更新以及null值解决方案

前言

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>

ERROR: null value in column “user_name” violates not-null constraint mybatis 批量操作报错问题处理

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

null 原因

当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>

推荐使用第一种方式,数据库语句只有一条,减少数据库执行语句的负担

3.union与union all区别
在这里插入图片描述

 <!--插入所有列清单-->
    <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做连接查询结果去重

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌兮~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值