typeHandlers

Whenever MyBatis sets a parameter on a PreparedStatement or retrieves a value from a ResultSet, a TypeHandler is used to retrieve the value in a means appropriate to the Java type. The following table describes the default TypeHandlers.

NOTE Since version 3.4.5, MyBatis supports JSR-310 (Date and Time API) by default.

Type HandlerJava TypesJDBC Types
BooleanTypeHandlerjava.lang.Boolean, booleanAny compatible BOOLEAN
ByteTypeHandlerjava.lang.Byte, byteAny compatible NUMERIC or BYTE
ShortTypeHandlerjava.lang.Short, shortAny compatible NUMERIC or SMALLINT
IntegerTypeHandlerjava.lang.Integer, intAny compatible NUMERIC or INTEGER
LongTypeHandlerjava.lang.Long, longAny compatible NUMERIC or BIGINT
FloatTypeHandlerjava.lang.Float, floatAny compatible NUMERIC or FLOAT
DoubleTypeHandlerjava.lang.Double, doubleAny compatible NUMERIC or DOUBLE
BigDecimalTypeHandlerjava.math.BigDecimalAny compatible NUMERIC or DECIMAL
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
ClobReaderTypeHandlerjava.io.Reader-
ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
NClobTypeHandlerjava.lang.StringNCLOB
BlobInputStreamTypeHandlerjava.io.InputStream-
ByteArrayTypeHandlerbyte[]Any compatible byte stream type
BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME
ObjectTypeHandlerAnyOTHER, or unspecified
EnumTypeHandlerEnumeration TypeVARCHAR any string compatible type, as the code is stored (not index).
EnumOrdinalTypeHandlerEnumeration TypeAny compatible NUMERIC or DOUBLE, as the position is stored (not the code itself).
SqlxmlTypeHandlerjava.lang.StringSQLXML
InstantTypeHandlerjava.time.InstantTIMESTAMP
LocalDateTimeTypeHandlerjava.time.LocalDateTimeTIMESTAMP
LocalDateTypeHandlerjava.time.LocalDateDATE
LocalTimeTypeHandlerjava.time.LocalTimeTIME
OffsetDateTimeTypeHandlerjava.time.OffsetDateTimeTIMESTAMP
OffsetTimeTypeHandlerjava.time.OffsetTimeTIME
ZonedDateTimeTypeHandlerjava.time.ZonedDateTimeTIMESTAMP
YearTypeHandlerjava.time.YearINTEGER
MonthTypeHandlerjava.time.MonthINTEGER
YearMonthTypeHandlerjava.time.YearMonthVARCHAR or LONGVARCHAR
JapaneseDateTypeHandlerjava.time.chrono.JapaneseDateDATE

You can override the type handlers or create your own to deal with unsupported or non-standard types. To do so, implement the interface org.apache.ibatis.type.TypeHandler or extend the convenience class org.apache.ibatis.type.BaseTypeHandler and optionally map it to a JDBC type. For example:

  1. // ExampleTypeHandler.java
  2. @MappedJdbcTypes(JdbcType.VARCHAR)
  3. public class ExampleTypeHandler extends BaseTypeHandler<String> {
  4. @Override
  5. public void setNonNullParameter(PreparedStatement ps, int i,
  6. String parameter, JdbcType jdbcType) throws SQLException {
  7. ps.setString(i, parameter);
  8. }
  9. @Override
  10. public String getNullableResult(ResultSet rs, String columnName)
  11. throws SQLException {
  12. return rs.getString(columnName);
  13. }
  14. @Override
  15. public String getNullableResult(ResultSet rs, int columnIndex)
  16. throws SQLException {
  17. return rs.getString(columnIndex);
  18. }
  19. @Override
  20. public String getNullableResult(CallableStatement cs, int columnIndex)
  21. throws SQLException {
  22. return cs.getString(columnIndex);
  23. }
  24. }
  1. <!-- mybatis-config.xml -->
  2. <typeHandlers>
  3. <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
  4. </typeHandlers>

Using such a TypeHandler would override the existing type handler for Java String properties and VARCHAR parameters and results. Note that MyBatis does not introspect upon the database metadata to determine the type, so you must specify that it’s a VARCHAR field in the parameter and result mappings to hook in the correct type handler. This is due to the fact that MyBatis is unaware of the data type until the statement is executed.

MyBatis will know the Java type that you want to handle with this TypeHandler by introspecting its generic type, but you can override this behavior by two means:

  • Adding a javaType attribute to the typeHandler element (for example: javaType="String")
  • Adding a @MappedTypes annotation to your TypeHandler class specifying the list of java types to associate it with. This annotation will be ignored if the javaType attribute as also been specified.

The associated JDBC type can be specified by two means:

  • Adding a jdbcType attribute to the typeHandler element (for example: jdbcType="VARCHAR").
  • Adding a @MappedJdbcTypes annotation to your TypeHandler class specifying the list of JDBC types to associate it with. This annotation will be ignored if the jdbcType attribute as also been specified.

When deciding which TypeHandler to use in a ResultMap, the Java type is known (from the result type), but the JDBC type is unknown. MyBatis therefore uses the combination javaType=[TheJavaType], jdbcType=null to choose a TypeHandler. This means that using a @MappedJdbcTypes annotation restricts the scope of a TypeHandler and makes it unavailable for use in ResultMaps unless explicity set. To make a TypeHandler available for use in a ResultMap, set includeNullJdbcType=true on the @MappedJdbcTypes annotation. Since Mybatis 3.4.0 however, if a single TypeHandler is registered to handle a Java type, it will be used by default in ResultMaps using this Java type (i.e. even without includeNullJdbcType=true).

And finally you can let MyBatis search for your TypeHandlers:

  1. <!-- mybatis-config.xml -->
  2. <typeHandlers>
  3. <package name="org.mybatis.example"/>
  4. </typeHandlers>

Note that when using the autodiscovery feature JDBC types can only be specified with annotations.

You can create a generic TypeHandler that is able to handle more than one class. For that purpose add a constructor that receives the class as a parameter and MyBatis will pass the actual class when constructing the TypeHandler.

  1. //GenericTypeHandler.java
  2. public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {
  3. private Class<E> type;
  4. public GenericTypeHandler(Class<E> type) {
  5. if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
  6. this.type = type;
  7. }
  8. ...

EnumTypeHandler and EnumOrdinalTypeHandler are generic TypeHandlers. We will learn about them in the following section.