Hibernate能否映射继承List的接口?映射报错咨询
这个问题我之前也碰到过,Hibernate对集合类型的映射确实有它的一套规则,咱们一步步拆解原因和解决办法。
为啥会报这个错?
Hibernate在处理@OneToMany这类集合映射时,要求被映射的属性必须是标准的集合接口(比如List、Set)或者它们的具体实现类。你定义的FooList虽然继承了List<Bar>,但它是个自定义接口,Hibernate不知道该怎么实例化它——毕竟接口本身不能直接创建对象,所以就抛出了Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements这个错误。
具体解决方案
方案1:改用标准集合类型(最简单直接)
如果不是必须要用FooList接口,直接把属性类型换成List<Bar>或者具体的实现类(比如ArrayList<Bar>)就行:
public class Baz { @OneToMany private List<Bar> fooList = new ArrayList<>(); // 记得加上getter和setter }
这样Hibernate就能正常识别并自动实例化集合了,完美解决报错。
方案2:保留自定义接口,指定具体实现类
如果你业务上必须用FooList接口,那得给它写一个具体实现类,再告诉Hibernate用这个类来实例化集合:
- 先写
FooList的实现类:
public class FooListImpl extends ArrayList<Bar> implements FooList { // 这里可以添加你的自定义方法,或者直接继承ArrayList的所有实现逻辑 }
- 然后在
Baz的属性上用@CollectionType注解指定实现类的全限定名:
import org.hibernate.annotations.CollectionType; public class Baz { @OneToMany @CollectionType(type = "com.yourpackage.FooListImpl") // 替换成你实际的包路径+类名 private FooList fooList; // getter和setter不能少 }
这下Hibernate就知道要创建FooListImpl对象来作为fooList的实例了。
方案3:自定义集合类型处理器(适配特殊需求)
如果你的FooList有特殊的业务逻辑需要定制(比如自定义加载、序列化逻辑),可以实现Hibernate的UserCollectionType接口,自己控制集合的生命周期:
- 实现
UserCollectionType接口(这里给出核心逻辑示例):
import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.usertype.UserCollectionType; import java.util.Collection; import java.util.Iterator; public class FooListType implements UserCollectionType { @Override public PersistentCollection instantiate(SharedSessionContractImplementor session, org.hibernate.persister.collection.CollectionPersister persister) { // 返回自定义的持久化集合包装类,需要继承PersistentList并实现FooList return new PersistentFooList(session); } @Override public PersistentCollection wrap(SharedSessionContractImplementor session, Object collection) { return new PersistentFooList(session, (FooList) collection); } @Override public Iterator<?> getElementsIterator(Object collection) { return ((Collection<?>) collection).iterator(); } @Override public boolean contains(Object collection, Object entity) { return ((Collection<?>) collection).contains(entity); } @Override public Object indexOf(Object collection, Object entity) { return ((List<?>) collection).indexOf(entity); } @Override public Object replaceElements(Object original, Object target, org.hibernate.persister.collection.CollectionPersister persister, Object owner, java.util.Map copyCache, SharedSessionContractImplementor session) { FooList targetList = (FooList) target; targetList.clear(); targetList.addAll((Collection<? extends Bar>) original); return target; } @Override public Object instantiate(int anticipatedSize) { return new FooListImpl(); } }
(注:PersistentFooList需要继承org.hibernate.collection.spi.PersistentList并实现FooList接口,这里就不展开细节了)
- 在映射中指定这个自定义类型:
public class Baz { @OneToMany @CollectionType(type = "com.yourpackage.FooListType") // 替换成你的类型全限定名 private FooList fooList; // // getter和setter }
总结
一般情况下,方案1和方案2就足够解决问题了,方案3适合有特殊集合逻辑的场景,按需选择就行。
内容的提问来源于stack exchange,提问作者lanoxx

