EnumOrdinalTypeHandler
和 EnumTypeHandler
两种。分别使用枚举的 ordinal
以及 name
来存取枚举。这两种 TypeHandler 都通过继承 BaseTypeHandler<E>
实现的。
考虑如下的一个场景:我希望对一系列某个接口的子类实例进行处理,如果针对每一个子类实现一个 TypeHandler 会非常麻烦,而我们在序列化和反序列化子类实例的时候都使用接口方法的返回值,那么我们就可以定义一个泛型 TypeHandler。下面给出一个枚举结合泛型 TypeHandler 的例子。
首先定义一个接口:
package com.avaloninc.generictypehandler.domain;
public interface Translatable {
int getCode();
String getNameCn();
}
然后定义一个实现该接口的枚举:
package com.avaloninc.generictypehandler.domain;
public enum Gender implements Translatable {
MALE(1, "男"),
FEMALE(2, "女");
private String nameCn;
private int code;
Gender(int code, String nameCn) {
this.code = code;
this.nameCn = nameCn;
}
public int getCode() {
return this.code;
}
public String getNameCn() {
return this.nameCn;
}
}
然后我们准备用自定义的 code
来进行存取,而不是枚举的 ordinal
(注意 ordinal
和枚举常量的定义顺序是一致的,默认从 0 开始)。
下面给出实体类:
package com.avaloninc.generictypehandler.domain;
import lombok.Data;
public class Person {
private int id;
private String name;
private int age;
private Gender gender;
}
定义泛型 TypeHandler,注意我们这里使用了指定了泛型的上界:枚举并且实现了接口。
package com.avaloninc.generictypehandler.typehandler;
import com.avaloninc.generictypehandler.domain.Translatable;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
public class GenericTranslatableEnumHandler<T extends Enum<T> & Translatable> extends BaseTypeHandler<T> {
private Class<T> type;
public GenericTranslatableEnumHandler(Class<T> type) {
if (Objects.isNull(type)) {
throw new IllegalArgumentException("type should not be null.");
}
this.type = type;
}
public void setNonNullParameter(PreparedStatement preparedStatement, int i, T t, JdbcType
jdbcType) throws SQLException {
preparedStatement.setInt(i, t.getCode());
}
public T getNullableResult(ResultSet resultSet, String s) throws SQLException {
int code = resultSet.getInt(s);
Optional<T> first = Arrays.stream(type.getEnumConstants())
.filter(ele -> ele.getCode() == code).findFirst();
if (first.isPresent()) {
return first.get();
} else {
throw new SQLException("Illegal argument " + code + " for " + type.getCanonicalName());
}
}
public T getNullableResult(ResultSet resultSet, int i) throws SQLException {
int code = resultSet.getInt(i);
Optional<T> first = Arrays.stream(type.getEnumConstants())
.filter(ele -> ele.getCode() == code).findFirst();
if (first.isPresent()) {
return first.get();
} else {
throw new SQLException("Illegal argument " + code + " for " + type.getCanonicalName());
}
}
public T getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
int code = callableStatement.getInt(i);
Optional<T> first = Arrays.stream(type.getEnumConstants())
.filter(ele -> ele.getCode() == code).findFirst();
if (first.isPresent()) {
return first.get();
} else {
throw new SQLException("Illegal argument " + code + " for " + type.getCanonicalName());
}
}
}
mybatis-config.xml
中指定类型和 TypeHandler 的关联:
<typeHandlers>
<typeHandler handler="com.avaloninc.generictypehandler.typehandler.GenericTranslatableEnumHandler"
javaType="com.avaloninc.generictypehandler.domain.Gender"/>
</typeHandlers>
最后是 Mapper 接口:
package com.avaloninc.generictypehandler.mapper;
import com.avaloninc.generictypehandler.domain.Person;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface PersonMapper {
/**
* Insert int.
*
* @param person the person
* @return the int
*/
"insert into person (name, age, gender) values (#{p.name}, #{p.age}, #{p.gender})")
( useGeneratedKeys = true, keyProperty = "p.id")
( int insert( ("p") Person person);
/**
* Get person.
*
* @param id the id
* @return the person
*/
"select id, name, age, gender from person where id = #{id}")
( Person get( ("id") int id);
}