mybatis从使用到了解(五)_mybatis类型映射(TypeHandler)

mybatis的TypeHandler类型转换

java有java的数据类型,数据哭有数据库的类型,当我们把java数据插入数据库的时候,需要讲java数据类型转化为数据库数据类型;当我们从数据库读取数据的时候,需要把数据库类型转化为java类型来处理。这中间的转化,mybatis中是通过TypeHandler类型处理器来处理的。

我们先通过一个例子来了解TypeHandler的实现机制

在这个例子中讲java字符数组String[]转化成数据库varchar类型。如:String[] interests = {"movie", "music"}转为”movie,music",同时也能实现从varchar转化为String[] 数组。

  • 自定义类型转化器
@MappedTypes({String[].class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class StringArrayTypeHandler extends BaseTypeHandler<String[]>{
    /**
     * 把Java类型参数转换为对应的数据库类型
     * @param ps 当前的PreparedStatement对象
     * @param i 当前参数位置
     * @param parameter 当前参数的Java对象
     * @param jdbcType 当前参数的数据库类型
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException {
        // 由于BaseTypeHandler中已经把parameter为null的情况做了处理,所以这里我们就不用在判断parameter是否为空,直接用就可以了
        StringBuffer result = new StringBuffer();
        for (String value : parameter) {
            result.append(value).append(",");
        }
        result.deleteCharAt(result.length() - 1);
        ps.setString(i, result.toString());
    }

    /**
     * 获取数据结果集时把数据库类型转换为对应的Java类型
     * @param rs 当前的结果集
     * @param columnName 当前的字段名称
     * @return  转换后的Java对象
     * @throws SQLException
     */
    @Override
    public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.getStringArray(rs.getString(columnName));
    }

    /**
     * 通过字段位置获取字段数据时把数据库类型转换为对应的Java类型
     * @param rs 当前的结果集
     * @param columnIndex 当前字段的位置
     * @return 转换后的Java对象
     * @throws SQLException
     */
    @Override
    public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.getStringArray(rs.getString(columnIndex));
    }

    /**
     * 调用存储过程后把数据库类型的数据转换为对应的Java类型
     * @param cs  当前的CallableStatement执行后的CallableStatement
     * @param columnIndex  当前输出参数的位置
     * @return
     * @throws SQLException
     */
    @Override
    public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.getStringArray(cs.getString(columnIndex));
    }

    /**
     * 讲”movie,music"转化为数组对象
     * @param columnValue
     * @return
     */
    private String[] getStringArray(String columnValue) {
        if (columnValue == null)
            return null;
        return columnValue.split(",");
    }
}
  • 配置文件里配置自定义的TypeHandler
<typeHandlers>
    <!--<typeHandler handler="com.yongssu.mybatis.demo1.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>-->
    <typeHandler handler="com.yongssu.mybatis.demo1.StringArrayTypeHandler"/>
</typeHandlers>

如果在自定义类型转化器时通过注解标注了对应的javaType以及jdbcType,则在配置文件中不需要再次声明;如果没有,则需要在配置文件中说明avaType和jdbcType。

  • 声明包含String[]数组的学生类
public class Student {
    private int student_id;
    private String student_name;
    private int student_age;
    private String student_phone;
    private String[] interests;
    public Student() {
        super();
    }
    public int getStudent_id() {
        return student_id;
    }
    public void setStudent_id(int student_id) {
        this.student_id = student_id;
    }
    public String getStudent_name() {
        return student_name;
    }
    public void setStudent_name(String student_name) {
        this.student_name = student_name;
    }
    public int getStudent_age() {
        return student_age;
    }
    public void setStudent_age(int student_age) {
        this.student_age = student_age;
    }
    public String getStudent_phone() {
        return student_phone;
    }
    public void setStudent_phone(String student_phone) {
        this.student_phone = student_phone;
    }
    public String[] getInterests() {
        return interests;
    }
    public void setInterests(String[] interests) {
        this.interests = interests;
    }
    @Override
    public String toString() {
        return "Student{" +
                "student_id=" + student_id +
                ", student_name='" + student_name + '\'' +
                ", student_age=" + student_age +
                ", student_phone='" + student_phone + '\'' +
                ", interests=" + Arrays.toString(interests) +
                '}';
    }
}
  • 在stutent.xml中增加sql语句
