JPQL query with many to many relationship - java

Hey so I have a many to many relationship between two tables, Knights and Quests.
I want to write JPQL query where I get quests with id of 1 from knights of id 1 as well, later on I will change it to quest status, but for now I want to make it work:
#Query("select k.quests from Knight k join k.quests q where k.id=1 and q.id=1")
Collection<Quest> findDoneStories();
It kinda works, because it gives me quests from knight with id 1, but it gives me all quests rather than only one quest with id 1.
#Entity
#Table(name = "knights")
public class Knight {
#Id
#Column(name = "id")
int id;
#Column
String name;
#Column(name = "status")
#Enumerated(EnumType.STRING)
private KnightEnum status;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "knights_quests", joinColumns = #JoinColumn(name = "id_knights"),
inverseJoinColumns = #JoinColumn(name = "id_quest"))
List < Quest > stories;
}
#Entity
#Table(name = "quests")
public class Quest {
#Id
#Column(name = "id")
int id;
#Column
String name;
#Column(name = "description")
String description;
#Column(name = "status")
#Enumerated(EnumType.STRING)
QuestEnum status;
#Column(name = "story_points")
int storyPoints;
#ManyToMany(mappedBy = "stories", fetch = FetchType.EAGER)
List < Sprint > knights;
}

If you want to get only one Quest, your query and method should be like this:
#Query("SELECT DISTINCT q FROM Knight k JOIN k.stories q WHERE k.id = 1 AND q.id = 1")
Quest findDoneStories();

Since it's many to many relation you have to add one more table knights_quests with columns: knighs_id and quests_id where you can store 1 and 1 for example.
Your Entity will be like this:
#ManyToMany() #JoinTable(name = "knights_quests", joinColumns = #JoinColumn(name = "knighs_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "quests_id",referencedColumnName = "name") )
And after you can execute query with simple join, JPQL will handle it under the hood

Related

Query for tables with #ManyToMany relation(Hibernate)

I rewrite my SpringMVC app with using Hibernate.I try to make query for selecting lectures for group of students by id of group.with using SQL query(before I started rewrite it with using Hibernate)this query was:
"SELECT * FROM lectures WHERE id IN (SELECT lecture_id FROM lectures_groups WHERE group_id =?) ORDER BY date_of_lecture"
I have Lecture and Group etities:
#Entity
#Table(name = "lectures")
public class Lecture {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "lectures_groups", joinColumns = #JoinColumn(name = "lecture_id"), inverseJoinColumns = #JoinColumn(name = "group_id"))
private List<Group> groups = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "teacher_id")
private Teacher teacher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "subject_id")
private Subject subject;
#Column(name = "date_of_lecture")
private LocalDateTime date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "audience")
private Audience audience;
public Lecture() {
}
//getters setters
}
and:
#Entity
#Table(name = "groups")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "group_name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cathedra_id", referencedColumnName = "id")
private Cathedra cathedra;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "lectures_groups", joinColumns = #JoinColumn(name = "group_id"), inverseJoinColumns = #JoinColumn(name = "lecture_id"))
private List<Lecture> lectures = new ArrayList<>();
public Group() {
}
//getters setters
}
I tried somthing like:
List<Lecture> lectures = session.createQuery("select l from lectures l join l.groups g where g.id=:groupId")
.setParameter("groupId", group.getId())
.list();
but I get Exception: org.hibernate.hql.internal.ast.QuerySyntaxException: lectures is not mapped
So how can i do it?
In hql query you need to provide the name of the entity in the query instead of the table name. So in your case, you should replace lectures with Lecture in the query.
List<Lecture> lectures = session.createQuery("select l from Lecture l join l.groups g where g.id=:groupId")
.setParameter("groupId", group.getId())
.list();

Get records from two table in relation many to many

I have two table in relation many to many
public class Repertoire {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, unique = true)
private Integer id;
private String name;
private Integer dayWeek;
#ManyToMany(cascade = CascadeType.REMOVE)
#JoinTable(
name = "repertoire_seance",
joinColumns = { #JoinColumn(name = "repertoire_id")},
inverseJoinColumns = {#JoinColumn(name = "seance_id")}
)
List<Seance> seances = new ArrayList<>();
}
and
public class Seance {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, unique = true)
private Integer id;
private java.time.LocalTime displayTime;
#ManyToOne
private Film film;
#Column(length=127)
private String kind;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Hall hall;
#OneToMany(mappedBy = "reservationSeance")
#JsonIgnore
private List<Reservation> reservations = new ArrayList<>();
}
Hibernate create linked tabel repertoire_seance is posible get seances by seancesId and repertoire.dayWeek using spring data (JpaRepository) something like that findBySeanceIdAndRepertoireDayWeek()?
You could achieve this by writing an HQL.
It would look something like this:
select s from Repertoire r inner join r.seances s where r.dayWeek ="Your Value" and s.id = "Your Id Value"

How to create a namedquery of manytomany entity?

