JPA Lazy Fetch is not working when getting collection

I'm getting a collection of objects and trying to get the object with LAZY loading in a @ManyToOne relation. But, when I calling the service method, my object from the collection gets the NULL value

List<Location> all = locationRepository.getLocations(ids);
Merchant merchant = all.get(0).getMerchant();
// merchant == null

LocationRepository.java

@Repository
public interface LocationRepository extends JpaRepository<Location, String> {

    @Query(value = "SELECT * FROM b_location WHERE id IN :ids", nativeQuery = true)
    List<Location> getLocations(@Param("ids") Set<String> ids);
    }

Location.java

@Entity
@Table(name = "b_location")
public class Location {

    @Id
    @Column(name = "id")
    private String id;

    @Column(name = "merchant_id", nullable = false)
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private Long merchantId;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "merchant_id", referencedColumnName = "id", insertable = false, updatable = false)
    private Merchant merchant;

    @Column(name = "is_active")
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private boolean isActive;

Merchant.java

@Entity
@Table(name = "b_merchant")
public class Merchant {

    @Id
    @Column(name = "id")
    private Long id;

    @Column(name = "merchant_name", nullable = false)
    private String merchantName;

    @Column(name ="is_premium", columnDefinition = "boolean default false", nullable = false)
    private boolean isPremium;

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @OneToMany(mappedBy = "merchant", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Set<Location> shops;

What i was trying to do:

1) Calling another query, example:

   @Query("SELECT l, l.merchant FROM Location l LEFT JOIN FETCH l.merchant WHERE l.id IN :ids")
    List<Location> getLocations(@Param("ids") List<String> ids);

and

@Query("SELECT l FROM Location l LEFT JOIN FETCH l.merchant WHERE l.id IN :ids")
        List<Location> getLocations(@Param("ids") List<String> ids);

and

@Query("from Location l left join fetch l.merchant where l.id IN (:ids)")
List<Location> getLocations(@Param("ids") List<String> ids);

2) Changing FetchType to everything possible (fetch = FetchType.EAGER)

3) Using

List<T> findAll(Iterable<ID> ids);
// and
List<Location> findByIdIn(List<String> ids);

P.S.

When I'm getting only one object, it's working very good. For example:

Merchant merchant = locationRepository.findOne("11111").getMerchant();

UPDATE

Turns out my understanding of the underlying problem was not correct. Before the getting collection I was using locationRepository.save(location); operation. As it turns out JPA has a couple of levels of caching. My problem was solved cache cleaning using EntityMananager, for example:

entityManager.clear();

More info here: Invalidating JPA EntityManager session

But as long as my question was not correctly asked, I suggest that Maciej Kowalski gave the wright answer. Thanks

Upvotes: 1

Views: 2915

Answers (1)

Maciej Kowalski
Maciej Kowalski

Reputation: 26492

1) You are using a native query here:

@Query(value = "SELECT * FROM b_location WHERE id IN :ids", nativeQuery = true)

No lazy loading will work in that case. The result object has no association with the Persistence Context whatsoever.

2) FetchType.LAZY is only a hint to the persistence provider. It does not have to make the association lazy, it may decide to fetch it eagerly.

3) In your case you do not even need a custom query. This should work:

List<Location> findByIdIn(List<String> ids);

Upvotes: 5

Related Questions