Mybatis之#{}和${}的区别及其实现方式

简单的说#{}和${}的区别:$是String 拼接插入的#则是占位符来做处理的,写法比如字符串类型,$需要自己添加‘‘#就不需要添加,对于日志的差别就是$会打印在日志里面,#则显示?

大多数我们都是用#{} 因为可以防止sql注入,但是有时候${}还是很必要的,比如传入tableName,或者fieldName比如Order By id||time 就需要${}传入 #{}就无法搞定

typeHandler就无法对${}起作用

---只有明白工具是如何玩耍的,我们才能更好的使用工具

先介绍一个解析#{}和${}的类,这里${}就替换成对对应的值了,而#{}替换成?,并且在这里解析了这个属性的字段,包括判断了类型等等

public class GenericTokenParser {

 

  private final String openToken;//这个比如#{ 或者${

  private final String closeToken;//这里基本上就是}

  private final TokenHandler handler;//根据#{key}或者${key}得到key的值

 

  public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {

    this.openToken = openToken;

    this.closeToken = closeToken;

    this.handler = handler;

  }

 /**

*这个就是替换的了${key} 然后通过hanlder获取value 拼接进去

**/

  public String parse(String text) {

    StringBuilder builder = new StringBuilder();

    if (text != null && text.length() > 0) {

      char[] src = text.toCharArray();

      int offset = 0;

      int start = text.indexOf(openToken, offset);

      while (start > -1) {

        if (start > 0 && src[start - 1] == ‘\\‘) {

          // the variable is escaped. remove the backslash.

          builder.append(src, offset, start - 1).append(openToken);

          offset = start + openToken.length();

        else {

          int end = text.indexOf(closeToken, start);

          if (end == -1) {

            builder.append(src, offset, src.length - offset);

            offset = src.length;

          else {

            builder.append(src, offset, start - offset);

            offset = start + openToken.length();

            String content = new String(src, offset, end - offset);//拿到#{key}||${key}中的key

            builder.append(handler.handleToken(content));//根据key获取对应的"value"

            offset = end + closeToken.length();

          }

        }

        start = text.indexOf(openToken, offset);

      }

      if (offset < src.length) {

        builder.append(src, offset, src.length - offset);

      }

    }

    return builder.toString();

  }

 

}



其实最大的区别也就是下面的两个不同的实现类

1.${} 的解析实现类

判断一下参数的类型,然后就把value给搞定了,没有添加其他的东西,万能的Ognl


private static class BindingTokenParser implements TokenHandler {

 

    private DynamicContext context;

 

    public BindingTokenParser(DynamicContext context) {

      this.context = context;

    }

 

    public String handleToken(String content) {

      Object parameter = context.getBindings().get("_parameter");

      if (parameter == null) {

        context.getBindings().put("value"null);

      else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {

        context.getBindings().put("value", parameter);

      }

      Object value = OgnlCache.getValue(content, context.getBindings());

      return (value == null "" : String.valueOf(value)); // issue #274 return "" instead of "null"

    }

  }

2.#{} 的解析实现类

  这里就比较复杂了,判断了javaType,typeHandler,数字精度,通过hanlder,我们就可以处理一些列复杂的数据
private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {

 

    private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();

    private Class<?> parameterType;

    private MetaObject metaParameters;

 

    public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {

      super(configuration);

      this.parameterType = parameterType;

      this.metaParameters = configuration.newMetaObject(additionalParameters);

    }

 

    public List<ParameterMapping> getParameterMappings() {

      return parameterMappings;

    }

 

    public String handleToken(String content) {

      parameterMappings.add(buildParameterMapping(content));

      return "?";

    }

 

//这里就是把#{key}内容进行解析成一个带有一些列属性的类然后再由一些列typehanlder来setValue

    private ParameterMapping buildParameterMapping(String content) {

      Map<String, String> propertiesMap = parseParameterMapping(content);

      String property = propertiesMap.get("property");

      Class<?> propertyType;

      if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params

        propertyType = metaParameters.getGetterType(property);

      else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {

        propertyType = parameterType;

      else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {

        propertyType = java.sql.ResultSet.class;

      else if (property != null) {

        MetaClass metaClass = MetaClass.forClass(parameterType);

        if (metaClass.hasGetter(property)) {

          propertyType = metaClass.getGetterType(property);

        else {

          propertyType = Object.class;

        }

      else {

        propertyType = Object.class;

      }

      ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);

      Class<?> javaType = propertyType;

      String typeHandlerAlias = null;

      for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {

        String name = entry.getKey();

        String value = entry.getValue();

        if ("javaType".equals(name)) {

          javaType = resolveClass(value);

          builder.javaType(javaType);

        else if ("jdbcType".equals(name)) {

          builder.jdbcType(resolveJdbcType(value));

        else if ("mode".equals(name)) {

          builder.mode(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)) {

          // Do Nothing

        else if ("expression".equals(name)) {

          throw new BuilderException("Expression based parameters are not supported yet");

        else {

          throw new BuilderException("An invalid property ‘" + name + "‘ was found in mapping #{" + content + "}.  Valid properties are " + parameterProperties);

        }

      }

      if (typeHandlerAlias != null) {

        builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));

      }

      return builder.build();

    }

 

    private Map<String, String> parseParameterMapping(String content) {

      try {

        return new ParameterExpression(content);

      catch (BuilderException ex) {

        throw ex;

      catch (Exception ex) {

        throw new BuilderException("Parsing error was found in mapping #{" + content + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);

      }

    }

  }



时间: 2024-10-07 17:30:35

Mybatis之#{}和${}的区别及其实现方式的相关文章

mybatis中&quot;#&quot;和&quot;$&quot;的区别

mybatis中"#"和"$"的区别 动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析.mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}. 在下面的语句中,如果 username 的值为 zhangsan,则两种方式无任何区别: select * from user where name = #{name}; select * from

Mybatis与Ibatis的区别

Mybatis与Ibatis的区别: 1.Mybatis实现了接口绑定,使用更加方便 在ibatis2.x中我们需要在DAO的实现类中指定具体对应哪个xml映射文件, 而Mybatis实现了DAO接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来变得更加省事和方便. 这可以说是Mybatis最重要的改进. 注意: 虽然Mybatis支持在接口中直接使用annotation的配置方式来简化配置, 不过强烈建议仍然使用xml配置的方式.毕竟annotation的配置方式功能有限且代码

MyBatis中#{ }和${ }的区别(笔面试题)

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 19.0px "PingFang SC" } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 19.0px "PingFang SC"; min-height: 26.0px } span.s1 { font: 19.0px Helvetica } MyBatis中#{ }和${ }的区别详解 1.#将传入的数据当成一个字符串,会对

mybatis与hibernate的区别持久层对比【面试题】

Mybatis技术特点: 好处: 通过直接编写SQL语句,可以直接对SQL进行性能的优化: 学习门槛低,学习成本低.只要有SQL基础,就可以学习mybatis,而且很容易上手: 由于直接编写SQL语句,所以灵活多变,代码维护性更好. 缺点: 不能支持数据库无关性,即数据库发生变更,要写多套代码进行支持,移植性不好. Mysql:limit Oracle:rownum 需要编写结果映射. Hibernate技术特点: 好处: 标准的orm框架,程序员不需要编写SQL语句. 具有良好的数据库无关性,

MyBatis和Hibernate的区别

MyIbatis和Hibernate的区别 1.MyBatis 真正实现了java代码和sql的分离 2.Hibernate 是全自动的,MyBatis是半自动的 Hibernate实现了部分自动生成SQL 3.SQL优化上 MyBatis 强于 Hibernate 1---MyBatis优化维护方便 SQL全存在于XML中 不需要修改源代码2---开发效率上 Hibernate略强于mybatiss 3---从性能上说 因为Mybatis全都是自己手写的SQL 因此性能相对较高 反之 Hibe

[DB][mybatis]MyBatis mapper文件中的变量引用方式#{}与${}的差别

MyBatis mapper文件中的变量引用方式#{}与${}的差别 默认情况下,使用#{}语法,MyBatis会产生PreparedStatement语句中,并且安全的设置PreparedStatement参数,这个过程中MyBatis会进行必要的安全检查和转义. 示例1: 执行SQL:Select * from emp where name = #{employeeName} 参数:employeeName=>Smith 解析后执行的SQL:Select * from emp where n

JavaScript发展史,与JScript区别,引入方式,数据类型,命名规范,命名推荐,解决命名冲突

今天真机调试的时候莫名其妙遇到了这样的一个问题: This product type must be built using a provisioning profile, however no provisioning profile matching both the identity "iPhone Developer" and the bundle identifier..... 具体如下图所示: 十分蛋疼, 发现不管是从网上下的demo, 还是自己的过程.凡事真机测试的时候都

SQL Select count(*)和Count(1)的区别和执行方式及SQL性能优化

SQL性能优化:http://www.cnblogs.com/CareySon/category/360333.html Select count(*)和Count(1)的区别和执行方式 在SQL Server中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理. 往常我经常会看到一些所谓的优化建议不使用Count(* )而是使用Count(1),从而可以提升性能,给出的理由是Coun

MyBatis mapper文件中的变量引用方式#{}与${}的差别

MyBatis mapper文件中的变量引用方式#{}与${}的差别 默认情况下,使用#{}语法,MyBatis会产生PreparedStatement语句中,并且安全的设置PreparedStatement参数,这个过程中MyBatis会进行必要的安全检查和转义.示例1:执行SQL:Select * from emp where name = #{employeeName}参数:employeeName=>Smith解析后执行的SQL:Select * from emp where name