mybatis批量插入返回主键解决方案

解决方案有两种

首次写博客,不喜勿喷。

方案一
   升级mybatis版本到3.3.1以上, 升级版本可能会出现问题,比如mapper.xml中集合和字符串做比较会出现异常,如以下xml片段:
<if test="underPersons != '' underPersons != null">
     and user.id in
   <foreach collection="underPersons" item="userid">
        #{userid}
   </foreach>
</if>

调用方法出现错误提示:mybatis invalid comparison: java.util.arraylist and java.lang.string

這个方案方便快捷,但前提是mapper.xml写的内容规范


方案二
修改mybatis源码,原理暂时没有时间写,先给方案吧,方案如下:
  • 自定义一个KeyGenerator类继承 org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator,重写processAfter 方法,如下:
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    List<Object> parameters = new ArrayList<Object>();
    if(parameter instanceof Map){
        @SuppressWarnings("unchecked")
        Map<String,List<Object>> map=(Map<String, List<Object>>) parameter;
        parameters=map.get("list");
    }else{
        parameters.add(parameter);
    }

    processBatch(ms, stmt, parameters);
  }
  • 自定义一个mybatis拦截器,修改MappedStatement的KeyGenerator,实现代码以及xml配置如下:
package com.calm.erp.pageutil;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@Intercepts({ @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }) })
public class PageInterceptor implements Interceptor {
    private static Logger LOGGER = LoggerFactory.getLogger(PageInterceptor.class);
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();

    /**
     * 拦截后要执行的方法
     */
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation
                .getTarget();
        MetaObject metaStatementHandler = MetaObject.forObject(
                statementHandler, DEFAULT_OBJECT_FACTORY,
                DEFAULT_OBJECT_WRAPPER_FACTORY);
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler
                .getValue("delegate.mappedStatement");

        //针对批量插入不返回主键问题做修改,修改mappedStatement的keyGenerator为自定义的keyGenerator
        if( ArrayUtils.isNotEmpty(mappedStatement.getKeyProperties()) && SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())){
            //重点就在这   利用java反射修改mappedStatement的keyGenerator,修改为我们自定义的Jdbc3KeyGenerator
            ReflectUtil.setFieldOjectValue(mappedStatement, "keyGenerator", new Jdbc3KeyGenerator());
        }

        return invocation.proceed();

    }


    /**
     * 利用反射进行操作的一个工具类
     * 
     */
    private static class ReflectUtil {

        /**
         * 利用反射获取指定对象里面的指定属性
         * 
         * @param obj
         *            目标对象
         * @param fieldName
         *            目标属性
         * @return 目标字段
         */
        private static Field getField(Object obj, String fieldName) {
            Field field = null;
            for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz
                    .getSuperclass()) {
                try {
                    field = clazz.getDeclaredField(fieldName);
                    break;
                } catch (NoSuchFieldException e) {
                    // 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
                }
            }
            return field;
        }

        /**
         * 利用反射设置指定对象的指定属性为指定的值 值为object对象
         * 
         * @param obj
         *            目标对象
         * @param fieldName
         *            目标属性
         * @param fieldValue
         *            目标值
         */
        public static void setFieldOjectValue(Object obj, String fieldName,
                Object fieldValue) {
            Field field = ReflectUtil.getField(obj, fieldName);
            if (field != null) {
                try {
                    field.setAccessible(true);
                    field.set(obj, fieldValue);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    LOGGER.error("" ,e);
                }
            }
        }
    }

}
  • mybatis配置xml,很简单在configuration加入以下配置:
<plugins>
        <plugin interceptor="com.calm.erp.pageutil.PageInterceptor" />
    </plugins>
  • 批量插入按以下方式写便可返回主键id到参数中
<insert id="batchAddCommercial" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
        insert into erp_crm_commercial
        (dept_id,commercial_name,sales_status,
        commercial_address,memo,source,source_first,status,update_time,updator_id,updator_name,create_time,creator_id,
        creator_name,regist_time,business_duty,sales_id,divert_user_id,commercial_intention,city_id,city_name,province_id,
        province_name,dist_id,dist_name,advisory_thing,fault_type,assigned_state,business_type,assigned_way) values
        <foreach collection="list" index="index" item="item" separator=",">
        ( #{item.deptId},#{item.commercialName},#{item.salesStatus},#{item.commercialAddress},#{item.memo},#{item.source},#{item.sourceFirst},0,now(),#{item.updatorId},#{item.updatorName},
         now(),#{item.creatorId},#{item.creatorName},#{item.registTime},#{item.businessDuty},#{item.salesId},#{item.divertUserId},#{item.commercialIntention},#{item.cityId},#{item.cityName},#{item.provinceId},
         #{item.provinceName},#{item.distId},#{item.distName},#{item.advisoryThing},#{item.faultType},#{item.assignedState},#{item.businessType},#{item.assignedWay})
        </foreach>
    </insert>

其实第二种方式有一个非常简单的办法,只是不怎么优雅,也说说吧,写一个和源码一模一样的类,包路径也和源码保持一致,只需要修改processAfter方法,和上面的一样,java会优先使用我们自己写的类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值