Reputation: 231
Just a quick question, I am using JPA with spring boot. I have a class Member with a @ManyToMany(FetchType.LAZY) relationship to class Artist. Members does not have to have Artists but Artists needs to have Members.
In my JpaRepository extension I have a method for fetching Members and any possible Artist children it may have. Looks like this:
@Query("select m from Member m left join fetch m.artists where m.id=:id")
Member getMemberWithArtists(@Param("id") long memberId);
In the case where A has no B children the B collection comes back as null. I was using this approach with a JavaEE project before and then this kind of query would always give me an empty collection instead of just NULL. Is spring boot handling this differently?
My question is, is there anything I can do so that it returns an empty collection instead of null?
Here is Entity A
@Entity
@Table(name="MEMBER")
public class Member extends LocationHolder implements Messenger {
@ManyToMany(cascade={CascadeType.MERGE,CascadeType.REFRESH}, fetch=FetchType.LAZY)
@JoinTable(name="MEMBER_ARTIST_REL",
joinColumns=@JoinColumn(name="MEMBER_ID", referencedColumnName="ENTITY_ID"),
inverseJoinColumns=@JoinColumn(name="ARTIST_ID", referencedColumnName="ENTITY_ID"))
private List<Artist> artists;
Here is B
@Entity
@Table(name="ARTIST")
public class Artist extends LocationHolder {
@ManyToMany(mappedBy="artists", targetEntity=Member.class, fetch=FetchType.LAZY, cascade={MERGE, REFRESH})
private List<Member> members;
@Id field is in the superclass and is of type Long
Upvotes: 3
Views: 960
Reputation: 771
By default JPA should indeed return an empty set in the scenario you have described. However, looking at your entities I suspect that there is a scenario which could lead to the null
result :
artists
list to an empty collection when an member is created.Member
member
using getMemberWithArtists
repository method then, indeed, the member.artists
collection would have been null
. This is caused by the fact that the member
instance is returned from the JPA session cache (the one created in the step (2) where member.artists
was equal to null
due to missing default initialization of the collection)I have tried to illustrate same in the code snippet bellow :
@Test
public void testNullCollection() {
Member member = new Member();
memberRepository.save(member);
member = memberRepository.getMemberWithArtists(member.getId());
// Here the collection is going to be null since the instance is returned from the session cache
assertNull(member.getArtists());
entityManager.flush();
entityManager.clear();
// Here the collection is going to be empty, since in the previous step the session was cleared hence forcing JPA to initialize the entity from scratch and inject lazy collection
member = memberRepository.getMemberWithArtists(member.getId());
assertNotNull(member.getArtists());
assertTrue(member.getArtists().isEmpty());
}
This integration test actually passes for me. (in case of test JPA session spans for the whole test. In the actual web application you should configure the JPA session to last only for a single request).
I believe that just initializing the member.artists
to an empty collection in the constructor of the Member
entity should solve the issue for the newly created and persisted entities (when loaded within the same JPA session).
public Member() {
this.artists = new ArrayList();
}
Hope this helps.
Upvotes: 2