我发现了一个TypeHandler的bug你怎么看

来源:-

镜幻

2014-12-10 13:39

<typeHandler handler="com.user.type.UserTypeHandler" javaType="com.user.model.User"/> 
<typeHandler handler="com.user.type.SchoolTypeHandler" javaType="com.user.model.School"/>

<select id="selectUser" resultMap="selectOneResult" parameterType="user">
    select * from user where id = #{id} and school=#{school,typeHandler=com.user.type.SchoolTypeHandler}
</select>
这样运行时会先调用UserTypeHandler然后调用SchoolTypeHandler
UserTypeHandler调用没有问题,但是SchoolTypeHandler调用时会发现传入的parameterObject是User而不是School。。。。你怎么看?


写回答 关注

7回答

  • 源生活
    2014-12-17 16:30:18

    但如果是你来设计typeHandler的解析过程,它首先是要对你的User参数进行类型转换的,转换后肯定不再是User了(要不然你也不会进行类型转换了),这个时候你又如何用#{school}来和转换后的类型进行匹配呢,你可能会说,当我用typeHandler=com.user.type.SchoolTypeHandler的时候它就不应该再做类型转换而把原来的类型给我,可这样另一处#{id}应该是用转换后的类型,那这样经过UserTypeHandler后,竟然还需要同时保持转换前与转换后两种类型,如果是你你会这样设计吗?不敢说mybaits的方式是perfect,但你该如何设计才能即达到你说的局部配置覆盖全局的效果,又能不让typeHandler的效果让人感到歧义?不管讨论的结果如何,我个人很赞赏你这种钻研的精神和清晰的分析思路,用不了多久你就不用在这里和我讨论了。:)

  • 源生活
    2014-12-12 15:57:55

    我明白你的代码结构与逻辑了,不能说是他逻辑上有问题,只能说你的代码结构导致了这样互相干扰的情况

    你的类型转换器的javaType与parameterType是同一个,这是问题的根源

    你应该知道,无论是parameterType还是#{}中,只要出现java类型与类型转换器中指定的javaType相同就会自动调用,这样问题就来了,你的参数是User,他会自动调用UserTypeHandler中的setParameter方法来为SQL语句中所有的#{}赋值,一切要注意,我这说的是为“所有”的#{}赋值,当然,这个时候setParameter的参数parameter是User,从这个时候开始已经和你想的不一样了,你用typeHandler=com.user.type.SchoolTypeHandler来改变了其中一个#{}的类型转换器,但是没用,你只是改变了类型转换器,但参数是经过了UserTypeHandler传给你的,所以是User,所以这个时候无所谓你写的是#{school,typeHandler=com.user.type.SchoolTypeHandler}也好,还是写成#{adsfdsafafdsdsaffdsafdsafdsafdsa,typeHandler=com.user.type.SchoolTypeHandler}也好,这已经不重要了,他们拿到的类型都是UserTypeHandler拿到的类型,为这些#{}赋值的控制,已经和你想的不一样了。

    镜幻

    但是按照我们正常的思维方式,如果我修改一个主要配置文件,那么他将对全局其作用,但是如果我在一个子配置里覆盖主配置文件的设置,那么他应该能在这个子配置起作用的范围内覆盖全局的配置才对,这儿明显是违背这种常规做法的

    2014-12-15 09:51:19

    共 1 条回复 >

  • 源生活
    2014-12-11 09:55:43

    我需要看下user的类结构

    镜幻

    代码已经在下面了

    2014-12-11 11:37:47

    共 1 条回复 >

  • 慕粉3392904
    2016-09-01 09:44:34

    我也碰到相同的问题,User的所有属性都用UserTypeHandler去处理,请问楼主最后怎么解决的

  • 镜幻
    2014-12-11 10:54:25

    我已经查过了mybatis的源码,他确实是逻辑上有问题

    public void setParameters(PreparedStatement ps) throws SQLException {
        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())) {//如果根据parameterObject可以找到转换器,那么value就赋值为parameterObject了
                value = parameterObject;
              } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                value = metaObject.getValue(propertyName);
              }
              TypeHandler typeHandler = parameterMapping.getTypeHandler();//这儿获得的转换器是school的转换器
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
              typeHandler.setParameter(ps, i + 1, value, jdbcType);//转换器是school的 但是传值是User
            }
          }
        }
      }


  • 镜幻
    2014-12-11 10:51:37
    public class User {
    private long id ;
    private String name;
    private String password;
    private String sex;
    private String email;
    private String tel;
    private String department_name;
    private String department_id;
    private School school;
    .......
    }
    public class School {
    private String name;
    private String address;
    ......
    @Override
    public String toString() {
    // TODO Auto-generated method stub
    return name+":"+address;
    }
    }
    public class SchoolTypeHandler extends BaseTypeHandler<School> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    School parameter, JdbcType jdbcType) throws SQLException {
    System.out.println("SchoolTypeHandlers  setParameter被调用了 i="+i+"\tjavaType="+parameter.getClass()+"\tjdbcType="+jdbcType);
    ps.setString(i, parameter.toString());
    }
    @Override
    public School getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    System.out.println("SchoolTypeHandlers  ResultSet columnName");
    School school = new School();
    school.setName(rs.getString(columnName).split(":")[0]);
    school.setAddress(rs.getString(columnName).split(":")[1]);
    return school;
    }
    @Override
    public School getNullableResult(ResultSet rs, int columnIndex)
    throws SQLException {
    System.out.println("SchoolTypeHandlers  ResultSet columnIndex");
    School school = new School();
    school.setName(rs.getString(columnIndex).split(":")[0]);
    school.setAddress(rs.getString(columnIndex).split(":")[1]);
    return school;
    }
    @Override
    public School getNullableResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    System.out.println("CallableStatement columnIndex被调用了");
    return null;
    }
    }
    public class UserTypeHandler implements TypeHandler {
    @Override
    public void setParameter(PreparedStatement ps, int i, Object parameter,
    JdbcType jdbcType) throws SQLException {
    System.out.println("One setNonNullParameter被调用了 i="+i+"\tjavaType="+parameter.getClass()+"\tjdbcType="+jdbcType);
    if (i == 1)
    ps.setLong(i, ((User)parameter).getId());
    else
    ps.setString(i, ((User)parameter).getName());
    }
    @Override
    public User getResult(ResultSet rs, String columnName)
    throws SQLException {
    System.out.println("One ResultSet columnName被调用了");
    User u = new User();
    u.setName(rs.getString("name"));
    return u;
    }
    @Override
    public User getResult(ResultSet rs, int columnIndex) throws SQLException {
    System.out.println("One ResultSet columnIndex");
    User u = new User();
    u.setName(rs.getString("name"));
    return u;
    }
    @Override
    public User getResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    System.out.println("One CallableStatement columnIndex被调用了");
    return null;
    }
    }


  • 镜幻
    2014-12-11 10:50:48

    public class User {

    private long id ;

    private String name;

    private String password;

    private String sex;

    private String email;

    private String tel;

    private String department_name;

    private String department_id;

    private School school;

    .......

    }

    public class School {

    private String name;

    private String address;

    ......

    @Override
    public String toString() {
    // TODO Auto-generated method stub
    return name+":"+address;
    }
    }
    public class SchoolTypeHandler extends BaseTypeHandler<School> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    School parameter, JdbcType jdbcType) throws SQLException {
    System.out.println("SchoolTypeHandlers  setParameter被调用了 i="+i+"\tjavaType="+parameter.getClass()+"\tjdbcType="+jdbcType);
    ps.setString(i, parameter.toString());
    }
    @Override
    public School getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    System.out.println("SchoolTypeHandlers  ResultSet columnName");
    School school = new School();
    school.setName(rs.getString(columnName).split(":")[0]);
    school.setAddress(rs.getString(columnName).split(":")[1]);
    return school;
    }
    @Override
    public School getNullableResult(ResultSet rs, int columnIndex)
    throws SQLException {
    System.out.println("SchoolTypeHandlers  ResultSet columnIndex");
    School school = new School();
    school.setName(rs.getString(columnIndex).split(":")[0]);
    school.setAddress(rs.getString(columnIndex).split(":")[1]);
    return school;
    }
    @Override
    public School getNullableResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    System.out.println("CallableStatement columnIndex被调用了");
    return null;
    }
    }
    public class UserTypeHandler implements TypeHandler {
    @Override
    public void setParameter(PreparedStatement ps, int i, Object parameter,
    JdbcType jdbcType) throws SQLException {
    System.out.println("One setNonNullParameter被调用了 i="+i+"\tjavaType="+parameter.getClass()+"\tjdbcType="+jdbcType);
    if (i == 1)
    ps.setLong(i, ((User)parameter).getId());
    else
    ps.setString(i, ((User)parameter).getName());
    }
    @Override
    public User getResult(ResultSet rs, String columnName)
    throws SQLException {
    System.out.println("One ResultSet columnName被调用了");
    User u = new User();
    u.setName(rs.getString("name"));
    return u;
    }
    @Override
    public User getResult(ResultSet rs, int columnIndex) throws SQLException {
    System.out.println("One ResultSet columnIndex");
    User u = new User();
    u.setName(rs.getString("name"));
    return u;
    }
    @Override
    public User getResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    System.out.println("One CallableStatement columnIndex被调用了");
    return null;
    }
    }


通过自动回复机器人学Mybatis---基础版

微信公众号自动回复功能学习Mybatis,基础教程加案例实战方式学习

107412 学习 · 786 问题

查看课程

相似问题