将Hibernate中的枚举转换为自定义数值
问题
在编码过程中,经常会遇到用某个数值来表示某种状态、类型或者阶段的情况,比如有这样一个枚举:
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
。
探索
首先,我们先看看Hibernate是否能够满足我们的需求。
Hibernate内置了org.hibernate.type.EnumType
转换器,可将枚举转换为Named
或Ordinal
。
这样使用它:
// 将ComputerState.OPEN转换OPEN
@Enumerated(EnumType.STRING)
private ComputerState state;
// ComputerState.OPEN转换为0,ComputerState.CLOSE转换为1
@Enumerated(EnumType.STRING)
private ComputerState state;
以上的两种方式不能满足我们的需求,看起来要自己实现转换的过程了。
准备工作
首先,我们需要做一些准备工作,便于在枚举和code
之间转换。
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(Class<E> enumClass, int code) {E[] enumConstants = enumClass.getEnumConstants();for (E e : enumConstants) {if (e.getCode() == code)return e;}return null;}
}
至此,准备工作完成。接下来需要在Hibernate中完成对枚举的转换。
方案1:AttributeConverter
Hibernate提供了javax.persistence.AttributeConverter<X,Y>
接口指定如何将实体属性转换为数据库列表示。
此方案适用与数量不多或者个别特殊的枚举。
需要实现两个方法:
public Y convertToDatabaseColumn (X attribute);
该方法指定如何将实体属性转换为数据库列属性public X convertToEntityAttribute (Y dbData);
该方法指定如何将数据库列属性转换为实体属性
我是这样实现的:
public class CodeEnumConverter implements AttributeConverter<ComputerState,Integer> {@Overridepublic Integer convertToDatabaseColumn(ComputerState attribute) {return attribute.getCode();}@Overridepublic ComputerState convertToEntityAttribute(Integer dbData) {return CodeEnumUtil.codeOf(ComputerState.class,dbData);}
}
这样使用:
@Convert( converter = CodeEnumConverter.class )
private ComputerState state;
方案2:UserType
除了AttributeConverter
还提供了一个用户自定义类型的接口:org.hibernate.usertype.UserType
。
注意! 这里的类型不是一个实际的属性类型,而是一个知道如何将数据类型序列化到JDBC的类!
此方案适用于具有相似行为的一组枚举。
需要实现以下方法:
public int[] sqlTypes()
返回由该类型映射列的SQL类型代码。public Class returnedClass()
指定由SQL类型转换成哪种数据类型public boolean equals(Object x, Object y) throws HibernateException;
数据类型之间的比对public int hashCode(Object x) throws HibernateException;
将数据类型转换为HashCodepublic Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException;
从JDBC ResultSet读取数据,将其转换为数据类型后返回,需要处理NULL值。public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException;
将数据类型转换为SQL类型public Object deepCopy(Object value) throws HibernateException;
深度拷贝public boolean isMutable();
类型是否可变public Serializable disassemble(Object value) throws HibernateException;
将对象转换为可缓存的表示形式public Object assemble(Serializable cached, Object owner) throws HibernateException;
从缓存中重建一个对象。public Object replace(Object original, Object target, Object owner) throws HibernateException;
在合并过程中,将正在合并的实体中的现有(目标)值替换为正在合并的分离实体的新(原始)值。
我是这样实现的(参考了org.hibernate.type.EnumType
):
public class CodeEnumType<E extends Enum<?> & BaseCodeEnum> implements UserType, DynamicParameterizedType {private static final int SQL_TYPE = Types.INTEGER;private static final String ENUM = "enumClass";private Class<E> enumClass;@Overridepublic void setParameterValues(Properties parameters) {final ParameterType reader = (ParameterType) parameters.get(PARAMETER_TYPE);if (reader != null) {enumClass = reader.getReturnedClass().asSubclass(Enum.class);} else {final String enumClassName = (String) parameters.get(ENUM);try {enumClass = ReflectHelper.classForName(enumClassName, this.getClass()).asSubclass(Enum.class);} catch (ClassNotFoundException exception) {throw new HibernateException("Enum class not found: " + enumClassName, exception);}}}@Overridepublic int[] sqlTypes() {return new int[]{SQL_TYPE};}@Overridepublic Class returnedClass() {return enumClass;}@Overridepublic boolean equals(Object x, Object y) throws HibernateException {return x == y;}@Overridepublic int hashCode(Object x) throws HibernateException {return x == null ? 0 : x.hashCode();}@Overridepublic E nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {final int value = rs.getInt(names[0]);return rs.wasNull() ? null : codeOf(value);}@Overridepublic void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {st.setObject(index, ((BaseCodeEnum) value).getCode(), SQL_TYPE);}@Overridepublic Object deepCopy(Object value) throws HibernateException {return value;}@Overridepublic boolean isMutable() {return false;}@Overridepublic Serializable disassemble(Object value) throws HibernateException {return (Serializable) value;}@Overridepublic Object assemble(Serializable cached, Object owner) throws HibernateException {return cached;}@Overridepublic Object replace(Object original, Object target, Object owner) throws HibernateException {return original;}private E codeOf(int code) {try {return CodeEnumUtil.codeOf(enumClass, code);} catch (Exception ex) {throw new IllegalArgumentException("Cannot convert " + code + " to " + enumClass.getSimpleName() + " by code value.", ex);}}}
其中实现了DynamicParameterizedType.setParameterValues
方法,是为了获取具体的子类。
这样使用:
@Type(type = "com.example.CodeEnumType")
private ComputerState state;
结束了
好久没有摸Hibernate了,生疏了很多。如果你还有更优的解决方案,请一定在评论中告知,万分感激。
在Mybatis中使用枚举可以看这里
参考资料:
Hibernate User Guide
将Hibernate中的枚举转换为自定义数值相关推荐
- LabVIEW2020 使用“扫描字符串”函数将字符串中的数字转换为十进制数值
目录 一.案例 二.前面板 三.程序框图 四.验证 一.案例 想把数值输入控件中的数字转换成字符串. 例如:字符串输入控件输入30,想转换成十进制数值30. 二.前面板 1.在前面板窗口上添加一个字符 ...
- pandas编写自定义函数高亮显示(highlight)dataframe中的指定内容(数值)(highlighting a specific values or content of a panda
pandas编写自定义函数高亮显示(highlight)dataframe中的指定内容(数值)(highlighting a specific values or content of a panda ...
- 西门子S7-1200PLC石灰反应釜程序,西门子触摸屏画面,程序采用FB块设计,自定义块中模拟量处理,数值转换
西门子S7-1200PLC石灰反应釜程序,西门子触摸屏画面,程序采用FB块设计,自定义块中模拟量处理,数值转换,电机控制,时间设置均采用SCL语言编写,子程序功能很全,包括与变频器通讯,程序同时设有与 ...
- python基于模型的预测概率和标签信息可视化ROC曲线、编写自定义函数计算约登值、寻找最佳阈值(threshold、cutoff)、可视化ROC曲线并在曲线中标记最佳阈值及其数值标签
python基于模型的预测概率和标签信息可视化ROC曲线.编写自定义函数计算约登值.寻找最佳阈值(threshold.cutoff).可视化ROC曲线并在曲线中标记最佳阈值及其数值标签 目录
- python编写自定义函数计算约登值(约登指数、Youden Index)、寻找最佳阈值(threshold、cutoff)、可视化ROC曲线并在曲线中标记最佳阈值及其数值标签
python编写自定义函数计算约登值(约登指数.Youden Index).寻找最佳阈值(threshold.cutoff).可视化ROC曲线并在曲线中标记最佳阈值及其数值标签 目录
- Groovy中转换成java,Groovy将字符串类型转换为自定义类型的方法
将一个字符串转换为自定义类型: 例如Quantity是自定义的一个class,现在想这么调用 Quantity quantity = "100个" as Quantity 或 de ...
- 在JavaScript中定义枚举的首选语法是什么? [关闭]
在JavaScript中定义枚举的首选语法是什么? 就像是: my.namespace.ColorEnum = {RED : 0,GREEN : 1,BLUE : 2 }// later onif(c ...
- 从C#中的枚举获取int值
我有一堂课,叫做Questions (复数). 在此类中,有一个名为Question (单数)的枚举,它看起来像这样. public enum Question {Role = 2,ProjectFu ...
- c++ main函数调用 类中的枚举_为啥用枚举,枚举有哪些用法?
Java基础:枚举的用法与原理 在学习过程中,我们也只是在定义常量的时候,会意识到枚举的存在,而定义常量其实可以在类中实现,这时就会感觉枚举有点鸡肋.但在实际项目开发的过程中,枚举因相当迷人的特性而受 ...
最新文章
- BZOJ 2748: [HAOI2012]音量调节【二维dp,枚举】
- 利用matlab画混淆矩阵(confusion matrix)
- 《探索需求》阅读笔记1
- python观察日志(part7)--可变长参数元祖
- 怎么在Guitar Pro乐谱中加入哇音
- 这个时代再也难出现贵子
- 广西教育培训网(Gxpx365)2018公务员全员培训考试参考+学法用法答案搜索工具
- 剑指offer第二版(C++实现)
- 阿里中间件-全链路压测 总结
- PHP安装教程及相关说明
- nginx 配置443端口
- 滚动 下拉简单实现分页
- 解决 ZLibrary 登录/注册不了的问题
- python学习之-- 协程
- hdu 4009 Transfer water(最小树形图模板)
- android adc,Android配置ADC接口
- 程序员二本毕业在华为外包工作3年,晒出收入和存款,还以为看错了!
- 【ICPC模板】卡迈克尔函数
- 如何优雅地下载计算机领域英文文献--dblp的食用方法(多图警告)
- 00后南航大二学生自制火箭,成功发射后回收
热门文章
- html 自定义属性_五道自测题-你我都应知道的HTML小知识
- abaqus失效单元删除_[转载]abaqus单元删除的一般方法
- cout不明确什么意思_年轻人不讲武德是什么梗和意思 年轻人不讲武德梗出处
- python3 asyncio 不阻塞_Python中的并发处理之asyncio包使用的详解
- es6html模板,js 字符串模板 ES6
- 可编程led灯带原理_技术分享:二极管发光原理与LED灯带
- python 逐行读取文件_Python fileinput模块:逐行读取多个文件
- 双路服务器单路运行,架构设计-具体案例求解惑:两个单路服务器比一个双路服务器性能高100%?...
- C语言中188 10取模等于多少,C语言编程:任取x为十进制整数,编程将x转换成对应的八进制数后输出。...
- 车载wince系统刷界面ui_UI入门秘笈,你想知道吗?