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 ShoppingCartItem
s 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 Item
s join fetched when join/batch fetching the ShoppingCartItem
s? How do I change the query and/or code?
While the left join fetch
s 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 ShoppingCartItem
s 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.