1

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

9
  • You should change your query to "SELECT l FROM Location l ...". Your getLocations method returns List of Location entities Commented Jul 20, 2019 at 11:11
  • As for second option you have to do it in transactional method. Try to use some Merchang getter in this method -> locationRepository.findOne("11111").getMerchant().getId(); Commented Jul 20, 2019 at 11:17
  • I think it is because of your custom query or method, but why don't you use findAllByIds docs.spring.io/spring-data/jpa/docs/current/api/org/… Commented Jul 20, 2019 at 11:18
  • @ alex valuiskyi Thanks. I appreciate your help ("SELECT l FROM Location l ..." and @Transactional). Unfortunately it still doesn' work Commented Jul 20, 2019 at 11:19
  • @Deadpool I have tried to use your comment (List<T> findAll(Iterable<ID> ids);) but it didn't work. Thanks Commented Jul 20, 2019 at 11:28

1 Answer 1

5

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);
Sign up to request clarification or add additional context in comments.

6 Comments

In my version JPA i have only this method (List<T> findAll(Iterable<ID> ids);. I have tried to use it, but it didn't work
You can declare method signature in you repository interface and use it. Framework creates method implementation
@alexvaluiskyi Thanks, but this option doesn't work also
@MaciejKowalski do you mean i should remove this (FetchType.LAZY) ? ))
No, it simply states that you may not achieve the lazy loading on an manytoany association.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.