I would like to use the EntityGraph Feature because of the known n+1 Problem. I have the following Entities structure:
#Entity
#Table(name = "customer")
public class Customer extends Person {
#Column(name = "foo")
public String foo;
#Column(name = "bar")
public String bar;
}
#Entity
#Table(name = "person")
public class Person {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "car.id")
public Car car;
#Embedded
public Key key;
}
#Entity
#Table(name = "car")
public class Car {
#Column(name = "a")
public String a;
#Column(name = "b")
public String b;
}
#Embeddable
public class Key
{
#Column(name = "key_id")
public Long keyId;
#Column(name = "key_color")
public String keyColor;
}
Now I want to use a NamedEntityGraph. As far as I understand with "#NamedEntityGraph(name = "getCustomer", includeAllAttributes=true)" it should work but it doesnt.
The NamedEntityGraph call with
em.createQuery(criteriaQuery).setHint("javax.persistence.fetchgraph", em.getEntityGraph("getCustomer")).getResultList()
returns the amount of Customers in the database but all Attributes including car and the Embedded Attribute key is always null.
Do I have to use subgraphs? I tried to declare the NamedEntityGraph on Customer class also on Person class. It makes no difference.
EDIT:
After struggling a long time with this problem, i tried to break down it to the lowest level with these two entities
#Entity
#Table(name = "publication")
#NamedEntityGraph(name = "graph.Publication.articles",
attributeNodes = #NamedAttributeNode("articles"))
public class Publication {
#Id
private String publicationId;
private String name;
private String category;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "publicationId")
private List<Article> articles;
#Entity
#Table(name = "article")
public class Article {
#Id
private String articleId;
private String title;
private String publicationId;
}
If i create a query i can see further more than one query in the postgres log.
EntityGraph<?> entityGraph = em.getEntityGraph("graph.Publication.articles");
List resultList = em.createQuery("SELECT x FROM Publication x").setHint("javax.persistence.fetchgraph", entityGraph).getResultList();
Different queries for all publications
SELECT ARTICLEID, publicationId, TITLE FROM article WHERE (publicationId = $1) parameters: $1 = 'publication_1'
SELECT ARTICLEID, publicationId, TITLE FROM article WHERE (publicationId = $1) parameters: $1 = 'publication_2'
But I would only have expected one query with a join here.
Finally I found a solution for my problem. I refer to the edited part of my question.
I found this page which describes very well how to use batch query hints to improve performance.
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html?m=1
For my example I don't need the entitygraph anymore. The query should created like this
List resultList = em.createQuery("SELECT x FROM Publication x").setHint("eclipselink.batch", "x.articles").getResultList();
Related
#Entity
#Table(name = "person")
public class Consignment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "person_id")
private String personId;
#Column(name = "person_name")
private String personName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person")
#Column(name = "cars_owned")
private Set<Cars> casrsowned = new HashSet<>();
}
#Entity
#Table(name = "cars")
public class Cars implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "cars_id")
private String carsId;
#ManyToOne
#JoinColumn(name = "person")
private Person person;
#OneToOne
private CarsDetail carsDetail;
}
#Entity
#Table(name = "carsDetail")
public class CarsDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "brand")
private String brand;
#Column(name = "color")
private String color;
#Column(name = "model")
private String model;
}
class CarModelDTO {
String personName;
List<String> models;
}
In the above relation, want to return CarModelDTO
JPA query where,
#Query("Select CarModelDTO(p.personName, p.casrsowned.carsDetail.model) from Person as p where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id);
I tried multiple ways but it gives
org.hibernate.QueryException: illegal attempt to dereference collection
As I have already described Retrieve List from repository interface to DTO list you should go through the following step :
first create a constructor using the fields you want to be returned from the query output
in you query you should create new instance of your dto and pass the field from db to new instalnce :
so you need these changes:
1. In the constructor:
You should not use a list as List<String> models; as you should consider that your dto as a result row of DB. so you need to have a simple String model;
public CarModelDTO (String name,String model){
this.name=name;
this.model=model;
}
2. In the #Query:
you should use multi inner join appropriately
you should also append your package name to CarModelDTO in the query (here i used com.example you should change it)
#Query("Select com.example.CarModelDTO(p.personName, d.model ) from Person as p inner join p.carsowned c inner join c.carDetail d where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id)
I have entities:
#Entity
public class C {
#Column
private String name;
}
#Entity
public class B {
#Column
private Integer id;
#ManyToOne
#JoinColumn(name = "id_c")
private C c;
}
#Entity
public class A {
#OneToMany(mappedBy = "a")
#OrderBy("id")
private Set<B> itemsB;
}
Now when i access to A.itemsB() - items ordered by B.id
I need to get A.itemsB() ordered by C.name. Is this possible?
I tried to write something like #OrderBy("c.name") but it not work.
just check wich Order is imported : org.hibernate.annotations.OrderBy or javax.persistence.OrderBy.. you should use the second.
You should not use a 'Set<B>', but a 'List<B>'.Sets are always unordered.
I am new to JPA 2.1 and started using only recently Named Entity Graphs. For my project I am mapping the following relation in JPA 2.1:
Order -> OrderDetail -> Product -> ProductLine
The question:
I want to instruct JPA to join and fetch properly all the needed data. So far this works flawlessly for Order -> OrderDetail -> Product but I have not managed so far to add a Sub-Sub Graph in order to go as deep as the ProductLine class. How do I make a subgraph of a subgraph ? Ex get the ProductLine of the Product ?
Here are my entities (getters and setters omitted):
Order
#Entity
#Table(name="ORDERS")
#NamedEntityGraph(
name = "graph.Order.details",
attributeNodes = {
#NamedAttributeNode(value = "details", subgraph = "graph.OrderDetail.product")
},
subgraphs = {
#NamedSubgraph(name = "graph.OrderDetail.product", attributeNodes = #NamedAttributeNode("product"))
}
)
public class Order implements Serializable{
#Id
#Column(name = "orderNumber")
private Long number;
#Column(name = "orderDate")
private Date date;
#OneToMany(mappedBy = "order")
private List<OrderDetail> details;
}
OrderDetail
#Entity
#Table(name = "orderdetails")
public class OrderDetail implements Serializable{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "orderNumber")
#Id
private Order order;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "productCode", nullable = false)
#Id
private Product product;
#Column(name = "orderLineNumber")
private int lineNumber;
#Column(name = "quantityOrdered")
private int quantity;
Product
#Entity
#Table(name = "products")
class Product {
#Column(name = "productCode")
#Id
private String code;
#Column(name = "quantityInStock")
public int quantity;
#ManyToOne
#JoinColumn(name = "productLine")
private ProductLine line;
ProductLine
#Entity
#Table(name = "productlines")
public class ProductLine {
#Id
#Column(name = "productLine")
private String line;
#Column
private String textDescription;
The simple answer is that you cannot do this because, with the current JPA implementation, you would end up doing two separate queries and having to deal with the Cartesian Products. Some future version of JPA could be extended to include more levels of subgraphs, but as it stands today it does not. There is a JPA SPEC group that works on the next version of JPA. Feel free to submit your request/suggestion there.
Here on StackOverflow there is another reference to the same question.
You can create multi level entity graphs with dynamic entity graphs.
I am using jpa 2.2 and Hibernate 5.3.7 and i am able to create entity
graphs and fetch data upto 3 levels . I hope this will work for
next level too . Below is the code snippet . For more details and actual code you can checkout my github repo : https://github.com/vaneetkataria/Jpa-Hibernate/blob/master/jdbcToJpaMigration/src/test/java/com/katariasoft/technologies/jpaHibernate/entity/fetch/entitygraph/dynamic/MultiInstructorsDynamicEntityGrpahTests.java
Code snippet :
#SuppressWarnings("unchecked")
#Test
#Rollback(false)
public void fetchInstrctrsIdProofVehiclesStudentsTheirInstructorsVehiclesAndTheirDocuments() {
doInTransaction(() -> {
EntityGraph<Instructor> instructorGraph = em.createEntityGraph(Instructor.class);
instructorGraph.addAttributeNodes(Instructor_.idProof, Instructor_.vehicles);
Subgraph<Student> studentSubgraph = instructorGraph.addSubgraph(Instructor_.STUDENTS);
studentSubgraph.addAttributeNodes(Student_.instructors);
Subgraph<Vehicle> vehicleSubgraph = studentSubgraph.addSubgraph(Student_.VEHICLES);
vehicleSubgraph.addAttributeNodes(Vehicle_.documents);
TypedQuery<Instructor> query = em.createQuery("select i from Instructor i ", Instructor.class)
.setHint(EntityGraphUtils.FETCH_GRAPH, instructorGraph);
List<Instructor> instructors = query.getResultList();
if (Objects.nonNull(instructors))
instructors.forEach(instructor -> {
IdProof idProof = instructor.getIdProof();
Set<Vehicle> vehicles = instructor.getVehicles();
Set<Student> students = instructor.getStudents();
System.out.println(instructor);
System.out.println(idProof);
if (Objects.nonNull(vehicles))
vehicles.forEach(v -> System.out.println(v.getVehicleNumber()));
if (Objects.nonNull(students))
students.forEach(s -> System.out.println(s.getName()));
});
});
}
Every NamedAttributeNode can specify a subgraph.
#Entity
#Table(name="ORDERS")
#NamedEntityGraph(
name = "graph.Order.details",
attributeNodes = {
#NamedAttributeNode(value = "details", subgraph = "graph.OrderDetail.product")
},
subgraphs = {
#NamedSubgraph(name = "graph.OrderDetail.product", attributeNodes = #NamedAttributeNode(value = "product", subgraph = "graph.Product.productLine")),
#NamedSubgraph(name = "graph.Product.productLine", attributeNodes = #NamedAttributeNode("line"))
}
)
I have 3 tables, each mapped to an entity. The entities are something like this:
#Entity
#Table(name = "person")
public class Person implements Serializable {
private int id;
//other fields
}
#Entity
#Table(name = "phone")
public class Phone implements Serializable {
private int id;
private Long price;
#ManyToOne
#JoinColumn(name = "personId")
private Person person;
#ManyToOne
#JoinColumn(name = "manufacturerId")
private Manufacturer manufacturer;
//other fields
}
#Entity
#Table(name = "manufacturer")
public class Manufacturer implements Serializable {
private int id;
private String name;
//other fields
}
What I want to do is to create a method that will return a list of Persons that have phones from a specified manufacturer with the price in a specified range.
EDIT: My dao class implements EntityJpaDao . I would need a solution that would work with this implementation.
Following query will return the Samsung mobile users with phone price range.
Criteria criteria = session.createCriteria(Phone.class, "phone");
criteria.createAlias("phone.person", "person")
criteria.add(Restrictions.between("phone.price", minPrice, maxPrice));
criteria.createAlias("phone.manufacturer","manufacturer");
criteria.add(Restrictions.eq("manufacturer.name", Samsung));
criteria.setProjection(Projections.property("person"));
List<Person> persons = criteria.list();
I have 2 tables. 1st one have oneToMany relationship with 2nd.
Class Author
#Entity
#Table(name = "Author")
Public class Author{
#Id
#Column(name = "AuthorId")
private int autherId;
#Column(name = "AuthorName")
private String authorName;
#OneToMany
#JoinColumn(name="AuthorId",referencedColumnName="AuthorId")
List<Book> Books;
//getter and setter
}
Class Book
#Entity
#Table(name = "Book")
Public class Book{
#Id
#Column(name = "BookId")
private int bookId;
#Column(name = "BookName")
private String bookName;
#Column(name = "AuthorId")
private int authorId;
//getter and setter
}
How can I write a Hql query so that I will get all author's and there books , with a condition that book name should starts with hello
I know using a query like this,
from Author;
I can fetch all author's and there books,but how to give condition on book?
I think its something like this:
select a from Author as a join a.Book as ab where ab.AuthorId like '%"hello"%';
not sure about a.Book though, it could also be a.Books as your columnname is named like that.