<resultMap id="studentResult" type="Student">
    <id column="student_id" property="student_id"/>
    <result column="student_name" property="student_name"/>
    <result column="student_age" property="student_age"/>
    <result column="student_phone" property="student_phone"/>
    <result column="interests" property="interests"/>
</resultMap>

<insert id="insertStudent" parameterType="Student" useGeneratedKeys="true" keyColumn="id">
    insert into student (student_name,student_age,student_phone,interests) values
    (#{student_name},#{student_age},#{student_phone},#{interests,javaType=[Ljava.lang.String;,jdbcType=VARCHAR})
</insert>

<select id="findById" parameterType="int" resultMap="studentResult">
    select * from student where student_id = #{id}
</select>
  • 测试类
/**
 * 方式4
 * @throws IOException
 */
public static void test04() throws IOException {
    // 读取配置文件
    Reader reader = Resources.getResourceAsReader("config.xml");
    // 创建SqlSeessionFactory工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    Student student = new Student();
    String[] interests = {"movie", "music"};
    student.setInterests(interests);
    student.setStudent_age(12);
    student.setStudent_name("yongsheng.su");
    student.setStudent_phone("123456");
    // 执行sql
    SqlSession session = sqlSessionFactory.openSession();
    try {
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        int result = mapper.insertStudent(student);
        System.out.println(result);
        session.commit();
    } finally {
        session.close();
    }
}

public static void test05() throws IOException {
    // 读取配置文件
    Reader reader = Resources.getResourceAsReader("config.xml");
    // 创建SqlSeessionFactory工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

    Student student = null;
    // 执行sql
    SqlSession session = sqlSessionFactory.openSession();
    try {
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        student = mapper.selectStudent(7);
        System.out.println(student);
        session.commit();
    } finally {
        session.close();
    }
    System.out.println(student);
}

mybatis类型映射实现说明

通过上面的例子,可以发现要实现自定义TypeHandler,只需要继承BaseTypeHandler,然后实现相应的转换规则就可以。其实除了继承BaseTypeHadler以外,还可以自己实现TypeHandler。通过下面代码,可以发现,BaseHandler也是继承自TypeHandler,并提供了一些默认的实现,对null值做了一些处理,这样我们继承BaseTypeHandler时就不需要对null值做处理了。

  • TypeHandler接口
public interface TypeHandler<T> {
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  T getResult(ResultSet rs, String columnName) throws SQLException;
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
  • BaseTypeHandler类实现
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
  protected Configuration configuration;
  public void setConfiguration(Configuration c) {
    this.configuration = c;
  }
  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
                "Cause: " + e, e);
      }
    } else {
      try {
        setNonNullParameter(ps, i, parameter, jdbcType);
      } catch (Exception e) {
        throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different configuration property. " +
                "Cause: " + e, e);
      }
    }
  }
  @Override
  public T getResult(ResultSet rs, String columnName) throws SQLException {
    T result;
    try {
      result = getNullableResult(rs, columnName);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
    }
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }
  @Override
  public T getResult(ResultSet rs, int columnIndex) throws SQLException {
    T result;
    try {
      result = getNullableResult(rs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set.  Cause: " + e, e);
    }
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }
  @Override
  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
    T result;
    try {
      result = getNullableResult(cs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement.  Cause: " + e, e);
    }
    if (cs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}

mybatis提供的默认类型

对于一些常用的java类型,mybatis已经提供了相应的TypeHandler,并且会自动注册他们。mybatis提供了以下集中类型转换器。

register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

参考文章:http://4443915.blog.51cto.com/4433915/1828533

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,282评论 0 4
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,566评论 25 707
  • 你 是一丛蹙眉的花衣 白日黑夜里 冷冷的娇艳 俯瞰或仰望 这如流浪者般的孤独 又如废墟般的静寂 是为谁而伫立 你 ...
    三姑娘的开放衣橱阅读 222评论 0 1
  • 昨天我们好多同学一起给程程过生日,祝程程生日快乐!学习进步!
    晨起1阅读 84评论 0 0