摘要:内置了转换器,可将枚举转换为或。接下来需要在中完成对枚举的转换。方案提供了接口指定如何将实体属性转换为数据库列表示。此方案适用与数量不多或者个别特殊的枚举。在合并过程中,将正在合并的实体中的现有目标值替换为正在合并的分离实体的新原始值。
问题
在编码过程中,经常会遇到用某个数值来表示某种状态、类型或者阶段的情况,比如有这样一个枚举:
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; } @Override public int getCode() { return this.code; } }3. 编写一个转换工具类
现在我们能顺利的将枚举转换为某个数值了,还需要一个工具将数值转换为枚举实例。
public class CodeEnumUtil { public static& BaseCodeEnum> E codeOf(Class enumClass, int code) { E[] enumConstants = enumClass.getEnumConstants(); for (E e : enumConstants) { if (e.getCode() == code) return e; } return null; } }
至此,准备工作完成。接下来需要在Hibernate中完成对枚举的转换。
方案1:AttributeConverterHibernate提供了javax.persistence.AttributeConverter
此方案适用与数量不多或者个别特殊的枚举。
需要实现两个方法:
public Y convertToDatabaseColumn (X attribute);
该方法指定如何将实体属性转换为数据库列属性
public X convertToEntityAttribute (Y dbData);
该方法指定如何将数据库列属性转换为实体属性
我是这样实现的:
public class CodeEnumConverter implements AttributeConverter{ @Override public Integer convertToDatabaseColumn(ComputerState attribute) { return attribute.getCode(); } @Override public 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;
将数据类型转换为HashCode
public 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& BaseCodeEnum> implements UserType, DynamicParameterizedType { private static final int SQL_TYPE = Types.INTEGER; private static final String ENUM = "enumClass"; private Class enumClass; @Override public 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); } } } @Override public int[] sqlTypes() { return new int[]{SQL_TYPE}; } @Override public Class returnedClass() { return enumClass; } @Override public boolean equals(Object x, Object y) throws HibernateException { return x == y; } @Override public int hashCode(Object x) throws HibernateException { return x == null ? 0 : x.hashCode(); } @Override public 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); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { st.setObject(index, ((BaseCodeEnum) value).getCode(), SQL_TYPE); } @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } @Override public 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
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68440.html
摘要:本文速览本篇文章是我为接下来的源码分析系列文章写的一个导读文章。年该项目从基金会迁出,并改名为。同期,停止维护。符号所在的行则是表示的执行结果。同时,使用无需处理受检异常,比如。另外,把写在配置文件中,进行集中管理,利于维护。 1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章。本篇文章从 MyBatis 是什么(what),为什么要使用(why),...
摘要:自带对枚举的处理类该类实现了枚举类型和类型的相互转换。而在具体中也需要使用属性,如在处理到该位置时,就会调用指定的处理类来处理枚举类型。 mybatis自带对枚举的处理类 org.apache.ibatis.type.EnumOrdinalTypeHandler :该类实现了枚举类型和Integer类型的相互转换。 但是给转换仅仅是将对应的枚举转换为其索引位置,也就是ordinal(...
摘要:如何解决呢在中我们可以使用方式来干预的创建过程,来完成转换器的指定。再也不用写的配置文件了结束了以上就是我对如何在中优雅的使用枚举的探索。 问题 在编码过程中,经常会遇到用某个数值来表示某种状态、类型或者阶段的情况,比如有这样一个枚举: public enum ComputerState { OPEN(10), //开启 CLOSE(11), ...
摘要:在接口服务开发中,难免会校验传入方的参数校验,尤其在请求时,验证字符长度,字符类型是否满足数据库中字段的最大长度及类型,如果不符合条件应及时拦截并返回,避免后续的流程。 showImg(https://segmentfault.com/img/remote/1460000018664784); 公司转java开发也有一段时间了,在实际开发过程中还是会遇到一些问题的,本篇主要记录下接口服...
摘要:结构型模式适配器模式桥接模式装饰模式组合模式外观模式享元模式代理模式。行为型模式模版方法模式命令模式迭代器模式观察者模式中介者模式备忘录模式解释器模式模式状态模式策略模式职责链模式责任链模式访问者模式。 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 v1.1 2018-03-12 增加新技术知识、完善知识体系 v2.0 2019-02-19 结构...
阅读 1629·2021-09-26 09:55
阅读 5166·2021-09-22 15:40
阅读 1978·2019-08-30 15:53
阅读 1468·2019-08-30 11:15
阅读 1686·2019-08-29 15:41
阅读 1814·2019-08-28 18:13
阅读 3111·2019-08-26 12:00
阅读 1639·2019-08-26 10:30