Query to sort data based on another entity - java

I'm writing a site of a hospital. This is an MVC application with database. Database contains data about patients, doctors etc.
I need to get List of doctors which should be sorted by patient count. I have already tried to do this with Comparator inside Java code like this example:
Page<Doctor> pageDoctor = doctorRepository.findAll(pageable);
List<Doctor> doctorList = pageDoctor.getContent();
doctorList.sort(Comparator.comparing(o -> patientRepository.findAllByDoctor(o).size()));
but I need the sorted list inside Page content. I don't really understand how to make the query equivalent to this example, because I'm new to SQL. Here are my entity and repository classes.
Doctor.java
#Entity
#Table(name = "doctors")
public class Doctor {
#Id
#Column(name = "id", nullable = false, unique = true)
#SequenceGenerator(name="doctors_generator", sequenceName = "doctors_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "doctors_generator")
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "doctors_type_id")
private DoctorsType doctorsType;
public Doctor(User user, DoctorsType doctorsType) {
this.user = user;
this.doctorsType = doctorsType;
}
public Doctor() {
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public DoctorsType getDoctorsType() {
return doctorsType;
}
public void setDoctorsType(DoctorsType doctorsType) {
this.doctorsType = doctorsType;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Patient.java
#Entity
#Table(name = "patients")
public class Patient {
#Id
#Column(name = "id", nullable = false, unique = true)
#SequenceGenerator(name="patients_generator", sequenceName = "patients_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "patients_generator")
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "doctor_id")
private Doctor doctor;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "treatment_id")
private Treatment treatment;
public Patient(User user, Doctor doctor, Treatment treatment) {
this.user = user;
this.doctor = doctor;
this.treatment = treatment;
}
public Patient() {
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Doctor getDoctor() {
return doctor;
}
public void setDoctor(Doctor doctor) {
this.doctor = doctor;
}
public Treatment getTreatment() {
return treatment;
}
public void setTreatment(Treatment treatment) {
this.treatment = treatment;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
PatientRepository.java
#Repository
public interface PatientRepository extends JpaRepository<Patient, Long> {
Patient findPatientByUser(User user);
List<Patient> findAllByDoctor(Doctor doctor);
Patient findPatientById(long id);
Page<Patient> findAllByOrderByIdAsc(Pageable pageable);
List<Patient> findAllByOrderByIdAsc();
}
DoctorRepository.java
#Repository
public interface DoctorRepository extends JpaRepository<Doctor, Long> {
Doctor findDoctorById(long id);
Doctor findDoctorByUser(User user);
#Query(
//sql query there
)
Page<Doctor> findAllByPatientCountAsc(Pageable pageable);
Page<Doctor> findAll(Pageable pageable);
List<Doctor> findAllByOrderByIdAsc();
List<Doctor> findAllByDoctorsTypeNot(DoctorsType doctorsType);
List<Doctor> findAllByDoctorsType(DoctorsType doctorsType);
}
Thanks for your answers in advance.

Check this one:
SELECT d FROM Doctor d,
LEFT JOIN Patient p ON d.id = p.doctor.id
GROUP BY d
ORDER BY COUNT(p.id)

Related

#ManyToMany saving relations is available only one time in one direction

I will try create #ManyToMany relation in my SpringBoot application.
I use JPARespository, I create 2 models:
User
#Entity(name = "User")
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
#ManyToMany//(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
#LazyCollection(value = LazyCollectionOption.FALSE)
#JoinTable(
name = "users_groups",
joinColumns = #JoinColumn(name = "users_id"),
inverseJoinColumns = #JoinColumn(name = "groups_id")
)
private Set<UGroup> groups;
public User() {
this.groups = new HashSet<>();
}
public void addGroup(UGroup uGroup) {
this.groups.add(uGroup);
uGroup.users.add(this);
}
public void removeGroup(UGroup uGroup) {
this.groups.remove(uGroup);
uGroup.users.remove(this);
}
}
UGroups
#Data
#Entity(name = "UGroup")
#Table(name = "groups")
public class UGroup {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
#ManyToMany(mappedBy = "groups", cascade = {CascadeType.MERGE, CascadeType.PERSIST})
#LazyCollection(value = LazyCollectionOption.FALSE)
public Set<User> users;
public UGroup() {
this.users = new HashSet<>();
}
public void addUser(User user) {
this.users.add(user);
user.getGroups().add(this);
}
public void removeUser(User user) {
this.users.remove(user);
user.getGroups().remove(this);
}
}
I load start data via DataConfiguration class:
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
User user = new User();
user.setName("Damian");
userRepository.save(user);
user = new User();
user.setName("Marta");
userRepository.save(user);
user = new User();
user.setName("Natalia");
userRepository.save(user);
UGroup uGroup = new UGroup();
uGroup.setName("Mieszkanie");
uGroup = uGroupRepository.save(uGroup);
user = userRepository.findById(1).get();
user.addGroup(uGroup);
user = userRepository.save(user);
// unable to execute
/*
user = userRepository.findById(2).get();
user.addGroup(uGroup);
user = userRepository.save(user);
*/
// unable to execute 2
/*
uGroup = uGroupRepository.findById(4).get();
uGroup.addUser(userRepository.findById(3).get());
uGroup = uGroupRepository.save(uGroup);
*/
}
When i execute only insert group for 1 user all is good.
Although when I try execute insert group for 1 and 2 users or insert 3 user for group it cause java.lang.StackOverflowError: null exception.
I don know where is error. Can anyone help me and explain me where and why I should change something?
Thanks in advance.
With Join Table,it it possible to map ManyToMany bidirectional relation.These are the modified entity classes
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Entity(name = "User")
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public Set<UGroup> getGroups() {
return groups;
}
public void setGroups(Set<UGroup> groups) {
this.groups = groups;
}
public void setName(String name) {
this.name = name;
}
#ManyToMany(mappedBy = "users",cascade = CascadeType.ALL)
#LazyCollection(value = LazyCollectionOption.FALSE)
private Set<UGroup> groups=new HashSet<UGroup>();
public User() {
}
public void addGroup(UGroup uGroup) {
this.groups.add(uGroup);
uGroup.users.add(this);
}
public void removeGroup(UGroup uGroup) {
this.groups.remove(uGroup);
uGroup.users.remove(this);
}
}
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Entity(name = "UGroup")
#Table(name = "groups")
public class UGroup {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "USER_GROUP_MAP" ,joinColumns = #JoinColumn(name="GROUP_ID",referencedColumnName = "ID")
,inverseJoinColumns = #JoinColumn(name="USER_ID",referencedColumnName = "ID"))
#LazyCollection(value = LazyCollectionOption.FALSE)
public Set<User> users=new HashSet<>();
public UGroup() {
}
public void addUser(User user) {
this.users.add(user);
user.addGroup(this);
}
public void removeUser(User user) {
this.users.remove(user);
user.removeGroup(this);
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
For ManyToMany relations join table is necessary.
SELECT * FROM USER_GROUP_MAP;
GROUP_ID USER_ID
4 1
4 2
4 3
This is the result with the code you have posted.You should override hashcode and equals method in both the entity classes in order to avoid duplication of data in the tables.
Code used for testing the above relationship mappings.
#Override
public void onApplicationEvent(ContextRefreshedEvent applicationEvent) {
User user = new User();
user.setName("Damian");
userRepository.save(user);
user = new User();
user.setName("Marta");
userRepository.save(user);
user = new User();
user.setName("Natalia");
userRepository.save(user);
UGroup uGroup = new UGroup();
uGroup.setName("Mieszkanie");
uGroup = uGroupRepository.save(uGroup);
user = userRepository.findById(1).get();
user.addGroup(uGroup);
user = userRepository.save(user);
// unable to execute
user = userRepository.findById(2).get();
user.addGroup(uGroupRepository.findById(4).get());
user = userRepository.save(user);
// unable to execute 2
uGroup = uGroupRepository.findById(4).get();
uGroup.addUser(userRepository.findById(3).get());
uGroup = uGroupRepository.save(uGroup);
}

Hiberate one-to-one annotaion builds wrong sql query

I have a small problem with hibernate query with one-to-one relation
I created 3 entities. In case of one-to-one relation user-to-group it works properly. I use property create in hiberanate config and it creates correct table with correct FK.
Creating record work correct too
MyUser user = new MyUser();
user.setLogin("Mark");
user.setPassword("123456");
MyPresence presence = new MyPresence();
presence.setPresenceId(2);
presence.setUser(user);
session.beginTransaction();
session.save(user);
session.save(presence);
session.getTransaction().commit();
session.close();
if get User by presence it works correct
int id = 2;
MyPresence myPresence = session.load(MyPresence.class, id);
System.out.println(myPresence);
But when I'm try to get presence by user result from database hibernate build wrong sql query
int id = 3;
MyUser myUser = session.load(MyUser.class, id);
System.out.println(myUser);
Generated Hibernate sql:
Hibernate: select myuser0_.id as id1_2_0_, myuser0_.group_id as group_id4_2_0_, myuser0_.login as login2_2_0_, myuser0_.password as password3_2_0_, mypresence1_.id as id1_1_1_, mypresence1_.presence_id as presence2_1_1_, mypresence1_.updated_at as updated_3_1_1_, mypresence1_.user_id as user_id4_1_1_ from users myuser0_ left outer join user_presence mypresence1_ on myuser0_.id=mypresence1_.id where myuser0_.id=?
But I expect to see
on myuser0_.id=mypresence1_.user_id instead of on myuser0_.id=mypresence1_.id
MyUser.java
#Entity
#Table(name = "users")
public class MyUser {
private int id;
private String login;
private String password;
private MyGroup group;
private MyPresence presence;
public MyUser() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "login")
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
#Column(name = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#JoinColumn(name = "group_id")
#OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
public MyGroup getGroup() {
return group;
}
public void setGroup(MyGroup group) {
this.group = group;
}
#OneToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "id", referencedColumnName = "user_id")
public MyPresence getPresence() {
return presence;
}
public void setPresence(MyPresence presence) {
this.presence = presence;
}
}
MyPresence.java
#Entity
#Table(name = "presence")
public class MyPresence {
private int id;
private MyUser user;
private int presenceId;
private Date updatedAt;
public MyPresence() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", referencedColumnName = "id")
public MyUser getUser() {
return user;
}
public void setUser(MyUser user) {
this.user = user;
}
#Column(name = "presence_id")
public int getPresenceId() {
return presenceId;
}
public void setPresenceId(int presenceId) {
this.presenceId = presenceId;
}
#Column(name = "updated_at")
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}

Empty List (not Table) at ManyToMany-Relation

i faced the same problem as empty tables when using many to many relations in jpa. Sadly this post was without solution. I have a class with a many-to-many relation in JPA/EclipseLink v2.6.3. Here the class ROLE:
#Entity
public class Role implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
#JoinTable(name = "ROLE_COMPETENCE", joinColumns = #JoinColumn(name = "ROLE_ID", referencedColumnName = "ID"), inverseJoinColumns = #JoinColumn(name = "COMPETENCE_ID", referencedColumnName = "ID"))
private List<Competence> competences = new ArrayList<Competence>();
#Override
public String toString() {
return "Role [id=" + id + "]";
}
public void addCompetence(Competence competence) {
if (this.competences != null) {
if (!this.competences.contains(competence)) {
this.competences.add(competence);
competence.addRole(this);
}
}
}
public int getId() {
return id;
}
public Role() {
super();
}
public List<Competence> getCompetences() {
return competences;
}
}
And the class COMPETENCE
#Entity
public class Competence implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(length = 1024, nullable = false)
private String title;
#JsonIgnore
#ManyToMany(mappedBy = "competences", fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
private List<Role> roles;
public int getId() {
return id;
}
public Competence() {
super();
}
public void addRole(Role role) {
if (this.roles != null) {
if (!this.roles.contains(role)) {
this.roles.add(role);
role.addCompetence(this);
}
}
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<Role> getRoles() {
return roles;
}
#Override
public String toString() {
return "Competence [id="+id+", title="+title+"]";
}
}
When i now use
for (int i = 0; i < 10; i++) {
Role role = new Role();
RoleService.create(role);
for (int j = 0; j < 3; j++) {
Competence c = new Competence();
c.setTitle("Competence "+i+"."+j);
CompetenceService.create(c);
lastCompetence = c;
role.addCompetence(c);
}
RoleService.update(role);
}
with
public void x(Object object) {
EntityManager em = ...
EntityTransaction tx = em.getTransaction();
tx.begin();
em.merge(object); //for x=update or em.persist(object) for x=create
tx.commit();
em.close();
}
The strange thing is that the join-table ROLE_COMPETENCE is correct when i add or remove objects. When i load ROLE's they have competences an can be shown. But the list "roles" is empty when i load competences from the database.
Any idea?
I found my mistake. It was just that the list was not instantiated. With List<Role> roles = new ArrayList<Role>(); everything works fine.

how to write join query in hibernate

I have created two beans User and VirtualDomain with many to many relationship
#Entity
#Table(name = "tblUser")
public class User implements Serializable {
private Long id;
private String username;
private Set<VirtualDomain> virtualdomainset;
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "username", length = 50, nullable = false)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#ManyToMany(targetEntity = VirtualDomain.class, cascade = {CascadeType.PERSIST},fetch=FetchType.EAGER)
#JoinTable(name = "tblUserDomainRel", joinColumns = #JoinColumn(name = "userid"), inverseJoinColumns = #JoinColumn(name = "domainid"))
public Set<VirtualDomain> getVirtualdomainset() {
return virtualdomainset;
}
public void setVirtualdomainset(Set<VirtualDomain> virtualdomainset) {
this.virtualdomainset = virtualdomainset;
}
}
#Entity
#Table(name = "tblVirtualDomain")
public class VirtualDomain {
private Long id;
private String domainname;
private Set<User> userset;
#Id
#JoinColumn(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "domain_name")
public String getDomainname() {
return domainname;
}
public void setDomainname(String domainname) {
this.domainname = domainname;
}
#ManyToMany(cascade = {CascadeType.ALL},fetch=FetchType.EAGER, mappedBy = "virtualdomainset", targetEntity = User.class)
public Set<User> getUserset() {
return userset;
}
public void setUserset(Set<User> userset) {
this.userset = userset;
}
}
how to get data of user like username related to particular domain through hibernate.
To add to gid's answer, if for some reason you need to eagerly fetch an entites relations, then the join syntax would be join fetch.
from VirtualDomain vd join fetch vd.usersset u
where vd.domainname = 'example.com' and u.username like 'foo%'
Always difficult to write HQL without a test system...but here we go:
select u from VirtualDomain vd join User vd.usersset u
where vd.domainname = 'example.com' and u.username like 'foo%'
Let me know how you get on.
One tip I often did prior to buying Intellji was to stop the app in the debugger and then use the immediate window to experiment with HQL.
The hibernate documentation on joins has always been a bit cryptic in my opinion.

