Hibernate user type with Annotation - 话题延续

本文探讨了Hibernate中自定义UserType的实现方法,并对比官方提供的GenericEnumUserType,分析了自定义实现的优势,包括减少参数配置、提高性能及简化枚举处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间写了一篇介绍hibernate user type的文章: [url]http://www.iteye.com/topic/214145[/url],很长时间没有看了,最近发现有人回帖说:

[quote="downpour"]http://www.hibernate.org/272.html

hibernate官方论坛早有实现。还是通用的GenericEnumUserType。不是很明白,为什么碰到问题总是喜欢自己实现,而不去google一下别人早已实现了很多遍并经过实践证明的东西。[/quote]

本想跟贴继续讨论,可是写着写着发现内容比较多,就干脆开个新贴讨论吧,想知道前因的朋友请去看前一个帖子。

好,下面开始本帖的正式话题:

downpour说的官方实现我也知道,我写这个UserType是出于如下考虑:

1. 当然是为了讲解如何编写自定义的hibernate UserType。
2. 为enum定义通用接口便于编写通用util方法(后面会展开讲)。

首先我得承认,对比我这个实现,官方的GenericEnumUserType覆盖面更广,但是使用的时候也有如下不足之处:

1. 需要处理的parameter太多。针对我这个例子,如果要使用GenericEnumUserType的话,注释就得这么写:

@org.hibernate.annotations.TypeDef(
name = "status",
typeClass = de.besitec.octopus.domain.support.GenericEnumUserType.class,
parameters = {
@org.hibernate.annotations.Parameter(
name = "enumClass",
value = "com.yourpackage.Status"),
@org.hibernate.annotations.Parameter(
name = "identifierMethod",
value = "getId"),
@org.hibernate.annotations.Parameter(
name = "valueOfMethod",
value = "findEnumById"),
})

而且status类中的id变量必须为Integer。

对于少量的Enum或者是通用的Enum(注释可以写到package-info.java里面去)还好说。一旦enum的数量一多,使用起来岂不是比我的方案麻烦很多?

2. 性能相对较差。GenericEnumUserType实现里调用的反射方法比我写的这个要多,大家知道调用反射方法要比正常的方法调用慢大概10倍左右,如果系统中enum比较多,全部使用这个UserType的话,应该对性能有一定的影响。这一点只是猜测,具体我没有测试,如果有朋友有兴趣测试的话,请告诉我结果,不过请注意,测试的前提是enum比较多。

3. 持久化enum的特例毕竟有限,我们目前的系统除了要保存老的标识符(int)或者是该enum的描述(String)以外,还没有碰到其他的特例,因此编写两个UserType就足够了。日后同事使用起来也方便,毕竟需要的parameter要少很多。

至于为什么说“为enum定义通用接口便于编写通用helper方法”呢?解释起来要费点唇舌,让我们先回过头来看看status的代码:


public enum Status implements DescriptionID {

OPEN(5, "This object is activated"),
CLOSED(9, "This object is deactivated");

private int id;
private String description;

private Status(int statusNr, String description) {
this.id = statusNr;
this.description = description;
}

public String getDescription() {
return this.description;
}

public int getId() {
return id;
}

public static List<Status> getAll() {
return Arrays.asList(Status.class.getEnumConstants());
}

public static Status findById(Integer id) {
for (Status status : getAll()) {
if (id == status.getId()) {
return status;
}
}
return null ;
}
}


注意,在老帖子里getAll()写的不好,这里已经纠正了。

仔细看一下status的代码,我们会发现每一个类似的enum类都必须要写findById()这个方法,里面的逻辑代码是完全一样的,完全可以提出到一个util类中去。如果不定义通用接口的话,就必须要用反射来编写。前面说了,过度使用反射会带来性能问题,因此这里建议使用通用接口。

Okay,针对我们这个例子,EnumUtils可以这么些

public class EnumUtils {

public static <I extends DescriptionID> I getEnum(Class<I> type, int id) {
I[] types = type.getEnumConstants();
for (I t : types) {
if (t.getId() == id)
return t;
}
throw new AssertionError("unable to map: " + id + " to enum: "
+ type.getSimpleName());
}

public static <I extends DescriptionID> I getEnum(Class<I> type, String description) {
I[] types = type.getEnumConstants();
for (I t : types) {
if (StringUtils.equals(t.getDescription(), description))
return t;
}
throw new AssertionError("unable to map: " + description + " to enum: "
+ type.getSimpleName());
}

/**
* @param <I>
* @param type
* @return the enum description list
*/
public static <I extends DescriptionID> List<String> getDescriptions(Class<I> type) {
I[] types = type.getEnumConstants();
List<String> result = new ArrayList<String>(types.length);
for (I t : types) {
result.add(t.getDescription());
}
return result;
}
}

三个方法一次为:
1. 根据所给id返回对应enum实例.
2. 根据所给Description返回对应enum实例.
3. 返回所有enum实例的description.

其他方法大家可以自由发挥。

那个静态的findById()方法仍然可以保留,不过逻辑代码已经全部移出:


public static Status getEnumById(Integer id) {
return EnumUtils.getEnum(Status.class, id);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值