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; 
    }

    @Override
    public 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> {
	@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的类!

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

需要实现以下方法:

  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;

	@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.kingbal.CodeEnumType")
private ComputerState state;

  • 34
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hibernate是一个开源的Java对象持久化框架,它提供了一种方便的方式来将Java对象映射到数据库中的表,从而实现对象的持久化。下面是对Hibernate进行详细解释。 首先,Hibernate是基于ORM(对象关系映射)的技术。ORM是一种编程技术,它允许开发人员将Java对象与数据库表进行映射。通过Hibernate,开发人员只需要在Java类的注解或XML配置文件中定义对象的映射关系,就能够实现对象与数据库之间的转换。 其次,Hibernate提供了丰富的查询语言。Hibernate提供了一种称为HQL(Hibernate Query Language)的查询语言,它类似于SQL,但更加面向对象。开发人员可以利用HQL进行复杂的查询操作,而不需要直接编写SQL语句。 此外,Hibernate还支持事务管理。在Hibernate中,开发人员可以通过事务来管理数据库操作的一致性和原子性。通过使用Hibernate的事务管理,开发人员能够确保多个数据库操作在一个事务中执行,避免了数据不一致的问题。 另外,Hibernate还具有缓存功能。Hibernate提供了一级缓存和二级缓存,开发人员可以利用这些缓存来提高数据访问的性能。一级缓存是Hibernate默认的缓存机制,它将查询操作的结果缓存到Session对象中,减少了与数据库的交互次数。二级缓存是可选的,它将查询操作的结果缓存到SessionFactory中,用于多个Session之间的共享。 最后,Hibernate还具备透明的数据库操作功能。通过Hibernate,开发人员可以使用面向对象的方式进行数据库操作,而不需要关心底层数据库的细节。Hibernate会自动处理数据库连接、SQL语句的生成和执行等操作,极大地简化了数据库操作的过程。 综上所述,Hibernate作为一种Java对象持久化技术,提供了方便的对象与数据库之间的映射方式,丰富的查询语言,事务管理,缓存机制和透明的数据库操作功能。它的出现极大地简化了开发人员对数据库的操作,提高了开发效率和代码的可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值