系统定义的TypeHandler

MyBatis 内部定义了许多有用的 typeHandler,如表 1 所示。

表 1 系统定义的typeHandler
类型处理器Java类型JDBC类型
BooleanTypeHandlerjava.lang.Boolean,boolean数据库兼容的 BOOLEAN
ByteTypeHandlerjava.lang.Byte,byte数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandlerjava.lang.Short,short数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandlerjava.lang.Integer,int数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandlerjava.lang.Long,long数据库兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandlerjava.lang.Float,float数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandlerjava.lang.Double,double数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandlerjava.lang.StringCHAR、VARCHAR
ClobReaderTypeHandlerjava.io.Reader——
ClobTypeHandlerjava.lang.StringCLOB、LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR、NCHAR
NClobTypcHandlerjava.lang.StringNCLOB
BlobInputStreamTypeHandlerjava.io.InputStream——
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB、LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandlerjava.sql.TimeTIME
ObjectTypeHandlerAnyOTHER或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR 任何兼容的字符串类型,存储枚举的名称(而不是索引)
EnumOrdinalTypeHandlerEnumeration Type任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)


这些就是 MyBatis 系统已经创建好的 typeHandler。在大部分的情况下无须显式地声明 jdbcType 和 javaType,或者用 typeHandler 去指定对应的 typeHandler 来实现数据类型转换,因为 MyBatis 系统会自己探测。有时候需要修改一些转换规则,比如枚举类往往需要自己去编写规则。

在 MyBatis 中 typeHandler 都要实现接口 org.apache.ibatis.type.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;
}

这里我们稍微说明一下它的定义。

其中 T 是泛型,专指 javaType,比如我们需要 String 的时候,那么实现类可以写为 implements TypeHandler<String>。

setParameter 方法,是使用 typeHandler 通过 PreparedStatement 对象进行设置 SQL 参数的时候使用的具体方法,其中 i 是参数在 SQL 的下标,parameter 是参数,jdbcType 是数据库类型。

其中有 3 个 getResult 的方法,它的作用是从 JDBC 结果集中获取数据进行转换,要么使用列名(columnName)要么使用下标(columnIndex)获取数据库的数据,其中最后一个 getResult 方法是存储过程专用的。

在编写 typeHandler 前,先来研究一下 MyBatis 系统的 typeHandler 是如何实现的,所以有必要先研究一下 MyBatis 系统的 typeHandler。如果读者打开源码,就可以发现它们都继承了 org.apache.ibatis.type.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;

}

简单分析一下 BaseTypeHandler 的源码。

  • BaseTypeHandler 是个抽象类,需要子类去实现其定义的 4 个抽象方法,而它本身实现了 typeHandler 接口的 4 个方法。
  • getResult 方法,非空结果集是通过 getNullableResult 方法获取的。如果判断为空,则返回 null。
  • setParameter 方法,当参数 parameter 和 jdbcType 同时为空时,MyBatis 将抛出异常。如果能明确 jdbcType,则会进行空设置;如果参数不为空,那么它将采用 setNonNullParameter 方法设置参数。
  • getNullableResult 方法用于存储过程。


MyBatis 使用最多的 typeHanlder 之一 —— StringTypeHandler。它用于字符串转换,其源码如下所示。

@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setString(i, parameter);
}

@Override
public String getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getString(columnName);
}

@Override
public String getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getString(columnIndex);
}

@Override
public String getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getString(columnIndex);
}

显然它实现了 BaseTypeHandler 的 4 个抽象方法,代码也非常简单。

在这里,MyBatis 把 javaType 和 jdbcType 相互转换,那么它们是如何注册的呢?在 MyBatis 中采用 org.apache.ibatis.type.TypeHandlerRegistry 类对象的 register 方法进行注册,如下所示。 

public TypeHandlerRegistry()  {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    ....
    register(byte[].class, jdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, jdbcType.LONGVARBINARY, new BlobTypeHandler());
    ....
}

这样就实现了用代码的形式注册 typeHandler。注意,自定义的 typeHandler 一般不会使用代码注册,而是通过配置或扫描。

Spring Boot是一款开源的Java开发框架,可以简化Spring应用程序的开发过程。MyBatis Plus是在MyBatis框架基础上的增强版本,为开发者提供了更便捷的CRUD操作接口和其他实用功能。PostgreSQL是一种开源的关系型数据库管理系统。 在Spring Boot项目中使用MyBatis Plus框架,可以通过添加相应的依赖和配置,快速集成MyBatis Plus。对于PostgreSQL数据库的支持,可以通过引入PostgreSQL的JDBC驱动来实现。 在使用MyBatis Plus与PostgreSQL时,可能会遇到类型转换的问题。例如,某些特定的数据类型在Java和PostgreSQL之间存在差异,需要通过类型处理器(TypeHandler)进行转换。 TypeHandler在MyBatis Plus中是用于处理Java对象与数据库列之间的类型转换的组件。当在实体类中定义了需要特殊处理的属性时,可以通过自定义TypeHandler来实现转换逻辑。在处理与PostgreSQL相关的类型时,可以使用MyBatis Plus提供的内置TypeHandler或自定义TypeHandler。 自定义TypeHandler通常需要实现TypeHandler接口,并指定Java对象与数据库类型的映射关系。在转换过程中,可以使用PostgreSQL JDBC驱动提供的各种方法来处理特定类型的转换逻辑。 在Spring Boot中配置TypeHandler通常需要在MyBatis的配置文件或配置类中进行相关配置。通过指定要使用的TypeHandler和映射关系,可以实现Java对象与PostgreSQL类型的正确转换。 总之,使用Spring Boot、MyBatis Plus和PostgreSQL可以快速构建Java应用程序,并通过TypeHandler来处理类型转换问题,实现Java对象与PostgreSQL数据库之间的无缝集成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智慧浩海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值