mybatis使用enum

项目中经常会遇到一些PO类中的某个字段的取值范围范围是一组固定的值,为了方便期间使用了number来存储这些值。这样会带来的一个问题,当我创建一个PO对象时,这个字段怎么赋值,应该有那些值?通常我们会提供一个enum来维护这个字段的取值范围,随之而来的问题当项目中enum越来越多,或者文档没有更新的情况下,开发人员很难知道,这个字段是不是有enum与之对应,或者是哪个enum与之对应。很多时候我们知道有几种值,我们随手就赋值了一个字面常量值。这就可能出现了莫名其妙的类型。最直接的解决方案,我们在PO字段上直接声明enum类型,就完全避开了错误使用值的情况。

MyBatis官网提供了两种方案,EnumTypeHandlerEnumOrdinalTypeHandler。EnumTypeHandler在数据库中使用VARCHAR存储enum的名字。EnumOrdinalTypeHandler在数据库使用NUMBERDOUBLE存储enum的索引。

我们更想的是在表中存储NUMBER类型,所以EnumOrdinalTypeHandler更适合我们的需求。EnumOrdinalTypeHandler存储是enum的索引,但是如果我们在enum中想使用自己的code,而不是enum本身的索引,要怎么去做呢?根据MyBatis官网文档我们可以自定义类型处理器来试下,下面是自定义处理器的使用方法。

既然我们的目的是想使用自定义的code存储到表中,所以首先我们应该强制我们的enum类型应该有对应的code字段,我们通过在enum中实现接口来实现。

1
2
3
4
5
6
7
8
9
public interface EnumCode {

/**
* 返回枚举值代码
*
* @return
*/
int getCode();
}

上面我们定义了一个接口EnumCode它值定义了一个方法就是返回code。在我们的enum中实现该接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public enum PlatformEnum implements EnumCode {
/**
* 安卓
*/
ANDROID(0),
/**
* IOS
*/
IOS(1),
/**
* 微信小游戏
*/
WECHAT(2),
/**
* H5
*/
H5(3);

private int code;

PlatformEnum(int code) {
this.code = code;
}


@Override
public int getCode() {
return code;
}
}

接下来就是实现类型处理器,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class EnumCodeTypeHandler extends BaseTypeHandler<EnumCode> {

private Class<EnumCode> type;

public EnumCodeTypeHandler(@NonNull Class<EnumCode> type) {
this.type = type;
}

@Override
public void setNonNullParameter(PreparedStatement ps, int i, EnumCode parameter, JdbcType jdbcType) throws SQLException {
ps.setByte(i, (byte) parameter.getCode());
}

@Override
public EnumCode getNullableResult(ResultSet rs, String columnName) throws SQLException {
int i = rs.getByte(columnName);
if (rs.wasNull()) {
return null;
} else {
return getValuedEnum(i);
}
}

@Override
public EnumCode getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getByte(columnIndex);
return rs.wasNull() ? null : getValuedEnum(code);
}

@Override
public EnumCode getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getByte(columnIndex);
return cs.wasNull() ? null : getValuedEnum(code);
}


/**
* 转换为{@link EnumCode}
*
* @param value
* @return
*/
private EnumCode getValuedEnum(int value) {
EnumCode[] codes = type.getEnumConstants();
for (EnumCode em : codes) {
if (em.getCode() == value) {
return em;
}
}
throw new IllegalArgumentException(
"Cannot convert " + value + " to " + type.getSimpleName() + " by value.");
}
}

以上就完成我们自定义的类型处理器。接下来就看看我们如果在xml中使用类型处理器:

1
2
3
4
5
6
7
<resultMap>
<result column="platform" jdbcType="TINYINT" property="platform" typeHandler="cn.finegames.commons.integrate.EnumCodeTypeHandler"/>
</resultMap>

<if test="platform != null">
#{platform,jdbcType=TINYINT, typeHandler=cn.finegames.commons.integrate.EnumCodeTypeHandler},
</if>

在声明resultMap和进行插入、修改时使用上面形式就可以了。

还有一种比较常用的情况,如果在where语句中使用enum,而不是通过方法传入值该怎么处理?这种情况我也搞了很久,查了很多资料,终于找到对应的方案…

1
token_type = ${@cn.xxx.TokenTypeEnum@ACCESS.getCode()}

使用OGNL表达式就可以了。

坚持原创技术分享,更多深度分析、实践代码,您的支持将鼓励我继续创作!