Documentation / Mapping / Collections
Recommendation - Use List
The recommendation is to use List
to map collection properties
of @OneToMany
or @ManyToMany
.
This means we avoid implicit use of hashCode() / equals()
which
occurs with the use of Set
.
List vs Set
Hibernate bag semantics
With Hibernate there could be a preference for using Set
because Set and List have
different semantics with Hibernate. Set uses "bag semantics" and is often preferred with Hibernate.
hashCode() / equals()
Using Set by implication means that hashCode()/equals()
implementation is used.
This is problematic as the nature of entity beans is that they a mutating and often don't have
an @Id value until after the bean has been persisted.
The difficulty in implementing hashCode()/equals() on entity beans means that List
is the recommended collection type to hold @OneToMany and @ManyToMany collections.
@Entity
@Table(name="customer")
public class Customer extends BaseModel {
...
// List is recommended for collection types
@OneToMany(mappedBy="customer", cascade=CascadeType.PERSIST)
List<Contact> contacts;
...
Enhancement
When you define a collection type enhancement will ensure:
- Any List/Set initialisation is removed
- The List/Set is always initialised by Ebean (and never null)
List/Set initialisation is removed
// initialisation of the new ArrayList() is removed
@OneToMany(mappedBy="customer")
List<Contact> contacts = new ArrayList<Contact>;
// you can declare an un-initialised List if you wish
// and there is no actual difference to an initialised one
// because enhancement will always initialise it
@OneToMany(mappedBy="customer")
List<Contact> contacts;
In Kotlin we'd typically always want to define it as being not null:
// kotlin: contacts type not nullable
@OneToMany(mappedBy = "customer")
var contacts: MutableList<Contact> = ArrayList()
The List/Set is always initialised (never null)
Ebean needs to control the initialisation of the List/Set in order to support:
- Lazy loading
- Support @PrivateOwned where we need the list/set to be aware of removals
- Support @ManyToMany where we need the list/set to be aware of adds and removals
Enhancement ensures that whenever you access a List/Set Ebean will always initialise it and setup the List/Set to listen for adds/removals if necessary (for @PrivateOwned and @ManyToMany).
This has the effect that accessing the list/set it will always be not null.
// it "looks" like contacts could be null ...
@OneToMany(mappedBy="customer")
List<Contact> contacts;
public void addContact(Contact contact) {
// but actually contacts will never be null here
if (contacts == null) {
contacts = new ArrayList<>();
}
contacts.add(contact);
}
Enhancement replaces all the GETFIELD
instructions for persistent collections
and replaces that with code that ensures the List/Set is initialised if required.
@OneToMany(mappedBy="customer")
List<Contact> contacts;
public void addContact(Contact contact) {
// this is safe to write as contacts
// will never be null here
contacts.add(contact);
}