multiple join in hibernate with criteria - java

i want to join 4 tables with hibernate. i write following code for fetch course name , teacher detail of course and schedules of course by student id:
my student entity is:
#Entity
#Table(name = "students", schema = "public")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "students_id_seq")
#SequenceGenerator(name = "students_id_seq", sequenceName = "students_id_seq", allocationSize = 1)
#Column(name = "id", nullable = false, unique = true)
private int id;
#Column(name = "name", nullable = false, length = 60)
private String name;
#Column(name = "code_melli", nullable = false, length = 10)
private String codeMelli;
#Column(name = "register_date", nullable = false)
private Date registerDate;
#Column(name = "mobile", length = 11)
private String mobile;
#Column(name = "phone", length = 15)
private String phone;
#Column(name = "email", length = 50)
private String email;
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id", referencedColumnName = "id", insertable = false, updatable = false)
private List<Attend> attends;
my Attend Entity that determines the attendance of the student in the course and his score is:
#Entity
#Table(name = "attend", schema = "public")
public class Attend {
#EmbeddedId
AttendKey attendKey;
#Column(name = "score")
private int score;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id", referencedColumnName = "id", insertable = false, updatable = false)
private Student student;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "course_id", referencedColumnName = "id", insertable = false, updatable = false)
private Course course;
course entity that map course detail is:
#Entity
#Table(name = "courses", schema = "public")
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "courses_id_seq")
#SequenceGenerator(name = "courses_id_seq", sequenceName = "courses_id_seq", allocationSize = 1)
#Column(name = "id", nullable = false, unique = true)
private int id;
#Column(name = "code")
private int code;
#Enumerated(EnumType.ORDINAL)
#Column(name = "cluster", nullable = false)
private ClusterType cluster;
#Column(name = "name", nullable = false, length = 40)
private String name;
#OneToOne
#JoinColumn(name = "teacher_id")
private Teacher teacher;
#Column(name = "students", nullable = false)
private int students;
#Column(name = "salary", nullable = false)
private int salary;
#Column(name = "tuition", nullable = false)
private int tuition;
#Column(name = "start_date")
private Date startDate;
#Column(name = "end_date")
private Date endDate;
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "course_id", referencedColumnName = "id", insertable = false, updatable = false)
private List<Schedule> scheduleList;
and schedule entity that saves the start and end time of the class in one day of the week is:
#Entity
#Table(name = "schedule", schema = "public")
#IdClass(ScheduleKey.class)
public class Schedule {
#Id
#Column(name = "course_id")
private int course_id;
#Id
#Column(name = "day")
#Enumerated(EnumType.ORDINAL)
private Day day;
#Column(name = "start", nullable = false)
private Time start;
#Column(name = "endt", nullable = false)
private Time endt;
how can i join these tables with hibernate criteria API and fetch course schedule of specific student.
SQL query for fetch data:
select
*
from
students
inner join
attend
on students.id=attend.student_id
inner join
courses
on attend.course_id=course.id
left outer join
schedule_
on course.id=schedule.course_id
left outer join
public.teachers
on course.teacher_id=teacher.id
where
students.id=102552
i use
public Student getStudentDetail(int studentId) {
Criteria criteria = createEntityCriteria();
criteria.add(Restrictions.eq("id", studentId));
criteria.setFetchMode("attends", FetchMode.JOIN);
Criteria attendCriteria = criteria.createCriteria("attends", "attend")
.setFetchMode("course", FetchMode.JOIN);
Criteria courseCriteria = attendCriteria.createCriteria("course", "course")
.setFetchMode("scheduleList", FetchMode.JOIN);
Criteria scheduleCriteria = courseCriteria.createCriteria("scheduleList", "schedule");
return (Student) scheduleCriteria.uniqueResult();
}

Related

JPA created too many fields in table

enter image description here
I am trying to map some entities to tables in MySQL database using Spring Boot JPA. I have a problem with one of the tables because in that one too many foreign keys are added. I highlighted the columns in the picture. I suppose that the problem might be linked with the fact that the Tutorial table has either One to Many or Many to Many relations with the other 3 tables, but I am not sure
#Entity(name = "authors")
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "author_id")
private Long authorId;
#Column(name = "first_name", nullable = false, length = 100)
private String firstName;
#Column(name = "last_name", nullable = false, length = 100)
private String lastName;
#Column(name = "email", length = 320, unique = true)
private String email;
#Column(name = "job_title", length = 255)
private String jobTitle;
#Lob
#Type(type = "org.hibernate.type.BinaryType")
#Column(name = "profile_picture")
private byte[] profilePicture;
#Column(name = "about", length = 2000)
private String about;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "author_id")
private List<Tutorial> tutorials;
}
#Entity(name = "categories")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
private Long categoryId;
#Column(name = "category_name", nullable = false, unique = true, length = 100)
private String categoryName;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private List<Tutorial> tutorials;
}
#Entity(name = "tutorials")
public class Tutorial {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tutorial_id")
private Long tutorialId;
#Column(name = "tutorial_title", nullable = false, length = 150)
private String tutorialTitle;
#Column(name = "tutorial_description", nullable = false, length = 2000)
private String tutorialDescription;
#Column(name = "time_to_complete")
private Integer timeToComplete;
#Column(name = "date_published")
private Long datePublished;
#Column(name = "last_updated")
private Long lastUpdated;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "tutorials")
private List<User> users = new ArrayList<>();
#ManyToOne(fetch = FetchType.EAGER)
private Category category;
#ManyToOne(fetch = FetchType.EAGER)
private Author author;
}
Tutorials is the table where the problems appear as 4 foreign keys are generate instead of two
#Entity(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "first_name", nullable = false, length = 100)
private String firstName;
#Column(name = "last_name", nullable = false, length = 100)
private String lastName;
#Column(name = "user_name", nullable = false, unique = true, length = 100)
private String userName;
#Column(name = "age")
private Integer age;
#Column(name = "email", length = 320, unique = true)
private String email;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "users_tutorials",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "tutorial_id") })
private List<Tutorial> tutorials = new ArrayList<>();
}
Try this changes:
remove #JoinColumn(name = "author_id")from Author and place in Tutorial:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "author_id")
private Author author;
remove #JoinColumn(name = "category_id")from Category and place it in Tutorial as well:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id")
private Author author;
To get more information look here: Baeldung - Hibernate One to Many

