为了账号安全,请及时绑定邮箱和手机立即绑定

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

<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 回答

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

0 回复 有任何疑惑可以回复我~

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

你的类型转换器的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拿到的类型,为这些#{}赋值的控制,已经和你想的不一样了。

0 回复 有任何疑惑可以回复我~
#1

镜幻 提问者

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

我需要看下user的类结构

0 回复 有任何疑惑可以回复我~
#1

镜幻 提问者

代码已经在下面了
2014-12-11 回复 有任何疑惑可以回复我~

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

0 回复 有任何疑惑可以回复我~

我已经查过了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
        }
      }
    }
  }


0 回复 有任何疑惑可以回复我~
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;
}
}


0 回复 有任何疑惑可以回复我~

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;
}
}


0 回复 有任何疑惑可以回复我~

举报

0/150
提交
取消

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

我要回答 关注问题
意见反馈 帮助中心 APP下载
官方微信