让AnnotationConfiguration支持自定义UserCollectionType

在Hibernate的Jira上,这个两个issue已经放了很久了:
[url=http://opensource.atlassian.com/projects/hibernate/browse/ANN-141]Add annotation support for serCollectionType[/url]
[url=http://opensource.atlassian.com/projects/hibernate/browse/ANN-636]Add the annotations to map the User Collection Type[/url]
但是官方一直不给解决,咋办呢?以前唯一的办法就是不用Annotation,回到hbm文件的时代。

经过我几天的跟踪Hibernate的源代码,终于找到了解决办法,在这里分享给大家。

如果我们有这样的实体


@Entity
public class Cat {

@Id
@GeneratedValue
private long id;
String name;

@ManyToOne
@JoinColumn(name = "children")
Cat mother;

@OneToMany(mappedBy = "mother")
QSet<Cat> children = new QHashSet<Cat>();
}

public interface QSet<E> extends Set<E> {
// my stuff
}


直接拿给Hibernate持久,毫无疑问会遇到错误。但是之前用hbm文件的时候,是可以配置的。参见

[url="http://www.javalobby.org/java/forums/m91832311.html"]Hibernate: Custom Collection Types[/url]

核心的部分就是一行

<set name="subordinates" collection-type="com.javalobby.tnt.hib.FastSetType">


这个设置到了Annotation时代,要么是消失了,要么是我鼠目寸光,找不到。结果是,再也不能试用自定义的Collection类型了。那么我们来看看Hibernate报了什么错误吧:

[quote]
"Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: "
[/quote]

通过跟踪源代码,可以发现。之所以会出现这个错误,是因为Hibernate的反射系统认为QSet这个类型不是Collection。再进一步跟踪,发现只有Set.class, List.class这些才被EJB3ReflectionManager认为是Collection。所以第一步,就是通过继承,让ReflectionManager认为QSet也是Collection。


public class CustomizableEJB3ReflectionManager extends EJB3ReflectionManager {

private JavaXTypeConverter xTypeConverter = new NoopJavaXTypeConverter();

@Inject
public CustomizableEJB3ReflectionManager(JavaXTypeConverter xTypeConverter) {
this.xTypeConverter = xTypeConverter;
}

@Override
public JavaXType toXType(TypeEnvironment context, Type propType) {
// do things differently here, to make it aware of QSet
PublicJavaXType converted = xTypeConverter.convert(context, propType, this);
if (converted != null) {
return converted;
}
return super.toXType(context, propType);
}
}


这样,我们就可以通过第一步了。但是仍然会出错,这回的错误是session.save的时候Hibernate尝试用自己的包装过的collection来代替你手工new出来的collection的时候出错,Hibernate找不到合适的setter。当然嘛,Hibernate认为类型时Set.class,但是实际类型是QSet.class,显然会出错。

为了解决这个问题,还是要像过去一样通过配置一个UserCollectionType。好在Hibernate的Annotation配置和Hbm配置都是一张皮,肉都是一样,都是Configuration这个对象。通过

private void processValues(AnnotationConfiguration config, ValueProcessor processor) {
Iterator classMappings = config.getClassMappings();
while (classMappings.hasNext()) {
PersistentClass classMapping = (PersistentClass) classMappings.next();
Iterator properties = classMapping.getPropertyIterator();
while (properties.hasNext()) {
Property property = (Property) properties.next();
Value value = property.getValue();
processor.process(value);
}
}
}

我们可以拿到所有映射的类(其实不完全是,还有subclass和component,不过简便起见,略过)的属性的映射类。然后再:
[code]
public class SetValueProcessor implements ValueProcessor {

public void process(Value value) {
if (!(value instanceof Set)) {
return;
}
Set set = (Set) value;
if (set.getTypeName() != null) {
return;
}
set.setTypeName(QueryableSetType.class.getName());
}
}

public class QueryableSetType implements UserCollectionType {
// ...
}
[/code]
我们就可以把我们定义的UserCollectionType附加上去了。而且这样还有一个好处,不需要在所有使用的地方都去声明UserCollectionType,只需要用代码去遍历一遍Configuration就搞定了。具体的代码参加:
[url]http://javaonhorse.googlecode.com/svn/trunk/[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值