I think after some research I'm able to answer my own question. The key is that Hibernate dehydrates objects in the 2LC and this will influence whether you need to explicitly cache the associations.
Please add more details if I've missed anything or got any detail wrong.
Here's a simplified class structure:
@Entity
@Cacheable
public class Tutor
{
@OneToMany(mappedBy="tutor")
private Set<Student> students;
}
@Entity
@Cacheable
public class Student
{
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="TUTOR_FK")
private Tutor tutor;
}
For Hibernate
The Second Level Cache (2LC) stores entities in a dehydrated form, eg:
{1=CacheEntry(Tutor)[1, Jack Daw]}
{1=CacheEntry(Student)[1, Jane Smith, 1]}
The final 1 in the Student data is the foreign key to tutor.
So if you have a reference to Student id 1 and follow the reference to the tutor, we get a cache hit because the foreign key is in the cache. No extra select is needed.
However, if you go the other way, calling getStudents() on the Tutor, there is no foreign key in the 2LC so a select is needed. (once the select completes, hibernate has the ids and can start hitting the 2LC).
To avoid this, you will need to add the old org.hibernate.annotations.Cache annotation to the @OneToMany relationship.
For EclipseLink
I don't have an EclipseLink installation to hand so I can't test this but I understand that EclipseLink stores the 2LC data in the form of the original object graph, so the above isn't relevant. As per Chris' answer, the entity is cached with its references so further annotating isn't needed.