I have problems in mapping custom collection with JPA (Hiberante provider). For example when I am using object with attribute
List<Match> matches;
with
<one-to-many name="matches">
<cascade>
<cascade-all />
</cascade>
</one-to-many>
in my ORM file, it is allright; But if I replace "List matches;" by
private Matches matches;
,where "Matches" is defined like:
public class Matches extends ArrayList<Match> {
private static final long serialVersionUID = 1L;
}
It produces following error:
Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: by.sokol.labs.jpa.MatchBox.matches
Thanks for your attention!
You can, but you have to refer to it as one of the common collections - List
or Set
.
so:
private List matches = new Matches();
Why? Because Hibernate makes proxies to your collections to enable lazy loading, for example. So it creates PersistentList
, PersistentSet
and PersistentBag
, which are List
but aren't Matches
. So, if you want to add additional methods to that collection - well, you can't.
Check this article for more details.
You have a solution, however. Don't use inheritance, use composition. You can, for example, add a method to your entity called getMatchesCollection()
(in addition to the traditional getter), which looks like:
public Matches getMatchesCollection() {
return new Matches(matches);
}
And your Matches
class would look like (using google-collections' ForwardingList
):
public class Matches extends ForwardingList {
private List<Match> matches;
public Matches(List<Match> matches) { this.matches = matches; }
public List<Match> delegate() { return matches; }
// define your additional methods
}
If you can't use google collections, simply define the ForwardingList
yourself - it's calling all the methods of the underlying List
If you don't need any additional methods to operate on the structure, then don't define a custom collection.
Hibernate requires persistent collection-valued fields to be declared as an interface type (because they will be replaced with Hibernate's implementation for lazy loading purposes). From the reference documentation:
6.1. Persistent collections
Hibernate requires that persistent collection-valued fields be declared as an interface type. For example:
public class Product { private String serialNumber; private Set parts = new HashSet(); public Set getParts() { return parts; } void setParts(Set parts) { this.parts = parts; } public String getSerialNumber() { return serialNumber; } void setSerialNumber(String sn) { serialNumber = sn; } }
The actual interface might be
java.util.Set
,java.util.Collection
,java.util.List
,java.util.Map
,java.util.SortedSet
,java.util.SortedMap
or anything you like ("anything you like" means you will have to write an implementation oforg.hibernate.usertype.UserCollectionType
.)Notice how the instance variable was initialized with an instance of
HashSet
. This is the best way to initialize collection valued properties of newly instantiated (non-persistent) instances. When you make the instance persistent, by callingpersist()
for example, Hibernate will actually replace theHashSet
with an instance of Hibernate's own implementation ofSet
.
So your second approach is not possible, at least not the way you declared it. But to be honest, I don't really see the point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With