how to write delete query in hibernate for many to many relationship

I have two beans user and virtualdomain
#Entity
#Table(name = "tblUser")
public class User implements Serializable {
private Long id;
private String username;
private String deleteflag;
private Set<VirtualDomain> virtualdomainset;
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "username", length = 50, nullable = false)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "deleteflag")
public String getDeleteflag() {
return deleteflag;
}
public void setDeleteflag(String deleteflag) {
this.deleteflag = deleteflag;
}
#ManyToMany(targetEntity = VirtualDomain.class, cascade = {CascadeType.PERSIST},fetch=FetchType.EAGER)
#JoinTable(name = "tblUserDomainRel", joinColumns = #JoinColumn(name = "userid"), inverseJoinColumns = #JoinColumn(name = "domainid"))
public Set<VirtualDomain> getVirtualdomainset() {
return virtualdomainset;
}
public void setVirtualdomainset(Set<VirtualDomain> virtualdomainset) {
this.virtualdomainset = virtualdomainset;
}
}
#Entity
#Table(name = "tblVirtualDomain")
public class VirtualDomain {
private Long id;
private String domainname;
private String deleteflag;
private Set<User> userset;
#Id
#JoinColumn(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "domain_name")
public String getDomainname() {
return domainname;
}
public void setDomainname(String domainname) {
this.domainname = domainname;
}
#Column(name = "deleteflag")
public String getDeleteflag() {
return deleteflag;
}
public void setDeleteflag(String deleteflag) {
this.deleteflag = deleteflag;
}
#ManyToMany(cascade = {CascadeType.ALL},fetch=FetchType.EAGER, mappedBy = "virtualdomainset", targetEntity = User.class)
public Set<User> getUserset() {
return userset;
}
public void setUserset(Set<User> userset) {
this.userset = userset;
}
}
Now when I delete some user i use to set the deleteflag which means that the data remains in the database.
My requirement is that the user whose delete flag is set must be removed from the tblUserDomainRel table so how to write that delete query.
just remove the virtualDomain from the collection on the User and remove the other side for completeness
// on User
public void removeVirtualDomain(VirtualDomain vd){
virtualDomainset.remove(vd);
vd.getUserset().remove(this)
}
this will remove the relationship record. Or to remove a user from all virtual Domains:
// on User
public void removeFromAllVirtualDomains(){
for( VirtualDomain vd : virtualdomainset ){
vd.getUserset().remove(this);
}
virtualDomainset.clear();
}

Categories