Query for tables with #ManyToMany relation(Hibernate) - java

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();

Related

How to replace CriteriaBuilder with Spring JPA

I have next classes:
#Entity
#Table
public class Lesson implements ModelEntity {
#Id
#Column(name = "lesson_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "course_id")
private Course course;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lesson_type_id")
private LessonType lessonType;
private LocalDate date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "time_slot_id")
private TimeSlot timeSlot;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "auditorium_id")
private Auditorium auditorium;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "lesson_teacher", joinColumns = #JoinColumn(name = "lesson_id"), inverseJoinColumns = #JoinColumn(name = "person_id"))
private Set<Teacher> teachers = new HashSet<>();;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "lesson_group", joinColumns = #JoinColumn(name = "lesson_id"), inverseJoinColumns = #JoinColumn(name = "group_id"))
private Set<Group> groups = new HashSet<>();
}
#Entity
#Table(name = "groups")
public class Group implements ModelEntity {
#Id
#Column(name = "group_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "group_name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "faculty_id")
private Faculty faculty;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "group")
private List<Student> students;
}
#Entity
#Table
public class TimeSlot implements ModelEntity {
#Id
#Column(name = "time_slot_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "time_slot_number")
private Integer number;
#Column(name = "time_slot_name")
private String name;
#Column(name = "time_slot_start")
private LocalTime startTime;
#Column(name = "time_slot_end")
private LocalTime endTime;
}
I wrote method, that find all Groups_id by Date and TimeSlot_id not connected to Lesson with CriteriaBuilder API, it works perfect:
#Override
public Set<Integer> getBusyGroupsId(int lessonId, LocalDate date, int timeSlotId) {
logger.debug("getBusyGroupsId() with agruments {}, {}, {}.", lessonId, date, timeSlotId);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Integer> query = criteriaBuilder.createQuery(Integer.class);
Root<Lesson> root = query.from(Lesson.class);
List<Predicate> predicates = new ArrayList<>();
Join<Lesson, TimeSlot> timeSlotJoin = root.join("timeSlot", JoinType.LEFT);
predicates.add(criteriaBuilder.equal(timeSlotJoin.get("id"), timeSlotId));
predicates.add(criteriaBuilder.equal(root.get("date"), date));
if (nonNull(lessonId)) {
predicates.add(criteriaBuilder.notEqual(root.get("id"), lessonId));
}
query.where(predicates.toArray(new Predicate[] {}));
SetJoin<Lesson, Group> joinGroup = root.joinSet("groups");
query.multiselect(joinGroup.get("id"));
TypedQuery<Integer> result = entityManager.createQuery(query);
return result.getResultStream().collect(Collectors.toSet());
}
But after that I think- what about JPA, can it be easier?
I tried something like that, but it doesnt work:
public Set<Integer> findGroupIdByIdNotAndDateEqualsAndTimeSlotIdEquals(Integer lessonId, LocalDate date, Integer timeSlotId);
How to fix it?
Also I stacked with writing method with JPA that should find all Lesson by Group_id and Date(or startDate-endDate) and sort it: first by date, second- by TimeSlot_number.
Can it be written with JPA?
Thanks in advance.
Don't throw stones, I'm just getting to know Spring JPA.

Hibernate Criteria Api with Inner Join and Many to Many

I'm new with Hibernate and Criteria Query.
How can I implement it with Hibernate Criteria Object?
SELECT stateslocalization.StateId, stateslocalization.localization AS name
FROM processstate
Join states ON states.id = processstate.StateId
JOIN stateslocalization ON stateslocalization.StateId = states.id
WHERE processstate.ProcessId = 38 and processstate.StateId = states.id AND stateslocalization.StateId = states.id
Entities:
Process:
#Entity
#Table(name = "processes")
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinTable(
name = "processstate",
joinColumns = {#JoinColumn(name = "ProcessId")},
inverseJoinColumns = {#JoinColumn(name = "StateId")}
)
private Set<State> states;
//getters and setters.....
}
State:
#Entity
#Table(name = "states")
public class State {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
private String name;
#ManyToMany(mappedBy = "states", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Process> processes;
#OneToOne(mappedBy = "state")
private StateLocalization stateLocalization;
//getters and setters.....
}
StateLocalization:
#Entity
#Table(name = "stateslocalization")
public class StateLocalization {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "StateId", referencedColumnName = "id")
private State state;
private String localization;
//getters and setters.....
}
I did it with native query but I don't know how I can implement it to Hibernate Criteria, because I don't have entity processstate (it's only table).

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"

Hibernate two parents one child mapping

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.

Hibernate Criteria Query for many to many Enum

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;

Categories

Resources