The default EnumTypeHandler maps enum's name (e.g. "CREATED", "RUNNING"), so the column type must be one of text types like VARCHAR [1].
As MyBatis knows nothing about the id property, you have to write a custom type handler.
Here is an example implementation.
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
@MappedTypes(Status.class)
public class StatusTypeHandler implements TypeHandler<Status> {
@Override
public void setParameter(PreparedStatement ps,
int i, Status parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.INTEGER);
} else {
ps.setInt(i, parameter.getId());
}
}
@Override
public Status getResult(ResultSet rs, String columnName) throws SQLException {
return getStatus(rs.getInt(columnName));
}
@Override
public Status getResult(ResultSet rs, int columnIndex) throws SQLException {
return getStatus(rs.getInt(columnIndex));
}
@Override
public Status getResult(CallableStatement cs, int columnIndex) throws SQLException {
return getStatus(cs.getInt(columnIndex));
}
private static Status getStatus(int id) {
if (id == 0) {
return null;
}
for (Status status : Status.values()) {
if (id == status.getId()) {
return status;
}
}
throw new IllegalArgumentException("Cannot convert " + id + " to Status");
}
}
You should register this type handler globally.
Then it's unnecessary to specify typeHandler explicitly in most cases.
If you use mybatis-spring-boot, specifying mybatis.type-handlers-package in the config may be the easiest way to register type handlers globally.
If you use XML config, add the following.
<typeHandlers>
<typeHandler
handler="xxx.yyy.StatusTypeHandler" />
</typeHandlers>
If the Status is the only enum in your project, you can stop reading.
But, what if there are many enums that have id property and you don't want to write a similar custom type handler for each of them?
If your enums implement a common interface like below, you can write a type handler that can map all of them.
public interface HasId {
Integer getId();
}
Here is an example type handler implementation.
Note that it has a constructor that takes java.lang.Class as its argument.
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
@MappedTypes(HasId.class)
public class HasIdTypeHandler<E extends Enum<E> & HasId> implements TypeHandler<E> {
private Class<E> type;
private final E[] enums;
public HasIdTypeHandler(Class<E> type) {
if (type == null)
throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
this.enums = type.getEnumConstants();
if (!type.isInterface() && this.enums == null)
throw new IllegalArgumentException(type.getSimpleName()
+ " does not represent an enum type.");
}
@Override
public void setParameter(PreparedStatement ps,
int i, E parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.INTEGER);
} else {
ps.setInt(i, parameter.getId());
}
}
@Override
public E getResult(ResultSet rs, String columnName) throws SQLException {
return getEnum(rs.getInt(columnName));
}
@Override
public E getResult(ResultSet rs, int columnIndex) throws SQLException {
return getEnum(rs.getInt(columnIndex));
}
@Override
public E getResult(CallableStatement cs, int columnIndex) throws SQLException {
return getEnum(cs.getInt(columnIndex));
}
private E getEnum(int id) {
if (id == 0) {
return null;
}
for (E e : enums) {
if (id == e.getId()) {
return e;
}
}
throw new IllegalArgumentException("Cannot convert " +
id + " to " + type.getSimpleName());
}
}
Note that if you try to specify this type handler in a mapper, it might not work properly. There is a known issue.
[1] FYI, there is another built-in type handler for enums :EnumOrdinalTypeHandler maps enum's ordinal.