Custom MyBatis TypeHandler for Automatic JSON and Enum Conversion
This article explains how to create custom MyBatis TypeHandler classes to automatically convert Java objects—such as JSON strings and enum codes—to appropriate JDBC types, simplifying data persistence in Spring Boot applications and improving code maintainability.
Introduction
The author frequently encounters two conversion problems in recent projects: storing a JSON string from a Java entity into a VARCHAR column and storing an Enum's integer code into an INT column. Manually calling toJSONString() and parseObject() for JSON, or extracting the enum's int value for the database, works but is cumbersome.
What is a TypeHandler?
When using MyBatis or MyBatis‑Plus, every parameter setting and result retrieval passes through a TypeHandler, which maps Java types to JDBC types. MyBatis provides many built‑in handlers for primitive types, and developers can create custom handlers for complex types such as JSON strings or enums.
Conversion Steps
During execution of a prepared SQL statement, MyBatis locates the appropriate TypeHandler (defined in configuration or mapper XML), converts the Java value to a JDBC‑compatible value, and sets it on the PreparedStatement . The reverse occurs when reading results.
1. JSON Conversion
Add the following to the .yml configuration to scan custom handlers:
mybatis-plus:
type-handlers-package: # package where custom handlers resideCustom handler implementation:
@MappedTypes({JSONObject.class, JSONArray.class})
public class JSONTypeHandler
extends BaseTypeHandler
{
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T param, JdbcType jdbcType) throws SQLException {
ps.setString(i, JSON.toJSONString(param));
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
String jsonStr = rs.getString(columnName);
return StringUtils.isNotBlank(jsonStr) ? JSON.parseObject(jsonStr, getRawType()) : null;
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String jsonStr = rs.getString(columnIndex);
return StringUtils.isNotBlank(jsonStr) ? JSON.parseObject(jsonStr, getRawType()) : null;
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String jsonStr = cs.getString(columnIndex);
return StringUtils.isNotBlank(jsonStr) ? JSON.parseObject(jsonStr, getRawType()) : null;
}
}2. Enum Conversion
Custom handler for mapping an enum's code field to an INT column:
@MappedTypes(MyEnum.class)
public class EnumCodeTypeHandler
extends BaseTypeHandler
{
private final Class
type;
private final Map
enumMap = new ConcurrentHashMap<>();
public EnumCodeTypeHandler(Class
type) {
Assert.notNull(type, "argument cannot be null");
this.type = type;
for (E e : type.getEnumConstants()) {
enumMap.put(e.toCode(), e);
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int index, E e, JdbcType jdbcType) throws SQLException {
ps.setInt(index, e.toCode());
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
if (rs.wasNull()) return null;
return enumMap.get(code);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
if (rs.wasNull()) return null;
return enumMap.get(code);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
if (cs.wasNull()) return null;
return enumMap.get(code);
}
}Supporting interface that enums must implement:
public interface MyEnum {
MyEnum fromCode(int code);
int toCode();
}Example enum implementing the interface:
@Getter
@RequiredArgsConstructor
public enum StudyStatusEnum implements MyEnum {
ONE(1, "枚举1"),
TWO(2, "枚举2"),
THREE(3, "枚举3"),
FOUR(4, "枚举4"),
FIVE(5, "枚举5");
private final Integer code;
private final String desc;
@Override
public MyEnum fromCode(int code) {
return Arrays.stream(values())
.filter(v -> v.getCode().equals(code))
.findFirst()
.orElse(null);
}
@Override
public int toCode() {
return this.getCode();
}
}Conclusion
By leveraging built‑in and custom TypeHandlers, developers can seamlessly handle various data‑type conversion needs, boost development efficiency, and keep code maintainable. In a Spring Boot environment, registering custom TypeHandlers is straightforward, allowing you to focus on business logic.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.