TypeHandler类型转换器

1,概述:

我们思考一个问题,我们之前在是使用JDBC的时候,我们从ResultSet获取对应的数据,用到了对应getXXX方法。比如获取一个数据库为int的数据,我们可以使用rs.getInt(),如果我们获取数据库类型为varchar的数据,我们可以使用rs.getString()。

而我们现在使用了Mybatis之后,从数据库里面查询出来了之后,他是如何把数据库的类型和Java类型对应起来的呢?这个地方Mybatis就用到了typeHandler来实现。

2,设计思路:

在typeHander里面提出了两个类型。一个叫做jdbcType,一个叫做javaType。jdbcType表示数据库中的字段类型,另外一个javaType表示对应的PO中的java的类型。而我们的typeHander承担的就是把jdbcType和JavaType相互转换的作用。

spriingboot java 代码实现mybatis 转驼峰_java

3,系统自定义typeHandler:

Mybatis自己已经给我们定义好了很多typeHandler了,这里我们截图说明一下:

类型处理器

Java类型

JDBC类型

BooleanTypeHandler

java.lang.Boolean,boolean

数据库兼容的 BOOLEAN

ByteTypeHandler

java.lang.Byte,byte

数据库兼容的 NUMERIC 或 BYTE

ShortTypeHandler

java.lang.Short,short

数据库兼容的 NUMERIC 或 SHORT INTEGER

IntegerTypeHandler

java.lang.Integer,int

数据库兼容的 NUMERIC 或 INTEGER

LongTypeHandler

java.lang.Long,long

数据库兼容的 NUMERIC 或 LONG INTEGER

FloatTypeHandler

java.lang.Float,float

数据库兼容的 NUMERIC 或 FLOAT

DoubleTypeHandler

java.lang.Double,double

数据库兼容的 NUMERIC 或 DOUBLE

BigDecimalTypeHandler

java.math.BigDecimal

数据库兼容的 NUMERIC 或 DECIMAL

StringTypeHandler

java.lang.String

CHAR、VARCHAR

ClobReaderTypeHandler

java.io.Reader

——

ClobTypeHandler

java.lang.String

CLOB、LONGVARCHAR

NStringTypeHandler

java.lang.String

NVARCHAR、NCHAR

NClobTypcHandler

java.lang.String

NCLOB

BlobInputStreamTypeHandler

java.io.InputStream

——

ByteArrayTypeHandler

byte[]

数据库兼容的字节流类型

BlobTypeHandler

byte[]

BLOB、LONGVARBINARY

DateTypeHandler

java.util.Date

TIMESTAMP

DateOnlyTypeHandler

java.util.Date

DATE

TimeOnlyTypeHandler

java.util.Date

TIME

SqlTimestampTypeHandler

java.sql.Timestamp

TIMESTAMP

SqlDateTypeHandler 

java.sql.Date 

DATE

SqlTimeTypeHandler

java.sql.Time

TIME

ObjectTypeHandler

Any

OTHER或未指定类型

EnumTypeHandler

Enumeration Type

VARCHAR 任何兼容的字符串类型,存储枚举的名称(而不是索引)

EnumOrdinalTypeHandler

Enumeration Type

任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)

上面就是Mybatis自带的typeHandler。在大部分的情况下无须显式地声明jdbcType javaType,因为MyBatis系统会自己检测。 

4,分析typeHandler实现思路

我们这里要自定义类型转换器,我们先参考一下Mybatis自己定义的类型转换器。

这里我们就用最常用StringTypeHandler来举例。我们跟踪了一下代码,我们发现StringTypeHandler继承了BaseTypeHandler,BaseTypeHandler实现了TypeHandler接口。

1,分析TypeHandler接口:

package org.apache.ibatis.type;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

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;

}

1,这里有个泛型T,这个泛型就是表示我们的JavaType。比如我们的PO中的某个属性的是String类型,我们就可以把这个T写成String。这里,我们在ResultMap中某个属性上面配置,Mybatis就会自动检查我们对应属性的Java类型,然后设置过来。

2,setParameter方法,使用typeHandler通过PreparedStatement对象进行设置sql参数的时候调用的具体方法。其中i表示Sql参数的下表,parameter是参数,jdbcType表示数据库类型。

3,getResult方法:它的作用就是从jdbc结果集中获取数据进行转换。要么使用列表,要么使用下标。其中最后一个getResult是存储过程用的,我们不说。 

2,分析BaseTypeHandler抽象类:

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
  @Deprecated
  protected Configuration configuration;
  @Deprecated
  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 {
    try {
      return getNullableResult(rs, columnName);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
    }
  }

  @Override
  public T getResult(ResultSet rs, int columnIndex) throws SQLException {
    try {
      return getNullableResult(rs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set.  Cause: " + e, e);
    }
  }

  @Override
  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
    try {
      return getNullableResult(cs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement.  Cause: " + e, e);
    }
  }

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

1,我们在TypeHandler接口里面定义的方法,在本类当中都被实现了,但是,它是通过直接调用对应的抽象方法来实现的。

2,setParameter方法:当parameter和jdbcType同时为空的时候,Mybatis将抛出异常,如果能明确jdbcType,则会进行空设置。如果parameter不为空那么它讲采用setNotNullParameter方法来设置。而setNotNullParameter也是一个抽象方法,我们在自定义的TypeHandler中进行实现。

3,getResult方法:通过调用getNullableResult方法来获取,如果判断为空返回null。getNullableResult也是一个抽象方法,我们也需要在子类中实现。