Working with spring-data and hibernate and trying to write this request in a repository :
#Query("select u from AppUser u inner join u.roles r inner join u.contrats c where r = :role and not exists ( from c.project p where p = :project)")
My goal is to get List of AppUser (Users) from entity AppUer ,but with a condition , only those who don't have the entity project .
To explain more , there is an entity Contrat between Project and AppUser , it contains Project project , AppUser appuser .
There is a mapping between one_to_many between AppUser and Contrat(AppUser can have multiple contrats) .
Also a mapping of one_to_many between Contrat and project ( One project can have multiple contrat ) .
The problme is that this request returns a empty list while it should return a list of 12 users as they don't have the specified project i passe to it.
I remark in this request (from c.project p where p = :project) 'from' is underlined with red line saying :
<expression> expected got <from> in spring-data hibernate.
Any idea ?
Edit
Entity AppUser
#Entity
#Data
#AllArgsConstructor #NoArgsConstructor
public class AppUser implements Serializable {
#Id #GeneratedValue
private Long id;
#Column(unique = true)
private String username;
private String password;
private String prenom;
private String nom;
private Long tel;
private String cin;
private String email ;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<AppRole> roles = new ArrayList<>();
#OneToMany(mappedBy = "appUser" )
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference(value="appuser-contrat")
private Collection<Contrat> contrats = new ArrayList<Contrat>();
public void addToContrats(Contrat contrat){
this.contrats.add(contrat);
}
}
Entity Contrat
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Contrat implements Serializable{
#Id #GeneratedValue
private Long id;
private Date dateDebut ;
private Date dateFin ;
private Long idDevloppeur;
#ManyToOne
#JoinColumn(name = "Id_Project")
#JsonBackReference(value="projet-contrat")
private Project project;
#ManyToOne
#JoinColumn(name = "Id_AppUser")
#JsonBackReference(value="appuser-contrat")
private AppUser appUser;
}
Entity Project
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Project implements Serializable{
#Id #GeneratedValue
private long id;
private String intitule;
private String description;
#OneToMany(mappedBy = "project" )
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference(value="projet-contrat")
private Collection<Contrat> contrats = new ArrayList<Contrat>();
public void addToContrats(Contrat contrat){
this.contrats.add(contrat);
}
}
(Posting answer to summarize solution in comments)
In given select query "inner join u.contrats" was used, but there were no contracts entities for users. Solution: replace with "left join u.contrats".
Inner join vs left join already solved here
What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?
Related
I created a search button with a SQL query which is including JOIN 2 times, but its throwing error like:
org.hibernate.loader.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias ID during auto-discovery of a
native-sql query
This is the Repository method:
#Query(value = "select * from reports r join patients p on r.patient_id = p.id join lab_technicians lt on r.lab_technician_id = lt.id where p.name like '%:patientName%' and lt.name like '%:labTechnicianName%' and p.identity_no like '%:patientIdentityNo%'", nativeQuery = true)
List<Report> findBySearch(#Param("patientName") String patientName, #Param("labTechnicianName") String labTechnicianName, #Param("patientIdentityNo") String patientIdentityNo);
To make you understand the project templates, these are the entities classes:
Person class:
#MappedSuperclass
public abstract class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
Patient class:
#Entity
#Table(name = "patients")
public class Patient extends Person{
#Column(name = "identity_no") private String identityNo;
#OneToMany(mappedBy = "patient") private List<Report> reports;
LabTechnician class:
#Entity
#Table(name = "lab_technicians")
public class LabTechnician extends Person{
#Column(name = "hospital_id")
private String hospitalId;
#OneToMany(mappedBy = "labTechnician")
private List<Report> reports;
and lastly Report class:
#Entity
#Table(name = "reports")
public class Report {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "lab_technician_id")
private LabTechnician labTechnician;
#ManyToOne
#JoinColumn(name = "patient_id")
private Patient patient;
#Column(name = "file_number")
private String fileNumber;
#Column(name = "diagnostic")
private String diagnostic;
#Column(name = "detail_of_diagnostic")
private String detailOfDiagnostic;
#Column(name = "date_of_report")
private Date dateOfReport;
I changed List in #OneToMany relationships to Set but its not changed anything
#Query(value = "select * from reports r join patients p on r.patient_id =
p.identity_no join lab_technicians lt on r.lab_technician_id =
lt.hospital_id where p.name like '%:patientName%' and lt.name like
'%:labTechnicianName%' and p.identity_no like '%:patientIdentityNo%'",
nativeQuery = true)
List<Report> findBySearch(#Param("patientName") String patientName,
#Param("labTechnicianName") String labTechnicianName,
#Param("patientIdentityNo") String patientIdentityNo);
Use this query it will work fine
I'm stuck on a JPA Specification task.
I have two entities, prefixes and country codes.
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity(name = "prefixes")
public class PrefixEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String prefix;
#OneToMany(mappedBy = "prefix", cascade = CascadeType.ALL)
private List<CountryCodeEntity> countryCodes;
#Data
#Builder
#Entity(name = "country_codes")
public class CountryCodeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String countryCode;
#ManyToOne
#JoinColumn(name = "prefixes_id")
private PrefixEntity prefix;
}
I want to get country code from "country_codes" table searching by prefix in "prefixes" table. I have already written a native query.
#Query(value = "SELECT cc.country_code FROM prefix_country_codes.prefixes p" +
" JOIN country_codes cc ON p.id = cc.prefixes_id" +
" WHERE p.prefix=?1", nativeQuery = true)
List<String> getCountryCodeByPrefix(String prefix);
How can I do this using Spring JPA Specification?
Something like:
public interface PrefixSpecification<P> {
static Specification<PrefixEntity> joinTest(String prefix) {
return (root, query, criteriaBuilder) -> {
Join<PrefixEntity, CountryCodeEntity> countryCodes = root.joinList("countryCodes");
...
};
}
}
and
phoneNumberRepository.findAll(Specification.where(PrefixSpecification.joinTest("371")));
Video Entity :
#Entity
#Table(name = "video")
public class Video implements Serializable{
....
#OneToMany(mappedBy = "video",fetch = FetchType.LAZY)
#Getter
#Setter
private List<ActivityLog> activityLogs;
}
Second Entity :
#Entity
#Table(name = "activity_log")
public class ActivityLog implments Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
#Getter
#Setter
private Long id;
#Column(name = "action")
#Enumerated(EnumType.STRING)
#Getter
#Setter
private ActivityLogEnum action;
#Column(name = "ctime")
#Temporal(TemporalType.TIMESTAMP)
#Getter
#Setter
private Date ctime;
#JoinColumn(name = "account", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
#Getter
#Setter
private Account account;
#JoinColumn(name = "video", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
#Getter
#Setter
private Video video;
}
I want to select videos with activitylogs grouped by action field
So I tried :
select v,(select al from ActivityLog al where al.video.id=v.id group by al.action order by al.id desc) as al from Video v
Then faced that correlated subquery produces : Subquery returns more than 1 row MySQL
Tried also left join
select v from Video v left join (select al from ActivityLog al where al.video.id=v.id group by al.id,al.action order by al.id desc)
Same problem
Also tried join fetch but was stuck in how to write group by
So how to make this in Hibernate
activity_log table :
Action value would be one of 3 mentioned in following enum :
public enum ActivityLogEnum {
TRANSLATION_SESSION_STARTED("Session Start Time"), TRANSLATION_APPROVAL("Approval Start Time"), TRANSLATION_REJECTION("Rejection Start Time");
#Getter
#Setter
private String label;
private ActivityLogEnum(String label) {
this.label = label;
}
}
I want to group by the action desc so that multiple actions won't be fetched just last unque 3 types actions can be fetched.
Thanks in advance
So I have these relations between these 3 tables
And what I'm trying to do is to search (by code or designation) and get list of projects for a specific chief, something like this:
SELECT * FROM Project p JOIN ProjectHasChief phc ON (p.id = phc.idproject)
JOIN Chief c ON (c.id = phc.id_chief)
WHERE c.id = (myID)
AND (designation LIKE '%user_string%' OR code LIKE '%user_string%');
AND since I'm using hibernate I've used Criteria:
Criteria c = session.createCriteria(ProjectHasChief.class);
c.add(Restrictions.eq("chief", chief))
.add(Restrictions.disjunction()
.add(Restrictions.like("project.designation", reg, MatchMode.ANYWHERE))
.add(Restrictions.like("project.code", reg, MatchMode.ANYWHERE)));
List<ProjectHasChief> list = (List<ProjectHasChief>)c.list();
But I get this error:
org.hibernate.QueryException: could not resolve property: project.designation of: smt.agm.entities.ProjectHasChief
Project entity:
#Entity
#Table(name="projet")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", columnDefinition="serial")
private int id;
#Column(name="code")
private String code;
#Column(name="designation")
private String designation;
#OneToMany(cascade = CascadeType.ALL, mappedBy="project", orphanRemoval=true)
#OrderBy(clause="id DESC")
private List<ProjectHasChief> chiefs = new ArrayList<ProjectHasChief>();
}
Chief entity:
#Entity
#Table(name="chief")
public class Chief {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", columnDefinition="serial")
private int id;
#Column(name = "name")
private String name;
}
ProjectHasChief entity:
#Entity
#Table(name="mapping_chantier_chef")
public class ProjectHasChief {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", columnDefinition="serial")
private int id;
#ManyToOne
private Project project;
#ManyToOne
private Chief chief;
#Column(name="date_deb")
private Date startDate;
#Column(name="date_fin")
private Date endDate;
}
Whe hibernate don't know the property designation of Project entity ?!!
Are you absolutely sure all the mappings and joins actually work?
If so, you can try to declare an implicit alias for Project like so:
Criteria c = session.createCriteria(ProjectHasChief.class);
c.createAlias("project", "project") //THIS ONE
c.add(Restrictions.eq("chief", chief))
c.add(Restrictions.disjunction()
.add(Restrictions.like("project.designation", reg, MatchMode.ANYWHERE))
.add(Restrictions.like("project.code", reg, MatchMode.ANYWHERE)));
List<ProjectHasChief> list = (List<ProjectHasChief>)c.list();
This HQL results in the tremendously helpful "error in named query" message
"FROM courseform c WHERE c.application.application IN
(SELECT a.application FROM application a WHERE a.applicant=: applicant)"
The CourseForm has a OneToOne unidirectional relationship with the Application (this could potentially be made bidirectional if it would help). Application in turn has the same unidirectional OneToOne relationship with the Applicant. One applicant can have many applications.
Here are the (abridged) definitions
CourseForm
#Entity
#NamedQueries({
#NamedQuery(name = "CourseForm.findByApplicant",
query = "FROM courseform c WHERE c.application.application IN
(SELECT a.application FROM application a WHERE a.applicant=: applicant)") })
public class CourseForm implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
#Enumerated(EnumType.STRING)
private Career career;
private String academicPlan;
private String courseName;
#Enumerated(EnumType.STRING)
private ModeOfAttendance modeOfAttendance;
#OneToOne
#JoinColumn(name = "application_fk")
private Application application;
...
}
Application
#Entity
public class Application implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long applicationNumber;
#OneToOne
#JoinColumn(name = "applicant_fk")
private Applicant applicant;
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime lastUpdate = LocalDateTime.now();
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime submitted = null;
public Application() {
}
Applicant
#Entity
#NamedQueries({ #NamedQuery(name = "Applicant.findByApplicantID",
query = "FROM Applicant a WHERE a.applicantID= :applicantID") })
public class Applicant implements Serializable {
private static final long serialVersionUID = -7210042752148566673L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
private String applicantID;
Applicant() {
}
Ok, according to Mark, right answer is
FROM courseform c WHERE c.application.applicant =: applicant
I think the error is with FROM courseform c WHERE c.application.application.You have used c.application.application in where clause
It should be FROM courseform c WHERE c.application
You can change to:
FROM courseform c WHERE c.application.applicationNumber IN (SELECT a.applicationNumber FROM application a WHERE a.applicant=: applicant)