Hibernate实战_笔记34(映射枚举、实现EnhanceUserType接口)

映射枚举

枚举类型是一个常见的Java惯用语,其中类有着(小)数量不变的不可变实例。

1、在JDK5.0中使用枚举

如果使用JDK5.0,可以给类型安全的枚举使用内建的语言支持。例如,Rating类看起来如下:
package auction.model;
public enum Rating{
  EXCELLENT, OK, BAD
}
Comment类有这个类型的一个属性:
public class Comment{
    ...
    private Rating rating;
    private Item auction;
    ...
}

2、编写定制的枚举处理程序

现在要介绍的不是最基础的UserType接口,而是EnhanceUserType接口。
public class StringEnumUserType implements EnhancedUserType, ParameterizedType {

	private Class<Enum> enumClass;

	public void setParameterValues(Properties parameters) {
		String enumClassName = parameters.getProperty("enumClassName");

		try {
			enumClass = ReflectHelper.classForName(enumClassName);
		} catch (ClassNotFoundException e) {
			throw new HibernateException("Enum class not found", e);
		}
	}

	// //映射Java值类型
	public Class returnedClass() {
		return enumClass;
	}

	// 告诉Hibernate要使用什么SQL列类型生成DDL模式
	public int[] sqlTypes() {
		return new int[] { StringType.INSTANCE.sqlType() };
	}

	// 这个类型是否可变
	public boolean isMutable() {
		return false;
	}

	// 创建值的快照
	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}


	// 以序列化的形保存信息的数据的高速缓存
	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) value;
	}

	// 高速缓存的数据转变为MonetaryAmount的一个实例
	public Object assemble(Serializable cached, Object owner)
			throws HibernateException {
		return cached;
	}

	// 处理脱管对象状态的合并
	public Object replace(Object original, Object target, Object owner)
			throws HibernateException {
		return original;
	}

	public boolean equals(Object x, Object y) throws HibernateException {
		if (x == y)
			return true;
		if (null == x || null == y)
			return false;
		return x.equals(y);
	}

	public int hashCode(Object x) throws HibernateException {
		return x.hashCode();
	}

	public Object fromXMLString(String xmlValue) {
		return Enum.valueOf(enumClass, xmlValue);
	}

	public String objectToSQLString(Object value) {
		return ‘\‘‘ + ((Enum) value).name() + ‘\‘‘;
	}

	public String toXMLString(Object value) {
		return ((Enum) value).name();
	}

	public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
			throws HibernateException, SQLException {
		String name = rs.getString(names[0]);
		return rs.wasNull() ? null : Enum.valueOf(enumClass, name);
	}
	
	public void nullSafeSet(PreparedStatement st, Object value, int index)
			throws HibernateException, SQLException {
		if (null == value) {
			st.setNull(index, StringType.INSTANCE.sqlType());
		} else {
			st.setString(index, ((Enum) value).name());
		}
	}
}

3、用XML和注解映射枚举

在XML映射中,首先创建一个定制的类型定义:
<typedef class = "persistence.StringEnumUserType"
    name = "rating">
    <param name="enumClassName">auction.model.Rating</param>
</typedef>
现在可以在Comment类映射中使用名为rating的类型:
<property name = "rating"
    column = "RATING"
    type = "rating"
    not-null = "true"
    update = "false"
    access = "field"/>
另一方面,可以依赖Java Persistence提供程序持久化枚举。如果在类型java.lang.Enum中的一个被注解的实体类中有一个属性(例如Comment中的rating),并且它没有标记为@Transient或者transient(Java关键字),Hibernate JPA实现必须毫无怨言地保持该属性为开箱即用;它有一个处理这个的内建类型。这个内建的映射类型在数据库中必须默认为枚举的一种表示法。两种最常见的选择是字符串表示法,就像通过定制类型或者有序表示法与原生的Hibernate实现一样。有序表示法保存选中的枚举选项的位置:例如,1为EXCELLENT,2为OK,3为BAD。数据库列也默认为一个数字列。可以在属性上通过Enumerated注解改变这个默认的枚举映射:
public class Comment{
    ...
    @Enumerated(EnumType.STRING)
    @Column(name = "RATING", nullable = false, updatable = false)
    private Rating rating;
}
现在已经切换到了基于字符串的表示法,实际上是与你的定制类型可以读写的相同的表示法。也可以使用JPA XML描述符:
<entity class="auction.model.Item" access="PROPERTY">
    <attributes>
        ...
        <basic name = "rating">
            <column name = "RATING" nullable = "false" updatable = "false"/>
            <enumerated>STRING</enumerated>
        </basic>
    </attributes>
</entity>

4、用定制映射类型查询

你可能遇到的进一步问题是在Hibernate查询中使用被枚举的类型。例如,考虑获取所有等级为"bad"的评语的HQL格式的下列查询:
Query q = 
    session.createQuery(
        "from Comment c where c.rating = auction.model.Rating.BAD"
    );
如果把枚举持久化为字符串,虽然这个查询也有效(查询解析器用枚举值作为常量),但是如果选择了有序表示法,它就不起作用了。必须使用一个绑定参数,并对比较式通过编程来设置等级值:
Query q = 
    session.createQuery("from Comment c where c.rating = :rating");
Properties params = new Properties();
params.put("enumClassname",
    "auction.model.Rating");
q.setParameter("rating",Rating.BAD,
    Hibernate.custom(StringEnumUserType.class, params)
);
本例的最后一行使用静态的辅助方法Hibernate.custom(),来把定制的映射类型转化为Hibernate Type;这是告诉Hibernate有关枚举映射,以及如何处理Rating.BAD值的一种简单方法。注意,你还必须告诉Hibernate有关被参数化的类型可能需要的任何初始化属性。

Hibernate实战_笔记34(映射枚举、实现EnhanceUserType接口),布布扣,bubuko.com

Hibernate实战_笔记34(映射枚举、实现EnhanceUserType接口)

上一篇:Hibernate实战_笔记33(创建CompositeUserType、参数化定制类型)


下一篇:Hibernate实战_笔记32(考虑定制的映射类型、创建UserType)