Brand
public class Brand implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "BrandID", nullable = false)
private Integer brandID;
#Basic(optional = false)
#Column(name = "BrandName", nullable = false, length = 100)
private String brandName;
#Basic(optional = false)
#Column(name = "Description", nullable = false, length = 1000)
private String description;
#Column(name = "Is_Visible")
private Boolean isVisible;
#JoinTable(name = "brandcategory", joinColumns = {
#JoinColumn(name = "BrandID", referencedColumnName = "BrandID")}, inverseJoinColumns = {
#JoinColumn(name = "CategoryID", referencedColumnName = "CategoryID")})
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Category> categoryCollection;
#OneToMany(mappedBy = "brand", fetch = FetchType.EAGER)
private Collection<Product> productCollection;
I want to retrive the Brand IDs from table brandcategory whoes categoryID = :categoryID
how can i createnamed query for it in entity brand?
this does not work:
#NamedQuery(name = "Brand.getBrandListByCategory",
query = "SELECT b FROM Brand b WHERE b.brandID =
(SELECT bc.brandID
FROM b.brandctegory bc
WHERE bc.category.categoryID = :categoryID)")
If I understand correctly, you want all the brands belonging to a category. Why don't you simply make the association bidirectional. You could then just do:
Category category = em.find(Category.class, categoryId);
return category.getBrands();
If it's unidirectional, then you'll need a query, but it's much simpler that the one you tried:
select b from Brand b inner join b.categoryCollection category
where category.id = :categoryId;
Your query doesn't make sense: it uses a non-existing association (b.brandcategory). Remember that JPQL uses entities, their persistent fields and associations to other entities. And nothing else. Tables don't exist in JPQL.
AFAIK, you cant go out of a entity boundary, when creating queries in entity class.
Instead use .createNativeQuery() method of the entity manager, to create complex and mixed queries.

JPA : handling many to many relationships

I am using jpa-api 2.0-cr-1.(Maven)
I have 2 classes Person and Tag.
Each person can have multiple tags and vice-versa.
Now I want to retrieve all persons belonging to all given tags.
For e.g.
person1 is tagged with "tall","fat" and "bold"
person2 is tagged with "tall" and "thin"
person3 is tagged with "tall" and "bold"
Now the problem is that if
I query for ["tall","bold"]
I should get [person1,person3]
i.e. I want to retrieve persons who belong to all of the given tags.
public class Person {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "person_name")
private String personName;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "person_tag", joinColumns = { #JoinColumn(name = "person_id") }, inverseJoinColumns = #JoinColumn(name = "tag_id"))
private Set<Tag> tags;
}
public class Tag {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "person_tag", joinColumns = { #JoinColumn(name = "tag_id") }, inverseJoinColumns = #JoinColumn(name = "person_id"))
private Set<Person> persons;
if you are looking for a query, you could try the following one.
here I used the "tag.name", not id. but you know how to change it if it works for you.
select p from Person p
left join p.tags as t where t.name in ("tall","bold")
group by p having count(p)=2
btw, I didn't test it. hope it helps.
Select p from Person p join p.tags t1 join p.tags t2 where t1.name = 'tall' and t2.name = 'bold'
See,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#JOIN

Hibernate querying

I'm sorry for my maybe foolish question. I have products and orders tables (with many-to -many relationship), also i have an user table. And now I want to get the product count by user_id and by special field "order_satus". I can make two queries get order by special criteria and then get size of product in order. But this is not optimal at all. When i use JDBCTemplate I did a lot of joins and get only one query.
Here are my entities:
#Entity
#Table(name = "shop.order")
public class Order {
#Id
#Column(name = "order_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long orderId;
private long user_id;
#Column(name = "databegin")
private Date dateBegin;
#Column(name = "dataend")
private Date dateEnd;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "order_product", joinColumns = { #JoinColumn(name = "order_id") }, inverseJoinColumns = { #JoinColumn(name = "product_id") })
private List<Product> products;
}
Product Entity
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int product_id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "order_product", joinColumns = { #JoinColumn(name = "product_id") }, inverseJoinColumns = { #JoinColumn(name = "order_id") })
private List<Order> order;
public List<Order> getOrder() {
return order;
}
public void setOrder(List<Order> order) {
this.order = order;
}
#Column
#NotBlank
private String name;
#Column
#Max(value = 250)
private String descr;
#Column
#Max(value = 250)
private String manufacturer;
#Column
private Double price;
#Column
private Byte[] barcode;
#Column
private Byte[] picture;
#ForeignKey(name = "category_id")
private int category_id;
As you gave sql query..
'select count(*) from produtc p join order ord on ord.id = ? and
ord.status = ?' – Igor Masternoy
And according to the Entity structure you gave, the HQL will be..
select ord.products productList from Order ord where ord.id=? and ord.status=?
This query will return you list of products (List<Product> products) and then you can get the count by java code i.e. productList.size(); This size is the product count you need based on order id and order status you will pass as parameter and also you can append user.id in where cause to filter your result as per user.
This is productList as per your need..
Query query = getSession().createQuery("select ord.products productList from Order ord where ord.id=:orderID and ord.status=:orderStatus");
query.setInteger("orderID", orderIDParameter);
query.setString("orderStatus", orderStatusParameter);
List<Product> productList = (List<Product>) query.list();
Integer productCount = productList.size();
This productCount is your product count you need.
If I get it right, you can have many orders for one user, and many products for one order.
I think a good option is to use a DetachedCriteria and to build your query with it.
Should look like (not tested):
DetachedCriteria userCriteria = DetachedCriteria.forClass(User.class);
userCriteria.add(Projections.groupProperty("user_id"));
userCriteria.add(Projections.count("product_id");
DetachedCriteria orderCriteria = userCriteria.createCriteria("order.user_id","order",CriteriaSpecification.LEFT_JOIN);
DetachedCriteria orderCriteria = orderCriteria.createCriteria("order_product.order_id","product",CriteriaSpecification.LEFT_JOIN);
//orderCriteria.add(Restrictions.eq(...); // I can't see a "status" field in your model
List results = userCriteria.list();
select size(ord.products) from Order ord where ord.id = :orderId and ord.status = :orderStatus
or
select count(*) from Order ord join ord.products where ord.id = :orderId and ord.status = :orderStatus
The first form will result in a subquery to get the count. The second relies on the join to create the sql product over which to apply the count. The first form is more intuitive, in my opinion, while the second query will perform better in most cases.

Categories

Resources