简体   繁体   中英

Micronaut data problem lazily fetching related entities

I am currently in the process of incorporating micronaut data into an existing micronaut project (as part of upgrading it) and I am experiencing some problems regarding lazily fetching related collections (with a OneToMany relation).

I have the following entity structure:

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class Merchant implements Serializable {

  @Id private String name;

  private String displayName;

  private String staticSiteAddress;

  private String emailFromAddress;

  private String emailFromName;

  private String etmEmailFromAddress;

  private String etmEmailFromName;

  private String phone;

  private String username;

  @Embedded private PasswordHash password;

  @ToString.Exclude
  @EqualsAndHashCode.Exclude
  @OneToMany(mappedBy = "merchant", fetch = FetchType.LAZY)
  @Builder.Default
  private Set<MerchantStore> stores = new HashSet<>();

  @ToString.Exclude
  @EqualsAndHashCode.Exclude
  @OneToMany(mappedBy = "merchant", fetch = FetchType.LAZY)
  @Builder.Default
  private Set<CustomWebLink> customWebLinks = new HashSet<>();

  @ToString.Exclude
  @EqualsAndHashCode.Exclude
  @OneToMany(mappedBy = "merchant", fetch = FetchType.LAZY)
  @Builder.Default
  private Set<LoyaltyPlan> loyaltyPlans = new HashSet<>();

  @ToString.Exclude
  @EqualsAndHashCode.Exclude
  @OneToMany(mappedBy = "merchant", fetch = FetchType.LAZY)
  @Builder.Default
  private Set<MerchantPref> merchantPrefs = new HashSet<>();

}

With in on hand I am able to fetch the first level related entities fine (ie merchantPrefs ) with no issue using the following repository method:

@NonNull
@Override
@Cacheable("merchant")
@Join(value = "stores", type = Join.Type.LEFT_FETCH)
@Join(value = "customWebLinks", type = Join.Type.LEFT_FETCH)
@Join(value = "merchantPrefs", type = Join.Type.LEFT_FETCH)
@Join(value = "loyaltyPlans", type = Join.Type.LEFT_FETCH)
Optional<Merchant> findById(@NonNull @NotNull String s);

However when I try to fetch an innermost collection for example awards from this entity:

@Data
@Entity
public class LoyaltyPlan implements Serializable {

  @EmbeddedId private LoyaltyPlanID id;

  @ToString.Exclude
  @EqualsAndHashCode.Exclude
  @ManyToOne(fetch = FetchType.LAZY)
  @MapsId("merchant")
  @JoinColumn(name = "merchant", nullable = false)
  private Merchant merchant;

  private String displayName;

  private String emailFromName;

  private String emailFromAddress;

  private String uniqueId;

  private boolean defaultPlan;

  private float pointsFactor;

  private boolean hasGiftCards;

  private boolean hasLoyaltyCards;

  private boolean usesName;

  private boolean hasEmail;

  private boolean hasCellPhone;

  private boolean needsGiftCards;

  private boolean needsLoyaltyCards;

  private boolean needsName;

  private boolean needsEmail;

  private boolean needsCellPhone;

  private int signupBonus;

  private int closingAwardThreshold;

  private int maxPointsPerOrder;

  private int minPricePerOrder;

  private int recentHistoryDays;

  private int expiringCouponReminderDays;

  @ToString.Exclude
  @EqualsAndHashCode.Exclude
  @OneToMany(mappedBy = "loyaltyPlan", fetch = FetchType.LAZY)
  private List<LoyaltyPlanAward> awards;
}

I get a LazyInitializationException .

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: fts.marketing.entities.plan.LoyaltyPlan.awards, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
    at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387)
    at java.base/java.lang.Iterable.forEach(Iterable.java:74)
    at fts.marketing.TestController.doSomething(TestController.java:29)
    at fts.marketing.$TestControllerDefinition$Intercepted.$$access$$doSomething(Unknown Source)
    at fts.marketing.$TestControllerDefinition$$exec1.invokeInternal(Unknown Source)
    at io.micronaut.context.AbstractExecutableMethod.invoke(AbstractExecutableMethod.java:146)
    at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:73)
    at io.micronaut.transaction.interceptor.TransactionalInterceptor.intercept(TransactionalInterceptor.java:134)
    at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:82)
    at fts.marketing.$TestControllerDefinition$Intercepted.doSomething(Unknown Source)
    at fts.marketing.$TestControllerDefinition$$exec1.invokeInternal(Unknown Source)
    at io.micronaut.context.AbstractExecutableMethod.invoke(AbstractExecutableMethod.java:146)
    at io.micronaut.context.DefaultBeanContext$4.invoke(DefaultBeanContext.java:474)
    at io.micronaut.web.router.AbstractRouteMatch.execute(AbstractRouteMatch.java:312)
    at io.micronaut.web.router.RouteMatch.execute(RouteMatch.java:118)
    at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$buildResultEmitter$10(RoutingInBoundHandler.java:1369)
    at io.reactivex.internal.operators.flowable.FlowableDefer.subscribeActual(FlowableDefer.java:35)
    at io.reactivex.Flowable.subscribe(Flowable.java:14935)
    at io.reactivex.Flowable.subscribe(Flowable.java:14882)
    at io.micronaut.http.server.context.ServerRequestContextFilter.lambda$doFilter$0(ServerRequestContextFilter.java:62)
    at io.reactivex.internal.operators.flowable.FlowableFromPublisher.subscribeActual(FlowableFromPublisher.java:29)
    at io.reactivex.Flowable.subscribe(Flowable.java:14935)
    at io.reactivex.Flowable.subscribe(Flowable.java:14885)
    at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$buildExecutableRoute$6(RoutingInBoundHandler.java:1074)
    at io.micronaut.web.router.DefaultUriRouteMatch$1.execute(DefaultUriRouteMatch.java:80)
    at io.micronaut.web.router.RouteMatch.execute(RouteMatch.java:118)
    at io.micronaut.http.server.netty.RoutingInBoundHandler.handleRouteMatch(RoutingInBoundHandler.java:698)
    at io.micronaut.http.server.netty.RoutingInBoundHandler.channelRead0(RoutingInBoundHandler.java:554)
    at io.micronaut.http.server.netty.RoutingInBoundHandler.channelRead0(RoutingInBoundHandler.java:1

Furthemore this seems to be a sporadic issue, as sometimes the same code seems to be working OK. I am not really sure what is wrong and I think I have modelled the relations correctly as those have been fine before migrating to micronaut-data .

I do not know what I am doing wrong here and any help would highly appreciated in order to resolve this.

In Micronaut the concept of OpenSessionInView doesn't exist. It seems that you are trying to access to the relation when the transaction is already closed. Look at this: https://micronaut-projects.github.io/micronaut-sql/latest/guide/index.html#_understanding_lazyinitializationexception

Solutions:

  1. Don't use Lazy. Instead go to the DB with a repository every time you need the list.
  2. Use EAGER.
  3. Use @Transactional in the method that make the query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM