Reputation: 1199
I'm trying to use the JPA Criteria API to do a very easy query.
I have two entities:
@Entity
@Table(name = "PERSONS")
public class Person implements Serializable
{
@Id
@Column(name = "COD_PERSON")
private String personCode;
@Column(name = "NAME")
private String name;
@OneToMany(mappedBy="person")
List<Address > addresses;
... other attributes, getters and setters
}
@Entity
@Table(name = "ADDRESS")
public class Address implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@Column(name = "COD_ADDRESS")
private String addressCode;
@ManyToOne(fetch=FetchType.LAZY, optional=false)
@JoinColumn(name = "COD_PERSON")
private Person person;
@Column(name = "street")
private String street;
@Column(name = "zipCode")
private String zipCode;
... other attributes, getters and setters
}
We have defined a ManyToOne association in 'Address' entity, and a OneToMany association in 'Person' entity. Both are defined as lazy because of perfomance issues.
Then I would like for example to query people who lives in a certain zip code. And I would like to get people populated with their addresses.
Because we have defined associations as lazy, and because 'addresses' is a OneToMany association, I think I should use "eclipselink.batch" hint.
I have tried with:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> from = query.from(Person.class);
Join<Person, Address> join = from.join("addresses");
query.select(from);
Predicate predicate = cb.equal(join .get("zipCode"), "28020");
query.where(predicate);
List results = em.createQuery(query).setHint(org.eclipse.persistence.config.QueryHints.BATCH, "p.addresses").getResultList();
I get all the people who live in a certain zip code, that's ok, but 'addreses' attribute is not populated.
How could I get 'person' entities populated with 'addreses' attribute?
Thanks
THE SOLUTION I HAVE FOUND:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> from = query.from(Person.class);
Join<Person, Address> join = (Join<Person, Address>) from.fetch("addresses", JoinType.LEFT);
query.select(from);
Predicate predicate = cb.equal(join.get("zipCode"), "28020");
query.where(predicate);
List results = em.createQuery(query).getResultList();
What I don't like very much is the casting I have to do. I've tried it with EclipseLink provider. I don't know if it would work with other providers.
Upvotes: 0
Views: 1469
Reputation: 51
You can use the "fetch" method on "Root" class.
For example,
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery q = cb.createQuery(Order.class);
Root o = q.from(Order.class);
o.fetch("items", JoinType.INNER);
q.select(o);
q.where(cb.equal(o.get("id"), orderId));
Order order = (Order)this.em.createQuery(q).getSingleResult();
Also, you can check other options from here
https://www.thoughts-on-java.org/5-ways-to-initialize-lazy-relations-and-when-to-use-them/
Edit 1:
You can do fetch join that results in only one join like below
...
Join<Order, OrderItem> join = (Join<Order,OrderItem>)root.<Order,OrderItem>fetch("items");
...
Edit 2:
If you don't want to use casting, you can use entity graphs. But this solution is available since JPA 2.1
...
EntityGraph<Order> fetchGraph = entityManager.createEntityGraph(Order.class);
fetchGraph.addSubgraph("items");
TypedQuery<Order> orderQuery = entityManager.createQuery(query);
orderQuery.setHint("javax.persistence.loadgraph", fetchGraph);
...
Upvotes: 2