Intermediate table not updated when saving an entity

I have a set of entities user and conference. I have implemented a method in which I assign users to a conference and save it. However, when I save the conferencce the intermediate table attendance_table is not updated.
My database erd diagram example:
My entities:
#Entity
#Table(name = "user_table", schema = "public")
public class User {
#Id
#Column(name = "user_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long user_id;
#Column(name = "name")
private String name;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "confirmed")
private boolean confirmed;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.DETACH}, fetch = FetchType.LAZY)
#JoinTable(name = "attendance_table",
joinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "user_id",
nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "conference_id", referencedColumnName = "conference_id",
nullable = false, updatable = false)})
private Collection<Conference> conferences = new HashSet<>();
#Entity
#Table(name = "conference_table", schema = "public")
public class Conference {
#Id
#Column(name = "conference_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long conference_id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User creator ;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column(name = "startConference")
private Date startConference;
#Column(name = "endConference")
private Date endConference;
#ManyToMany(mappedBy = "conferences", fetch = FetchType.LAZY)
private Collection<User> students;
Any ideas? If you need any extra info I can update the question. Thanks in advance!
This was a interesting fix. I was messing around with the user and conference classes and found a solution.
I had to swap the implementations of #ManyToMany of both classes for it to work.
My User class now looks like:
#Entity
#Table(name = "user_table", schema = "public")
public class User {
#Id
#Column(name = "user_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long user_id;
#Column(name = "name")
private String name;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "confirmed")
private boolean confirmed;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.DETACH}, fetch = FetchType.EAGER)
#JoinTable(
name = "user_role_table",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "role_id"))
private Collection<Role> roles = new HashSet<>();
#ManyToMany(mappedBy = "students", fetch = FetchType.LAZY)
private Collection<Conference> conferences;
And my conference class:
#Entity
#Table(name = "conference_table", schema = "public")
public class Conference {
#Id
#Column(name = "conference_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long conference_id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User creator ;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column(name = "startConference")
private Date startConference;
#Column(name = "endConference")
private Date endConference;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.DETACH}, fetch = FetchType.LAZY)
#JoinTable(name = "attendance_table",
joinColumns = {
#JoinColumn(name = "conference_id", referencedColumnName = "conference_id",
nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "user_id",
nullable = false, updatable = false)})
private Collection<User> students = new HashSet<>();
Still not exactly sure why it works like this and not the other way around. If anyone knows please explain!

Criteria join query for composite primary key in hibernate

