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
Related
I'm having an issue creating a JPQLQuery for all parents whose children have a certain attribute (Boolean active) set to TRUE. All children have to have it set to TRUE, otherwise don't select the parent.
I've found similar issues and answers but none of them written in JPQLQuery and QueryDSL that I need.
Here are my entities (simplified):
Parent entity:
#Table(name = "parent")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "parent_child",
joinColumns = #JoinColumn(name = "parent_id"),
inverseJoinColumns = #JoinColumn(name = "child_id")
)
private List<Child> children;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(columnDefinition ="country_id")
private Country country;
Child entity:
#Table(name = "child")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "active")
private Boolean active;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "parent_child",
joinColumns = #JoinColumn(name = "child_id"),
inverseJoinColumns = #JoinColumn(name = "parent_id")
)
private List<Parent> parents;
And here's what I've tried but it doesn't work and I'm not very good at writing these so I'm not even sure why it doesn't work:
JPAQuery<Parent> jpaQuery = new JPAQuery<>(entityManager);
QParent qParent = QParent.parent;
QChild qChild = QChild.child;
Map<Country, List<Parent>> parents = jpaQuery.from(qChild)
.join(qChild.parent, qParent)
.where((QChild.child.active.isFalse()).count().eq(0L))
.transform(GroupBy.groupBy(qParent.country).as(GroupBy.list(qParent)));
the error I get for this is:
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found '=' near line 4, column 52
Appreciate any help, thanks!
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();
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"
I have following scenario: There are companies and employees. Each company has a set of employees. Each employee can work for several companies. So I implemented following relationships:
Company.class:
#JoinTable(name = "company_employee", joinColumns = #JoinColumn(name = "company_id") , inverseJoinColumns = #JoinColumn(name = "employee_id") )
#ManyToMany(fetch = FetchType.LAZY)
private List<Employee> employees;
Employee.class:
#JoinTable(name = "company_employee", joinColumns = #JoinColumn(name = "employee_id") , inverseJoinColumns = #JoinColumn(name = "company_id") )
#ManyToMany(fetch = FetchType.LAZY)
private List<Company> companies;
Obviously, to work for several companies, each employee should have several not overlapping schedules assigned for each company he or she works.
Also, there should be a list of schedules for each combination Company-Employee, as sometimes old schedule expires, and new schedule becomes effective.
So I also have Schedule.class, which is supposed to have child to parent #ManyToOne relationships both to Company and Employee, and should work following way: each Schedule, and thus, List<Schedule> should correspond to exactly one combination of Company and Employee instances.
How to implement this relationship?
Update 1
I only have in mind adding #OneToMany Schedule relationship to each Company and Employee, but then I need to put instances of Schedule both to Company and Employee each time, and this way just don't look right, also it's not obvious for me now how to fetch it back.
So any help will be appreciated.
This post was updated to show real-life scenario I have, not just generic Entity1, Entity2, Entity3 names for classes.
Update 2
I accepted the answer, but I cannot use it if Schedule contain Lists.
According to my plan, Schedule should contain List<Vacation> to know the set of Vacations over a year, and List of Days, each of which shows start of particular week day, break, and end of this day. Those Days are also unique for each Schedule instance.
It was supposed to be something like below, but obviously now I don't have schedule_id, so how to connect those lists to Schedule?
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "schedule_id")
private List<Vacation> vacations;
#JoinTable(name = "schedule_week", joinColumns = #JoinColumn(name = "schedule_id") , inverseJoinColumns = #JoinColumn(name = "day_id") )
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Day> week;
How to include those lists right?
I would like to suggest the following solution.
An embeddable class that contains the Company and Employee for a particular schedule.
#Embeddable
public class ScheduleOwner implements Serializable{
#MapsId("id")
#ManyToOne(cascade = CascadeType.ALL)
Company c;
#MapsId("id")
#ManyToOne(cascade = CascadeType.ALL)
Employee e;
}
The Schedule class is embedding a ScheduleOwner instance.
#Entity
public class Schedule {
#EmbeddedId
ScheduleOwner owner;
String description;
}
The Company and Employee classes(no change done to them)
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#JoinTable(name = "company_employee", joinColumns = #JoinColumn(name = "company_id") , inverseJoinColumns = #JoinColumn(name = "employee_id") )
#ManyToMany(fetch = FetchType.LAZY)
private List<Employee> employees;
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#JoinTable(name = "company_employee", joinColumns = #JoinColumn(name = "employee_id") , inverseJoinColumns = #JoinColumn(name = "company_id") )
#ManyToMany(fetch = FetchType.LAZY)
private List<Company> companies;
}
UPDATE 1
Below is how you could save and fetch results.
Employee e1 = new Employee();
Company c1 = new Company();
c1.employees.add(e1);
e1.companies.add(c1);
ScheduleOwner so = new ScheduleOwner();
so.c = c1;
so.e = e1;
Schedule s = new Schedule();
s.owner = so;
session.save(c1);
session.save(e1);
session.save(s);
// below query will fetch from schedule, where company id = 9
Schedule ss = (Schedule) session.createQuery("From Schedule sh where sh.owner.c.id = 9").uniqueResult();
UPDATE 2
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#JoinTable(name = "company_employee", joinColumns = #JoinColumn(name = "company_id", referencedColumnName="id")
, inverseJoinColumns = #JoinColumn(name = "employee_id", referencedColumnName="id"))
#ManyToMany(fetch = FetchType.LAZY)
List<Employee> employees = new ArrayList<>();
String name;
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "employees")
List<Company> companies = new ArrayList<>();
String name;
}
#Entity
public class Schedule {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int schedule_id;
#ManyToOne
#JoinColumn(name = "company_id", insertable = false, updatable = false)
private Company company;
#ManyToOne
#JoinColumn(name = "employee_id", insertable = false, updatable = false)
private Employee employee;
String description;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "schedule")
List<Vacation> vacations;
}
#Entity
public class Vacation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int vacation_id;
#ManyToOne
#JoinColumn(name = "schedule_id" )
Schedule schedule;
#OneToMany(mappedBy = "vacation")
List<Day> days;
}
Day entity directly relates to Vacation. Not to Schedule.
#Entity
public class Day {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "vacation_id")
Vacation vacation;
}
Hope this helps.
I have a class Comment:
#Entity
#Table(name = Constants.COMMENTS_TABLE)
#Audited
public class Comment {
#Column(name = "comment", nullable = false)
private String comment;
#ElementCollection(targetClass = CommentTopic.class)
#Enumerated(EnumType.STRING)
#Fetch(value = FetchMode.JOIN)
#CollectionTable(name = Constants.COMMENTS_TOPIC_JOIN_TABLE, joinColumns = #JoinColumn(name = "comment_id"))
#Column(name = "topic")
private Set<CommentTopic> commentTopics;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "comment_id", nullable = false)
private Long commentId;
}
Persisting the comment class works but the following criteria query:
Criteria criteria = session.createCriteria(Comment.class)
.add(Restrictions.eq("commentTopics", topic));
List<Comment> entries = criteria.list();
throws org.hibernate.exception.DataException: No value specified for parameter 1.
This is the query built:
select this_.comment_id as comment1_0_0_, this_.comment as comment0_0_, commenttop2_.comment_id as comment1_0_2_, commenttop2_.topic as topic2_ from comments this_ left outer join comments_topic commenttop2_ on this_.comment_id=commenttop2_.comment_id where this_.comment_id=?
Am I using incorrect annotations?
Is the criteria query not being constructed properly?
I placed the CommentTopic enum in a CommentTopicWrapper class.
I updated the annotation for the commentTopicsSet to:
#OneToMany(targetEntity = CommentTopicWrapper.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = Constants.COMMENTS_TOPIC_JOIN_TABLE, joinColumns = #JoinColumn(name = "comment_id"), inverseJoinColumns = #JoinColumn(name = "topic"))
private Set<CommentTopicWrapper> commentTopics;