优秀的编程知识分享平台

网站首页 > 技术文章 正文

Mybatis参数-ParameterMapping处理参数

nanyue 2024-12-18 16:03:19 技术文章 6 ℃

1.ParameterMapping的作用

ParameterHandler给PrepareStatement传递参数时,是从ParameterMapping获取参数值,然后通过下标方式给PrepareStatement设置参数

2.ParameterMapping创建过程

从DefaultParameterHandler的setParameters方法中,可以知道ParameterMapping是从BoundSql中通过getParameterMappings得到ParameterMapping的集合

BoundSql是通过SqlSourceBuilder调用parse方法解析sql时创建的,在方法中可以看到创建StaticSqlSource时会获取handler的ParameterMappings赋值给BoundSql

那么继续查看SqlSourceBuilder.ParameterMappingTokenHandler中是怎么创建ParameterMappings的

在SqlSourceBuilder的parse方法中,会创建一个SqlSourceBuilder.ParameterMappingTokenHandler对象和GenericTokenParser对象,而解析过程实际是调用GenericTokenParser的parse方法实现

GenericTokenParser的parse方法中会截取openToken和closeToken之间的字符放入expression中,然后调用handler的handleToken方法进行处理

GenericTokenParser初始化时传入的openToken和closeToken分别为“#{” 和 “}”,handler是SqlSourceBuilder.ParameterMappingTokenHandler的实例

SqlSourceBuilder.ParameterMappingTokenHandler的handleToken方法中将sql中#{}里面字符串通过buildParameterMapping方法处理后获得ParameterMapping对象,然后访入parameterMappings的集合中

而实际sql则用"?"替代原来的#{},进而可以通过PrepareStatement进行参数传递

在ParameterMappingTokenHandler的buildParameterMapping方法中,传入参数的名称,返回ParameterMapping对象

public class SqlSourceBuilder extends BaseBuilder {
    private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
		private ParameterMapping buildParameterMapping(String content) {
			//将字符串解析为map类型
            Map<String, String> propertiesMap = this.parseParameterMapping(content);
    		//从解析的Map中获取property的值
            String property = (String)propertiesMap.get("property");
			//获取propertyType的类对象
            Class propertyType;
			//1.从ObjectFactory中获取
            if (this.metaParameters.hasGetter(property)) {
                propertyType = this.metaParameters.getGetterType(property);
			//2.根据TypeHandler获取
            } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterType)) {
                propertyType = this.parameterType;
			//3.JdbcType.CURSOR处理
            } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
                propertyType = ResultSet.class;
			//4.Map形式参数处理
            } else if (property != null && !Map.class.isAssignableFrom(this.parameterType)) {
                MetaClass metaClass = MetaClass.forClass(this.parameterType, this.configuration.getReflectorFactory());
                if (metaClass.hasGetter(property)) {
                    propertyType = metaClass.getGetterType(property);
                } else {
                    propertyType = Object.class;
                }
            } else {
			//4.如果shangr情况不满足,则默认为Object对象
                propertyType = Object.class;
            }
			//根据Property和PropertyType创建Builder对象
            Builder builder = new Builder(this.configuration, property, propertyType);
            Class<?> javaType = propertyType;
            String typeHandlerAlias = null;
            Iterator var8 = propertiesMap.entrySet().iterator();
			//遍历上述的Map,根据值的类型给builder属性赋值
            while(var8.hasNext()) {
                Entry<String, String> entry = (Entry)var8.next();
                String name = (String)entry.getKey();
                String value = (String)entry.getValue();
                if ("javaType".equals(name)) {
                    javaType = this.resolveClass(value);
                    builder.javaType(javaType);
                } else if ("jdbcType".equals(name)) {
                    builder.jdbcType(this.resolveJdbcType(value));
                } else if ("mode".equals(name)) {
                    builder.mode(this.resolveParameterMode(value));
                } else if ("numericScale".equals(name)) {
                    builder.numericScale(Integer.valueOf(value));
                } else if ("resultMap".equals(name)) {
                    builder.resultMapId(value);
                } else if ("typeHandler".equals(name)) {
                    typeHandlerAlias = value;
                } else if ("jdbcTypeName".equals(name)) {
                    builder.jdbcTypeName(value);
                } else if (!"property".equals(name)) {
                    if ("expression".equals(name)) {
                        throw new BuilderException("Expression based parameters are not supported yet");
                    }
                    throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName");
                }
            }

            if (typeHandlerAlias != null) {
                builder.typeHandler(this.resolveTypeHandler(javaType, typeHandlerAlias));
            }
			//通过Builder创建ParameterMapping
            return builder.build();
        }
	}
}

