/** * Copyright (c) 2011-2014, hubin (jobob@qq.com). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus; import java.lang.reflect.Field; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeException; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; import com.baomidou.mybatisplus.entity.GlobalConfiguration; import com.baomidou.mybatisplus.entity.TableInfo; import com.baomidou.mybatisplus.enums.IdType; import com.baomidou.mybatisplus.mapper.MetaObjectHandler; import com.baomidou.mybatisplus.toolkit.IdWorker; import com.baomidou.mybatisplus.toolkit.MapUtils; import com.baomidou.mybatisplus.toolkit.StringUtils; import com.baomidou.mybatisplus.toolkit.TableInfoHelper; /** * <p> * 自定义 ParameterHandler 重装构造函数,填充插入方法主键 ID * </p> * * @author hubin * @Date 2016-03-11 */ public class MybatisDefaultParameterHandler extends DefaultParameterHandler { /** * @see org.apache.ibatis.mapping.BoundSql */ private static final Field additionalParametersField = getAdditionalParametersField(); private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; private final Object parameterObject; private BoundSql boundSql; private Configuration configuration; public MybatisDefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { super(mappedStatement, processBatch(mappedStatement, parameterObject), boundSql); this.mappedStatement = mappedStatement; this.configuration = mappedStatement.getConfiguration(); this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry(); this.parameterObject = parameterObject; this.boundSql = boundSql; } /** * 反射获取BoundSql中additionalParameters参数字段 * * @return * @see org.apache.ibatis.mapping.BoundSql */ private static Field getAdditionalParametersField() { try { Field additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters"); additionalParametersField.setAccessible(true); return additionalParametersField; } catch (NoSuchFieldException e) { // ignored, Because it will never happen. } return null; } /** * <p> * 批量(填充主键 ID) * </p> * * @param ms * @param parameterObject 插入数据库对象 * @return */ protected static Object processBatch(MappedStatement ms, Object parameterObject) { boolean isFill = false; // 全局配置是否配置填充器 MetaObjectHandler metaObjectHandler = GlobalConfiguration.getMetaObjectHandler(ms.getConfiguration()); /* 只处理插入或更新操作 */ if (ms.getSqlCommandType() == SqlCommandType.INSERT) { isFill = true; } else if (ms.getSqlCommandType() == SqlCommandType.UPDATE && metaObjectHandler.openUpdateFill()) { isFill = true; } if (isFill) { Collection<Object> parameters = getParameters(parameterObject); if (null != parameters) { List<Object> objList = new ArrayList<>(); for (Object parameter : parameters) { TableInfo tableInfo = TableInfoHelper.getTableInfo(parameter.getClass()); if (null != tableInfo) { objList.add(populateKeys(metaObjectHandler, tableInfo, ms, parameter)); } else { /* * 非表映射类不处理 */ objList.add(parameter); } } return objList; } else { TableInfo tableInfo = TableInfoHelper.getTableInfo(parameterObject.getClass()); return populateKeys(metaObjectHandler, tableInfo, ms, parameterObject); } } return parameterObject; } /** * <p> * 处理正常批量插入逻辑 * </p> * <p> * org.apache.ibatis.session.defaults.DefaultSqlSession$StrictMap 该类方法 * wrapCollection 实现 StrictMap 封装逻辑 * </p> * * @param parameter 插入数据库对象 * @return */ @SuppressWarnings({"rawtypes", "unchecked"}) protected static Collection<Object> getParameters(Object parameter) { Collection<Object> parameters = null; if (parameter instanceof Collection) { parameters = (Collection) parameter; } else if (parameter instanceof Map) { Map parameterMap = (Map) parameter; if (parameterMap.containsKey("collection")) { parameters = (Collection) parameterMap.get("collection"); } else if (parameterMap.containsKey("list")) { parameters = (List) parameterMap.get("list"); } else if (parameterMap.containsKey("array")) { parameters = Arrays.asList((Object[]) parameterMap.get("array")); } } return parameters; } /** * <p> * 自定义元对象填充控制器 * </p> * * @param metaObjectHandler 元数据填充处理器 * @param tableInfo 数据库表反射信息 * @param ms MappedStatement * @param parameterObject 插入数据库对象 * @return Object */ protected static Object populateKeys(MetaObjectHandler metaObjectHandler, TableInfo tableInfo, MappedStatement ms, Object parameterObject) { if (null == tableInfo || StringUtils.isEmpty(tableInfo.getKeyProperty()) || null == tableInfo.getIdType()) { /* 不处理 */ return parameterObject; } /* 自定义元对象填充控制器 */ MetaObject metaObject = ms.getConfiguration().newMetaObject(parameterObject); if (ms.getSqlCommandType() == SqlCommandType.INSERT) { if (tableInfo.getIdType().getKey() >= 2) { Object idValue = metaObject.getValue(tableInfo.getKeyProperty()); /* 自定义 ID */ if (StringUtils.checkValNull(idValue)) { if (tableInfo.getIdType() == IdType.ID_WORKER) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getId()); } else if (tableInfo.getIdType() == IdType.UUID) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.get32UUID()); } } } // 插入填充 if (metaObjectHandler.openInsertFill()) { metaObjectHandler.insertFill(metaObject); } } else if (ms.getSqlCommandType() == SqlCommandType.UPDATE && metaObjectHandler.openUpdateFill()) { // 更新填充 metaObjectHandler.updateFill(metaObject); } return metaObject.getOriginalObject(); } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public void setParameters(PreparedStatement ps) { // 反射获取动态参数 Map<String, Object> additionalParameters = null; try { additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql); } catch (IllegalAccessException e) { // ignored, Because it will never happen. } ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue // #448 // ask // first // for // additional // params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); if (value == null && MapUtils.isNotEmpty(additionalParameters)) { // issue #138 value = additionalParameters.get(propertyName); } } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } } }