JPA leaves join requests not instantiating child collections in Spring Boot?

Just a quick question, I am using JPA with spring boot. I have a class member with @ManyToMany (FetchType.LAZY) relationship to Artist class. Members do not need to have artists, but artists must have members.

In my JpaRepository extension , I have a method to fetch members and any possible child executors it may have. Looks like that:

@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 children B, collection B is returned as null. I've used this approach with a JavaEE project before, and then this kind of query would always give me an empty collection, not just NULL. Is spring boot differently?

My question is, is there anything I can do to make it return an empty collection instead of null?

Here is object 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 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;

      

The @Id field is in a superclass and is of type Long

+3


source to share


1 answer


By default, JPA should really return an empty set in the scenario you described. However, looking at your entities, I suspect there is a scenario that could produce the result null

:

  • It looks like your objects are not initializing the list with artists

    an empty collection when you create an item.
  • Create and save an instance Member

  • If, in the same JPA session, you tried to download Member

    using the repository method getMemberWithArtists

    , then indeed the collection member.artists

    would be null

    . This is caused by the fact that the instance is being Member

    returned from the JPA session cache (the one that was created in step (2) where it member.artists

    was equal null

    due to the lack of default initialization for the collection)

I tried to illustrate this in the following piece of code:



    @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 really works for me. (in case of a test JPA session for the whole test. In a real web application, you have to configure the JPA session to continue for only one request).

I believe that simply initializing member.artists

an empty collection in the object's constructor Member

should solve the problem for newly created and persisted objects (when loaded in the same JPA session ).



    public Member() {
        this.artists = new ArrayList();
    }


      

Hope it helps.

+2


source







All Articles