spring-attic / spring-cloud-gcp

Integration for Google Cloud Platform APIs with Spring
Apache License 2.0
704 stars 696 forks source link

Spanner Lazy Loading seems not to be working #2649

Closed shinytruth closed 3 years ago

shinytruth commented 3 years ago

Hello, I am currently trying to use spring-cloud-gcp-starter-data-spanner and spring-cloud-gcp-starter

Springboot v2.3.3

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-gcp-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
    </dependency>

The basic CRUD features are tested and it works fine.

My question is about joining tables.

I made a very simple example to demonstrate my case.

This is my "Order" entity which has multiple "OrderItem" entities.

@Table(name="orders")
@Data
@ToString(of = {"id", "description", "timestamp"})
public class Order {
  @PrimaryKey
  @Column(name="order_id")
  private String id;

  private String description;

  @Column(name="creation_timestamp")
  private LocalDateTime timestamp;

  @Interleaved(lazy = true)
  private List<OrderItem> items = new ArrayList<>();

  public Order() {
    this.id = UUID.randomUUID().toString();
    this.timestamp = LocalDateTime.now();
  }

  public Order(String description, OrderItem... orderItems) {
    this.id = UUID.randomUUID().toString();
    this.timestamp = LocalDateTime.now();
    this.description = description;
    for (OrderItem orderItem : orderItems) {
      orderItem.setOrderId(id);
      items.add(orderItem);
    }
  }
}

and this is the "OrderItem" entity

@Table(name="order_items")
@Data
@ToString(of = {"orderItemId", "orderId", "description", "quantity"})
public class OrderItem {
  @PrimaryKey(keyOrder = 1)
  @Column(name="order_id")
  private String orderId;

  @PrimaryKey(keyOrder = 2)
  @Column(name="order_item_id")
  private String orderItemId;

  private String description;
  private Long quantity;

  public OrderItem(String description, Long quantity) {
    this.orderItemId = UUID.randomUUID().toString();
    this.description = description;
    this.quantity = quantity;
  }
}

And this is a simple controller to check if join functions well.

  @GetMapping("/api/orders")
  public String test() {
    List<Order> list =orderRepository.findAll();
    for (Order o: list) {
      List<OrderItem> itemList =o.getItems();
      System.out.println("-----");
      for(OrderItem oi : itemList) {
        System.out.println(oi);
      }
      System.out.println("-----");
    }
    return "";
  }

When I set "items" in "Order" entity as @Interleaved(lazy = false), the orderRepository.findAll() function get the whole data including "OrderItem" well.

  @Interleaved(lazy = false)
  private List<OrderItem> items = new ArrayList<>();

스크린샷 2021-02-25 오전 11 57 28

It is normal because I set the lazy false, which means "eager" way fetch

But the main problem is, When I set "items" in "Order" entity as @Interleaved(lazy = true), the same print from the same code is like below 스크린샷 2021-02-25 오후 12 02 37

which indicates that the lazy loading has not executed.

As far as I learned about lazy loading, the data of "OrderItems" are not supposed to load when orderRepository.findAll(); is called, but supposed to load at least when

 for(OrderItem oi : itemList) {
        System.out.println(oi);
}

is called.

Am I setting something wrong here? Should I use the interleaved in different way?

Thanks.

meltsufin commented 3 years ago

Duplicate of https://github.com/GoogleCloudPlatform/spring-cloud-gcp/issues/335.