2.1.将字符串解析为Map类型

ParameterMappingTokenHandler的parseParameterMapping方法中创建一个ParameterExpression对象,传入#{}中的字符串

public class SqlSourceBuilder extends BaseBuilder {
    private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
        private Map<String, String> parseParameterMapping(String content) {
            try {
                return new ParameterExpression(content);
            } catch (BuilderException var3) {
                throw var3;
            } catch (Exception var4) {
                throw new BuilderException("Parsing error was found in mapping #{" + content + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", var4);
            }
        }
    }
}

ParameterExpression继承与HashMap,主要是对字符串的处理,它会将字符串以“,”逗号和冒号分隔,第一个字符串以property属性存放,后面的字符串以“=”分隔,将=号前后的字符串以key-value的形式存放

如果属性后面接的是“:”冒号,则默认为jdbcType属性,不需要显示的填写jdbcType字符了

2.2.通过Builder创建ParameterMapping

然后根据配置获取各个属性的值,并赋值给builder,注意只能是以下这些类型“javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName”,否则会报错

如果配置了TypeHandler,则会根据javaType和TypeHandler去获取TypeHandler类并赋值,就是到typeHandlerRegistry中获取

最后调用Builder的build方法创建ParameterMapping对象,创建是使用了构造者模式,用来解决参数过多不好管理的问题

创建是首先会判断typeHandler是否为空,如果为空,也就是配置文件中没有指定。那么会根据javaType和jdbcType去typeHandlerRegistry中获取对应的TypeHandler

public class ParameterMapping {
    public static class Builder {
       public ParameterMapping build() {
            this.resolveTypeHandler();
            this.validate();
            return this.parameterMapping;
        }

        private void validate() {
            if (ResultSet.class.equals(this.parameterMapping.javaType)) {
                if (this.parameterMapping.resultMapId == null) {
                    throw new IllegalStateException("Missing resultmap in property '" + this.parameterMapping.property + "'.  Parameters of type java.sql.ResultSet require a resultmap.");
                }
            } else if (this.parameterMapping.typeHandler == null) {
                throw new IllegalStateException("Type handler was null on parameter mapping for property '" + this.parameterMapping.property + "'. It was either not specified and/or could not be found for the javaType (" + this.parameterMapping.javaType.getName() + ") : jdbcType (" + this.parameterMapping.jdbcType + ") combination.");
            }

        }

        private void resolveTypeHandler() {
            if (this.parameterMapping.typeHandler == null && this.parameterMapping.javaType != null) {
                Configuration configuration = this.parameterMapping.configuration;
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                this.parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(this.parameterMapping.javaType, this.parameterMapping.jdbcType);
            }
        }
    }
}

ParameterMapping中就是保存了参数的相关信息,最为重要的是TypeHandler属性

public class ParameterMapping {
    private Configuration configuration;
    private String property;
    private ParameterMode mode;
    private Class<?> javaType;
    private JdbcType jdbcType;
    private Integer numericScale;
    private TypeHandler<?> typeHandler;
    private String resultMapId;
    private String jdbcTypeName;
    private String expression;
}

3.ParameterMapping使用

了解的ParameterMapping的创建过程,继续查看它的使用情况

ParameterMapping使用比较简单,主要是在DefaultParameterMapping中对PrepareStatement设置参数

设置过程:从ParameterMapping中获取property属性,然后从方法入参中获得对应的值,接着从ParameterMapping中获取TypeHandler,最后是通过TypeHandler给PrepareStatement通过下标的方式设置参数

Tags:

最近发表
标签列表