Need criteria join query for a composite primary key.
Entities:
ArtWork
#Entity
#Table(name = "artwork")
public class ArtWork implements io.malevich.web.entity.Entity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Id
#Column(name = "language", columnDefinition = "CHAR(2)")
private String language;
#Column(name = "art_name", nullable = false)
private String artName;
#Column(name = "creation_date", nullable = false)
private Date creationDate;
#Column(name = "edition_flag", nullable = false, columnDefinition = "tinyint(1)")
private boolean editionFlag;
#Column(name = "replica_flag", nullable = false, columnDefinition = "tinyint(1)")
private boolean replicaFlag;
#Column(name = "number_of_editions")
private Long numberOfEditions;
#Column(name = "original_id")
private Long originalId;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "category_id",
referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(
name = "language",
referencedColumnName = "language", insertable = false, updatable = false)
})
private Category category;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "gallery_id",
referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(
name = "language",
referencedColumnName = "language", insertable = false, updatable = false)
})
private Gallery gallery;
#ManyToOne
private Specialization specialization;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "author_id",
referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(
name = "language",
referencedColumnName = "language", insertable = false, updatable = false)
})
private Author author;
#Column
private String description;
#Column
private Double price;
//getter setter
}
User:
#javax.persistence.Entity
#Table(name = "user")
public class User implements Entity, UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true, length = 255, nullable = false)
private String name;
#Column(length = 255, nullable = false)
private String password;
#ElementCollection(fetch = FetchType.EAGER)
private Set<Role> roles = new HashSet<>();
#Column(name = "user_type_id")
private Long userTypeId;
#ManyToOne
#JoinColumn(name = "person_id", referencedColumnName = "id")
private Person person;
#ManyToOne
#JoinColumn(name = "organization_id", referencedColumnName = "id")
private Organization organization;
#ManyToOne
#JoinColumn(name = "file_id", referencedColumnName = "id")
private File file;
#Column(name = "activity_flag")
private boolean activityFlag;
//gettter and setter
}
Account States
#javax.persistence.Entity
#Table(name = "account_states")
public class AccountStates implements Entity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(insertable = false, updatable = false)
private String language;
#ManyToOne
#JoinColumns({ #JoinColumn(name = "artwork_id", referencedColumnName = "id"),
#JoinColumn(name = "language", referencedColumnName = "language") })
private ArtWork artwork;
#ManyToOne
#JoinColumn(name = "art_owner_id", referencedColumnName = "id")
private User artOwner;
#Column(name = "quantity")
private Long quantity;
#Temporal(TemporalType.DATE)
#Column(name = "buy_date")
private Date buyDate;
}
Account State Dao:
public class JpaAccountStatesDao extends JpaDao
implements AccountStatesDao {
public JpaAccountStatesDao() {
super(AccountStates.class);
}
#Override
public AccountStates find(Long artOwnerId, Long artworkId, String language) {
final CriteriaBuilder builder = this.getEntityManager().getCriteriaBuilder();
final CriteriaQuery<AccountStates> criteriaQuery = builder.createQuery(AccountStates.class);
Root<AccountStates> root = criteriaQuery.from(AccountStates.class);
Predicate p1 = builder.and(builder.equal(root.get("artwork"), artworkId),
builder.equal(root.get("artwork"), language), builder.equal(root.get("artOwner"), artOwnerId));
criteriaQuery.where(p1);
TypedQuery<AccountStates> typedQuery = this.getEntityManager().createQuery(criteriaQuery);
return typedQuery.getSingleResult();
}
}
I want to find Account States where artOwner id = 1 and language = en and artwork id = 1.
Can anyone suggest proper query for the same?
I found a solution for the same, I tried to pass a whole object instead of object id.
So final query is:
#Override
public AccountStates find(User artOwner, Artwork artwork) {
final CriteriaBuilder builder = this.getEntityManager().getCriteriaBuilder();
final CriteriaQuery<AccountStates> criteriaQuery = builder.createQuery(AccountStates.class);
Root<AccountStates> root = criteriaQuery.from(AccountStates.class);
Predicate p1 = builder.and(builder.equal(root.get("artwork"), artwork),
builder.equal(root.get("artOwner"), artOwner));
criteriaQuery.where(p1);
TypedQuery<AccountStates> typedQuery = this.getEntityManager().createQuery(criteriaQuery);
return typedQuery.getSingleResult();
}
}
Now, It works successfully... thanks

The constructor Category(String, String) is not visible - create constructor for some field using lombok?

I did googled a lot, still dont find any solution hence posting a question here..
I am developing Many-To-Many relationship example using lombok. I just want to create argument constructor for only two fields out of four. How we can do that ?
#Data
#Entity
#Table(name = "stock")
public class Stock implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "STOCK_ID", unique = true, nullable = false)
private Integer stockId;
#Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
private String stockCode;
#Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
private String stockName;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "stock_category", joinColumns = {
#JoinColumn(name = "STOCK_ID", nullable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "CATEGORY_ID", nullable = false, updatable = false)})
private Set<Category> categories = new HashSet<Category>(0);
}
Category
#Data
#RequiredArgsConstructor(staticName = "of")
#Entity
#Table(name = "category")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "CATEGORY_ID", unique = true, nullable = false)
private Integer categoryId;
#Column(name = "NAME", nullable = false, length = 10)
#NonNull
private String name;
#Column(name = "[DESC]", nullable = false)
#NonNull
private String desc;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private Set<Stock> stocks = new HashSet<Stock>(0);
}
App.java
Why cant I set the limitted field constructor
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
Category category2 = new Category("INVESTMENT", "INVESTMENT COMPANY");
Set<Category> categories = new HashSet<Category>();
categories.add(category1);
categories.add(category2);
stock.setCategories(categories);
session.save(stock);
session.getTransaction().commit();
System.out.println("Done");
}
}
The reason is that
If staticName set, the generated constructor will be private, and an additional
static 'constructor' is generated with the same argument list that
wraps the real constructor.
Please, don't forget about #NoArgsConstructor because Hibernate needs it.

Can't write my SQL query in Spring Data JPA custom repository

There is part of SQL i want to realize in my Custom JPA repository
SELECT * FROM users u
JOIN skills_user sku on sku.user_id = u.id
JOIN specs_user spu on spu.user_id = u.id
GROUP BY u.id
HAVING ANY(sku.dictionary_id in (15,20) or spu.dictionary_id in (15,20))
ORDER BY u.id
I tried this:
//Other predicates
if (filterQuery.getSkills() != null && !filterQuery.getSkills().isEmpty()) {
String[] tmp = filterQuery.getSkills().replaceAll(" ", "").split(",");
List<Integer> ids = new ArrayList<>();
for (String s : tmp) {
ids.add(Integer.parseInt(s));
}
List<Predicate> tmpPredicates = new ArrayList<>();
Join<User, Dictionary> skillJoin = root.join("skillList");
Join<User, Dictionary> specsJoin = root.join("specsList");
for (Integer id : ids) {
tmpPredicates.add(builder.or(builder.equal(skillJoin.get("id"), id), builder.equal(specsJoin.get("id"), id)));
}
predicates.add(builder.and(tmpPredicates.toArray(new Predicate[tmpPredicates.size()])));
}
//Other predicates
But it isn't work correctly.
How can i realise this correctly in JPA custom repository?
there is code of User and Dictionary classes:
#Entity
#SequenceGenerator(name = "user_gen", sequenceName = "users_seq")
#Table(name = "users")
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_gen")
private Long id;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#Column(name = "middlename")
private String middlename;
#Column(name = "academic_group")
private String academicGroup;
#Column(name = "entrance_year")
private int entranceYear;
#Column(name = "avatar_URL")
private String avatarURL;
#Column(name = "salt")
private String salt;
#Enumerated(EnumType.ORDINAL)
#Column(name = "user_group")
private UserGroup group;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "SocialRole_User", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "socialRole_id",
nullable = false, updatable = false) })
private List<SocialRole> socialRoleList;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "specs_user", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = true)},
inverseJoinColumns = {#JoinColumn(name = "dictionary_id",
nullable = false, updatable = true)})
private List<Dictionary> specsList;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "skills_user", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = true)},
inverseJoinColumns = {#JoinColumn(name = "dictionary_id",
nullable = false, updatable = true)})
private List<Dictionary> skillList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Contacts> contactsList;
//Getters and setters
}
Dictionary:
#Entity
#SequenceGenerator(name = "dictionary_gen", sequenceName = "dictionary_seq")
#Table(name = "dictionary")
public class Dictionary {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "dictionary_gen")
private Long id;
#Column(name = "dic_name")
private String name;
#Enumerated(EnumType.STRING)
#Column(name = "dic_type")
private DictionaryType type;
// Getters and Setters
}
Have you tried writing the query using JPQL?
SELECT a FROM User a
INNER JOIN a.specsList b
INNER JOIN a.skillList c
GROUP BY a.id
HAVING ANY(b.id in (15,20) OR c.id in (15,20))
ORDER BY a.id;
This JPQL should work the same as your plain SQL.

Categories

Resources