Search code examples
javajpaeclipselinkjpqlfetching-strategy

Join fetching @ManyToOne nested inside @OneToMany


I have created the following entities to manage a persistent shopping cart:

ShoppingCart.java:

@Entity
public class ShoppingCart {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @PrivateOwned
    @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
    @OrderBy("creationTimestamp")
    private List<ShoppingCartItem> items;

    public ShoppingCart() {}

    // Getters and setters...
}

ShoppingCartItem.java:

@Entity
@IdClass(ShoppingCartItemId.class)
public class ShoppingCartItem {
    @Id
    @ManyToOne
    private Item item;

    @Id
    @ManyToOne
    private ShoppingCart cart;

    private int quantity;

    @Column(precision = 17, scale = 2)
    private BigDecimal price;

    @Temporal(TemporalType.TIMESTAMP)
    private Date creationTimestamp;

    protected ShoppingCartItem() {}

    @PrePersist
    protected void prePersist() {
        creationTimestamp = new Date();
    }

    public ShoppingCartItem(ShoppingCart cart, Item item, int quantity) {
        this.cart = cart;
        this.item = item;
        this.quantity = quantity;
        this.price = item.getPrice();
    }

    // Getters and setters...
}

Item.java:

@Entity
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    private Brand brand;

    private String model;
    private String variant;
    private String description;

    @Column(precision = 17, scale = 2)
    private BigDecimal price;

    private int availability;

    protected Item() {}

    // Constructors, getters and setters...
}

When I issue the the following JPQL query:

SELECT c FROM ShoppingCart c JOIN FETCH c.items WHERE c.id = :id

I notice that all the ShoppingCartItems in the same ShoppingCart are retrieved as expected in a single query but the @ManyToOne private Item item; field is not in the join and a separate query for each ShoppingCartItem is issued to fetch that field when accessed.

Using EclipseLink, is there a way to have also the Items join fetched when join/batch fetching the ShoppingCartItems? How do I change the query and/or code?


Solution

  • While the left join fetchs with aliases seems to be ignored, I've found this query hint that do the job:

    Query query = entityManager.createQuery("SELECT c FROM ShoppingCart c WHERE c.id = :id");
    query.setHint("eclipselink.left-join-fetch", "c.items.item.brand");
    

    This is probably better than the annotation approach as it can be specified per single query.


    UPDATE

    Use of this hint broke @OrderBy("creationTimestamp") so ShoppingCartItems aren't returned in the order they were inserted anymore. This is probably due to a bug in EclipseLink but I think it doesn't really hurt so much since I actually need to have the items ordered only when showing the cart to the user and not, for example, when the user logs in and items in the anonymous cart must be transferred to the user cart.