给一个网站,哪有免费的网站,photoshop在线工具,精品网站建设需要多少钱From: https://segmentfault.com/a/1190000010755321
问题
在编码过程中#xff0c;经常会遇到用某个数值来表示某种状态、类型或者阶段的情况#xff0c;比如有这样一个枚举#xff1a;
public enum ComputerState {OPEN(10), //开启CLOSE(11), //关闭O…From: https://segmentfault.com/a/1190000010755321
问题
在编码过程中经常会遇到用某个数值来表示某种状态、类型或者阶段的情况比如有这样一个枚举
public enum ComputerState {OPEN(10), //开启CLOSE(11), //关闭OFF_LINE(12), //离线FAULT(200), //故障UNKNOWN(255); //未知private int code;ComputerState(int code) { this.code code; }
}通常我们希望将表示状态的数值存入数据库即ComputerState.OPEN存入数据库取值为10。
探索
首先我们先看看MyBatis是否能够满足我们的需求。 MyBatis内置了两个枚举转换器分别是org.apache.ibatis.type.EnumTypeHandler和org.apache.ibatis.type.EnumOrdinalTypeHandler。
EnumTypeHandler
这是默认的枚举转换器该转换器将枚举实例转换为实例名称的字符串即将ComputerState.OPEN转换OPEN。
EnumOrdinalTypeHandler
顾名思义这个转换器将枚举实例的ordinal属性作为取值即ComputerState.OPEN转换为0,ComputerState.CLOSE转换为1。 使用它的方式是在MyBatis配置文件中定义
typeHandlerstypeHandler handlerorg.apache.ibatis.type.EnumOrdinalTypeHandler javaTypecom.example.entity.enums.ComputerState/
/typeHandlers
以上的两种转换器都不能满足我们的需求所以看起来要自己编写一个转换器了。
方案
MyBatis提供了org.apache.ibatis.type.BaseTypeHandler类用于我们自己扩展类型转换器上面的EnumTypeHandler和EnumOrdinalTypeHandler也都实现了这个接口。
1. 定义接口
我们需要一个接口来确定某部分枚举类的行为。如下
public interface BaseCodeEnum {int getCode();
}
该接口只有一个返回编码的方法返回值将被存入数据库。
2. 改造枚举
就拿上面的ComputerState来实现BaseCodeEnum接口
public enum ComputerState implements BaseCodeEnum{OPEN(10), //开启CLOSE(11), //关闭OFF_LINE(12), //离线FAULT(200), //故障UNKNOWN(255); //未知private int code;ComputerState(int code) { this.code code; }Overridepublic int getCode() { return this.code; }
}
3. 编写一个转换工具类
现在我们能顺利的将枚举转换为某个数值了还需要一个工具将数值转换为枚举实例。
public class CodeEnumUtil {public static E extends Enum? BaseCodeEnum E codeOf(ClassE enumClass, int code) {E[] enumConstants enumClass.getEnumConstants();for (E e : enumConstants) {if (e.getCode() code)return e;}return null;}
}
4. 自定义类型转换器
准备工作做的差不多了是时候开始编写转换器了。BaseTypeHandlerT 一共需要实现4个方法
void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
用于定义设置参数时该如何把Java类型的参数转换为对应的数据库类型
T getNullableResult(ResultSet rs, String columnName)
用于定义通过字段名称获取字段数据时如何把数据库类型转换为对应的Java类型
T getNullableResult(ResultSet rs, int columnIndex)
用于定义通过字段索引获取字段数据时如何把数据库类型转换为对应的Java类型
T getNullableResult(CallableStatement cs, int columnIndex)
用定义调用存储过程后如何把数据库类型转换为对应的Java类型
我是这样实现的
public class CodeEnumTypeHandlerE extends Enum? BaseCodeEnum extends BaseTypeHandlerBaseCodeEnum {private ClassE type;public CodeEnumTypeHandler(ClassE type) {if (type null) {throw new IllegalArgumentException(Type argument cannot be null);}this.type type;}Overridepublic void setNonNullParameter(PreparedStatement ps, int i, BaseCodeEnum parameter, JdbcType jdbcType)throws SQLException {ps.setInt(i, parameter.getCode());}Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {int code rs.getInt(columnName);return rs.wasNull() ? null : codeOf(code);}Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {int code rs.getInt(columnIndex);return rs.wasNull() ? null : codeOf(code);}Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {int code cs.getInt(columnIndex);return cs.wasNull() ? null : codeOf(code);}private E codeOf(int code){try {return CodeEnumUtil.codeOf(type, code);} catch (Exception ex) {throw new IllegalArgumentException(Cannot convert code to type.getSimpleName() by code value., ex);}}
}5. 使用
接下来需要指定哪个类使用我们自己编写转换器进行转换在MyBatis配置文件中配置如下
typeHandlerstypeHandler handlercom.example.typeHandler.CodeEnumTypeHandler javaTypecom.example.entity.enums.ComputerState/
/typeHandlers
搞定 经测试ComputerState.OPEN被转换为10,ComputerState.UNKNOWN被转换为255达到了预期的效果。
6. 优化
在第5步时我们在MyBatis中添加typeHandler用于指定哪些类使用我们自定义的转换器一旦系统中的枚举类多了起来MyBatis的配置文件维护起来会变得非常麻烦也容易出错。如何解决呢 在Spring中我们可以使用JavaConfig方式来干预SqlSessionFactory的创建过程来完成转换器的指定。思路
再写一个能自动匹配转换行为的转换器通过sqlSessionFactory.getConfiguration().getTypeHandlerRegistry()取得类型转换器注册器再使用typeHandlerRegistry.setDefaultEnumTypeHandler(Class? extends TypeHandler typeHandler)将第一步的转换器注册成为默认的
首先我们需要一个能确定转换行为的转换器AutoEnumTypeHandler.java public class AutoEnumTypeHandlerE extends EnumE extends BaseTypeHandlerE {private BaseTypeHandler typeHandler null;public AutoEnumTypeHandler(ClassE type) {if (type null) {throw new IllegalArgumentException(Type argument cannot be null);}if(BaseCodeEnum.class.isAssignableFrom(type)){// 如果实现了 BaseCodeEnum 则使用我们自定义的转换器typeHandler new CodeEnumTypeHandler(type);}else {// 默认转换器 也可换成 EnumOrdinalTypeHandlertypeHandler new EnumTypeHandler(type);}}Overridepublic void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {typeHandler.setNonNullParameter(ps,i, parameter,jdbcType);}Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {return (E) typeHandler.getNullableResult(rs,columnName);}Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return (E) typeHandler.getNullableResult(rs,columnIndex);}Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return (E) typeHandler.getNullableResult(cs,columnIndex);}
}
接下来我们需要干预SqlSessionFactory的创建过程将刚刚的转换器指定为默认的
Configuration
ConfigurationProperties(prefix mybatis)
public class MyBatisConfig {private String configLocation;private String mapperLocations;Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource,JSONArrayHandler jsonArrayHandler,JSONObjectHandler jsonObjectHandler) throws Exception {SqlSessionFactoryBean factory new SqlSessionFactoryBean();factory.setDataSource(dataSource);// 设置配置文件及mapper文件地址ResourcePatternResolver resolver new PathMatchingResourcePatternResolver();factory.setConfigLocation(resolver.getResource(configLocation));factory.setMapperLocations(resolver.getResources(mapperLocations));SqlSessionFactory sqlSessionFactory factory.getObject();// 取得类型转换注册器TypeHandlerRegistry typeHandlerRegistry sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();// 注册默认枚举转换器typeHandlerRegistry.setDefaultEnumTypeHandler(AutoEnumTypeHandler.class);return sqlSessionFactory;}// ... getter setter
}搞定 这样一来如果枚举实现了BaseCodeEnum接口就使用我们自定义的CodeEnumTypeHandler如果没有实现BaseCodeEnum接口就使用默认的。再也不用写MyBatis的配置文件了
结束了
以上就是我对如何在MyBatis中优雅的使用枚举的探索。如果你还有更优的解决方案请一定在评论中告知万分感激。