Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using JPA entity graph with complex conditions

I have a project built on Spring MVC + JPA + Hibernate. I am using entity graphs (JPA 2.1) for defining which data to fetch from the database, like in the example below.

EntityGraph<Company> entityGraph = entityManager.createEntityGraph(Company.class);
entityGraph.addAttributeNodes("reviews");

Map<String, Object> hints = new HashMap<String, Object>();
hints.put("javax.persistence.loadgraph", entityGraph);

Company company = entityManager.find(Company.class, companyId, hints);

My Review entity has an association with a Company entity (ManyToOne).

Here I simply fetch a Company object with a populated reviews collection. This works well in scenarios like the above. But what if I want to fetch all or some of the reviews of a given company? That is, the Review objects that are associated with a company with a given ID. I want a List<Review> instead of a Company object with a List<Review>. This is just an example - basically I am looking for more flexibility than simply looking up an object based on a primary key. I can do it with HQL with no problem, but then I would have to write several similar queries depending on which data I need in a specific context.

The find method on javax.persistence.EntityManager simply makes it possible to query for an object based on a primary key. But is it somehow possible to use entity graphs in more complicated scenarios, e.g. with Criteria objects or HQL queries? For example, looking up objects with other conditions than by primary key - perhaps even conditions on associations.

I hope I made myself clear. Thanks in advance!

like image 235
ba0708 Avatar asked Dec 08 '14 15:12

ba0708


2 Answers

What you are looking for is probably not EntityGraphs, but JPA Query (either JPQL in form of a NamedQuery or a CriteriaQuery). This is all part of the JPA specification.

So basically you can:

  1. Annotate each entity clas with @NamedQueries to specify JPQL queries. Their advantage is that their syntax is checked on deployment (deployment will fail if e.g. the NamedQueries access missing properties) and are reusable and disadvantage is: they are statically defined (but of course accept parameters).
  2. Construct on runtime JPQL queries with EntityManager. I use NamedQueries more often than runtime queries because of the above mentioned advantages.
  3. Use the Criteria API, which has the advantage, they are type-safe, as you join/search/add conditions/play their with real Java Objects.

Now about EntityGraphs: they are just a help so fetch additional fields from a query (no matter if you use EntityManager.find() with the additional properties map parameter or Query.setHints()). You could also use Subgraphs for more complex situations. Check this and this example.

like image 143
V G Avatar answered Oct 14 '22 01:10

V G


As you note:

I can do it with HQL with no problem, but then I would have to write several similar queries depending on which data I need in a specific context.

I assume then that the ultimate solution would look something like the below where one query method can be defined but to which we can pass both dynamic criteria (be these either direct or nested properties of an Entity) and a dynamic fetch plan defining those EntityGraphs to be activated:

public interface CompanyRepository{

    List<Review> findAll(Criteria criteria, FetchPlan fetchPlan);
}

The Spring Data project can actually get us part of the way there but not, at the current time, all of the way (although there is some discussion around regarding the missing part: see further below).

What it does do at the moment is the first part:

public interface CompanyRepository{

    List<Review> findAll(Criteria criteria);

}

either by means of the Specification pattern (http://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/#specifications) or, more simply, by using QueryDSL's fluent API as an alternative to the JPA Criteria API.

Using the QueryDSL approach then we can create a Repository definition like this:

public interface ReviewRepository extends CrudRepository<Review, Long>, QueryDslPredicateExecutor<Review>{

}

Now, and without creating an implementation (created by the framework) or writing any more code we can call it as below, with any combination of attributes:

Review review = respository.findOne();
Iterable<Review> reviews = respository.findAll(QReview.review.created.eq(someDate));
Iterable<Review> reviews = respository.findAll(QReview.review.created.eq(someDate).and(QReview.review.creator.forename.eq("Jim"));
Iterable<Review> reviews = respository.findAll(QReview.review.created.eq(someDate).and(QReview.review.creator.forename.eq("Jim").and(QReview.review.company.name.eq("Amazon"));

etc....

where QReview is a Query Type auto generated by the QueryDSL library and giving you strongly typed queries and the findOne(Predicate predicate) and findAll(Precicate predicate) methods are inherited from:

http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/querydsl/QueryDslPredicateExecutor.html

This then gives you a lot for very little (zero) code other than creating some interface definitions but would still require an additional (sub-optimal in terms of DB interactions) mechanism for handling the lazy loading i.e. one of the mechanisms highlighted in your other recent question and which led on to this one.

I do not see however why Spring Data could not be updated to handle the dynamic specification of EntityGraphs and there has indeed been some discussion around this so it may be in the pipeline:

http://forum.spring.io/forum/spring-projects/data/108202-custom-fetch-groups-with-spring-data-jpa-possible

so might be worth raising a JIRA to see if this something they are planning or asking

https://stackoverflow.com/users/18122/oliver-gierke

directly.

Some support has been added to Spring Data for the new JPA 2.1 EntityGraph functionalityb however I do not see that it can be made to work with a generic query method like we want here:

http://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/#jpa.entity-graph

like image 40
Alan Hay Avatar answered Oct 13 '22 23:10

Alan Hay