问题

在编码过程中,经常会遇到用某个数值来表示某种状态、类型或者阶段的情况,比如有这样一个枚举:

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转换器,可将枚举转换为NamedOrdinal
这样使用它:

// 将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>接口指定如何将实体属性转换为数据库列表示。

此方案适用与数量不多或者个别特殊的枚举。

需要实现两个方法:

  1. public Y convertToDatabaseColumn (X attribute);
    该方法指定如何将实体属性转换为数据库列属性
  2. 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的类!

此方案适用于具有相似行为的一组枚举。

需要实现以下方法:

  1. public int[] sqlTypes()
    返回由该类型映射列的SQL类型代码。
  2. public Class returnedClass()
    指定由SQL类型转换成哪种数据类型
  3. public boolean equals(Object x, Object y) throws HibernateException;
    数据类型之间的比对
  4. public int hashCode(Object x) throws HibernateException;
    将数据类型转换为HashCode
  5. public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException;
    从JDBC ResultSet读取数据,将其转换为数据类型后返回,需要处理NULL值。
  6. public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException;
    将数据类型转换为SQL类型
  7. public Object deepCopy(Object value) throws HibernateException;
    深度拷贝
  8. public boolean isMutable();
    类型是否可变
  9. public Serializable disassemble(Object value) throws HibernateException;
    将对象转换为可缓存的表示形式
  10. public Object assemble(Serializable cached, Object owner) throws HibernateException;
    从缓存中重建一个对象。
  11. 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中的枚举转换为自定义数值相关推荐

  1. LabVIEW2020 使用“扫描字符串”函数将字符串中的数字转换为十进制数值

    目录 一.案例 二.前面板 三.程序框图 四.验证 一.案例 想把数值输入控件中的数字转换成字符串. 例如:字符串输入控件输入30,想转换成十进制数值30. 二.前面板 1.在前面板窗口上添加一个字符 ...

  2. pandas编写自定义函数高亮显示(highlight)dataframe中的指定内容(数值)(highlighting a specific values or content of a panda

    pandas编写自定义函数高亮显示(highlight)dataframe中的指定内容(数值)(highlighting a specific values or content of a panda ...

  3. 西门子S7-1200PLC石灰反应釜程序,西门子触摸屏画面,程序采用FB块设计,自定义块中模拟量处理,数值转换

    西门子S7-1200PLC石灰反应釜程序,西门子触摸屏画面,程序采用FB块设计,自定义块中模拟量处理,数值转换,电机控制,时间设置均采用SCL语言编写,子程序功能很全,包括与变频器通讯,程序同时设有与 ...

  4. python基于模型的预测概率和标签信息可视化ROC曲线、编写自定义函数计算约登值、寻找最佳阈值(threshold、cutoff)、可视化ROC曲线并在曲线中标记最佳阈值及其数值标签

    python基于模型的预测概率和标签信息可视化ROC曲线.编写自定义函数计算约登值.寻找最佳阈值(threshold.cutoff).可视化ROC曲线并在曲线中标记最佳阈值及其数值标签 目录

  5. python编写自定义函数计算约登值(约登指数、Youden Index)、寻找最佳阈值(threshold、cutoff)、可视化ROC曲线并在曲线中标记最佳阈值及其数值标签

    python编写自定义函数计算约登值(约登指数.Youden Index).寻找最佳阈值(threshold.cutoff).可视化ROC曲线并在曲线中标记最佳阈值及其数值标签 目录

  6. Groovy中转换成java,Groovy将字符串类型转换为自定义类型的方法

    将一个字符串转换为自定义类型: 例如Quantity是自定义的一个class,现在想这么调用 Quantity quantity = "100个" as Quantity 或 de ...

  7. 在JavaScript中定义枚举的首选语法是什么? [关闭]

    在JavaScript中定义枚举的首选语法是什么? 就像是: my.namespace.ColorEnum = {RED : 0,GREEN : 1,BLUE : 2 }// later onif(c ...

  8. 从C#中的枚举获取int值

    我有一堂课,叫做Questions (复数). 在此类中,有一个名为Question (单数)的枚举,它看起来像这样. public enum Question {Role = 2,ProjectFu ...

  9. c++ main函数调用 类中的枚举_为啥用枚举,枚举有哪些用法?

    Java基础:枚举的用法与原理 在学习过程中,我们也只是在定义常量的时候,会意识到枚举的存在,而定义常量其实可以在类中实现,这时就会感觉枚举有点鸡肋.但在实际项目开发的过程中,枚举因相当迷人的特性而受 ...

最新文章

  1. BZOJ 2748: [HAOI2012]音量调节【二维dp,枚举】
  2. 利用matlab画混淆矩阵(confusion matrix)
  3. 《探索需求》阅读笔记1
  4. python观察日志(part7)--可变长参数元祖
  5. 怎么在Guitar Pro乐谱中加入哇音
  6. 这个时代再也难出现贵子
  7. 广西教育培训网(Gxpx365)2018公务员全员培训考试参考+学法用法答案搜索工具
  8. 剑指offer第二版(C++实现)
  9. 阿里中间件-全链路压测 总结
  10. PHP安装教程及相关说明
  11. nginx 配置443端口
  12. 滚动 下拉简单实现分页
  13. 解决 ZLibrary 登录/注册不了的问题
  14. python学习之-- 协程
  15. hdu 4009 Transfer water(最小树形图模板)
  16. android adc,Android配置ADC接口
  17. 程序员二本毕业在华为外包工作3年,晒出收入和存款,还以为看错了!
  18. 【ICPC模板】卡迈克尔函数
  19. 如何优雅地下载计算机领域英文文献--dblp的食用方法(多图警告)
  20. 00后南航大二学生自制火箭,成功发射后回收

热门文章

  1. html 自定义属性_五道自测题-你我都应知道的HTML小知识
  2. abaqus失效单元删除_[转载]abaqus单元删除的一般方法
  3. cout不明确什么意思_年轻人不讲武德是什么梗和意思 年轻人不讲武德梗出处
  4. python3 asyncio 不阻塞_Python中的并发处理之asyncio包使用的详解
  5. es6html模板,js 字符串模板 ES6
  6. 可编程led灯带原理_技术分享:二极管发光原理与LED灯带
  7. python 逐行读取文件_Python fileinput模块:逐行读取多个文件
  8. 双路服务器单路运行,架构设计-具体案例求解惑:两个单路服务器比一个双路服务器性能高100%?...
  9. C语言中188 10取模等于多少,C语言编程:任取x为十进制整数,编程将x转换成对应的八进制数后输出。...
  10. 车载wince系统刷界面ui_UI入门秘笈,你想知道吗?