Maiko Kingma
Maiko Kingma

Reputation: 939

When does hibernate load mapped relations

I am creating a spring boot application and use it's build in JpaRepository interface to store my entities. I have the following two entities (removed getters and setters for readability):

Profile entity

@Entity
public class Profile {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @OneToMany(mappedBy = "profileOne", orphanRemoval = true)
    private List<Match> matchOnes;

    @OneToMany(mappedBy = "profileTwo", orphanRemoval = true)
    private List<Match> matchTwos;
}

Match entity

@Entity
@Table(uniqueConstraints={
    @UniqueConstraint(columnNames = { "profileOne_id", "profileTwo_id" })
}) 
public class Match {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ManyToOne
    @JoinColumn(name = "profileOne_id")
    private Profile profileOne;

    @ManyToOne
    @JoinColumn(name = "profileTwo_id")
    private Profile profileTwo;
}

To understand the JpaRepository behavior I wrote the following unit test.

@RunWith(SpringRunner.class)
@DataJpaTest
public class IProfileDaoTest {

    @Autowired
    private IProfileDao profileDao; //This class extends JpaRepository<Profile, long>

    @Autowired
    private IMatchDao matchDao; //This class extends JpaRepository<Match, long>

    @Test
    public void saveProfileTest() throws Exception {

    @Test
    public void profileMatchRelationTest() throws Exception {
        //Test if matches stored by the IMatchDao are retrievable from the IProfileDao
        Profile profileOne = new Profile("Bob"),
            profileTwo = new Profile("Alex");
        profileDao.saveAndFlush(profileOne);
        profileDao.saveAndFlush(profileTwo);
        matchDao.saveAndFlush(new Match(profileOne, profileTwo));
        profileOne = profileDao.getOne(profileOne.getId());
        Assert.assertEquals("Match not retrievable by profile.", 1, profileOne.getMatchOnes().size());
    }
}

Now I expected the matches to have appeared in the profile entity but they do not. I also tried adding CascadeType.ALL to the @ManyToOne annotation in the match entity and adding FetchType.EAGER to the @OneToMany annotation in the profile entity.

Is it possible to get the matches saved with the matchDao by requesting a profile in the profileDao? Or should I find the matches with a profile with a separate function?

Upvotes: 0

Views: 71

Answers (2)

SrThompson
SrThompson

Reputation: 5748

Spring Data repositories don't write to the database immediately for performance (and probably other) reasons. In tests, if you need to test query methods you need to use the TestEntityManager provided by @DataJpaTest (it's just the entity manager that the repositories use anyway in the background, but with a few convenience methods for testing).

Update 1: The matches aren't added to the profile. To make sure the relationship is bidirectional the matches should have the profiles but the profiles should also have the matches.

Try this:

@RunWith(SpringRunner.class)
@DataJpaTest
public class IProfileDaoTest {

    @Autowired
    private IProfileDao profileDao; //This class extends JpaRepository<Profile, long>

    @Autowired
    private IMatchDao matchDao; //This class extends JpaRepository<Match, long>

    @Autowired
    private TestEntityManager testEntityManager;

    @Test
    public void saveProfileTest() throws Exception {

    @Test
    public void profileMatchRelationTest() throws Exception {
        //Test if matches stored by the IMatchDao are retrievable from the IProfileDao
        Profile profileOne = new Profile("Bob"),
            profileTwo = new Profile("Alex");

        //Persist the profiles so they exist when they are added to the match
        entityManager.persistAndFlush(profileOne);
        entityManager.persistAndFlush(profileTwo);

        //Create and persist the match with two profiles
        Match yourMatch = entityManager.persistFlushFind(new Match(profileOne, profileTwo));

        //Add the match to both profiles and persist them again.
        profileOne.matchOnes.add(yourMatch);
        entityManager.persistAndFlush(profileOne);
        profileTwo.matchTwos.add(yourMatch);
        entityManager.persistAndFlush(profileTwo);

        profileOne = profileDao.getOne(profileOne.getId());
        Assert.assertEquals("Match not retrievable by profile.", 1, profileOne.getMatchOnes().size());
    }
}

Upvotes: 1

Jens Schauder
Jens Schauder

Reputation: 81970

Everything in your test happens in the same JPA session. Such a session guarantees that every entity is included only once. So when you execute

profileOne = profileDao.getOne(profileOne.getId());

you are getting the exact instance back you created 5 lines above. Hibernate nor any other JPA implementation will change anything in the entity for loading.

If you want to actually reload an entity you'll have to either evict it first from the entity manager or use a fresh Session/EntityManager.

For more details see chapter 3 of the JPA specification.

Upvotes: 0

Related Questions