JPQL Query error with Spring JPA and Hibernate - java

I am trying to fetch all the Skills relating to a specific Person.
Getting the following error when trying to fetch data from MySQL DB using JPQL in my Spring Boot application:
org.hibernate.QueryException: could not resolve property: person_skill of: com.skilltrack.app.Domain.Skill [SELECT s FROM com.skilltrack.app.Domain.Skill s JOIN s.person_skill ps WHERE ps.fk_person = ?1 ]
Here is my code:
Person.java
#Data
#EqualsAndHashCode(exclude = "personCourses")
#Entity(name = "person")
#Table(name = "person")
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer person_id;
#NotBlank
private String name;
#NotBlank
private String surname;
#NotBlank
private String email;
#NotBlank
private String password;
#NotBlank
private String personType; //admin or user
#JsonIgnore
#ManyToMany // this mapping is referred to in the Course class
#JoinTable(name = "person_course",
joinColumns = #JoinColumn(name = "fk_person", referencedColumnName = "person_id"),
inverseJoinColumns = #JoinColumn(name = "fk_course", referencedColumnName = "course_id"))
private List<Course> personCourses;
#JsonIgnore
#ManyToMany // this mapping is referred to in the Skill class
#JoinTable(name = "person_skill",
joinColumns = #JoinColumn(name = "fk_person", referencedColumnName = "person_id"),
inverseJoinColumns = #JoinColumn(name = "fk_skill", referencedColumnName = "skill_id"))
private List<Skill> personSkills;
Course.java
#Data
#EqualsAndHashCode(exclude = "skills")
#Entity(name = "course")
#Table(name = "course")
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer course_id;
#NotBlank
private String name;
#NotBlank
private String description;
#JsonIgnore
#ManyToMany(mappedBy = "courses") // mapping defined in Skill already
private List<Skill> skills;
#NotBlank
private Boolean completed; // 0 no 1 yes
#JsonIgnore
#ManyToMany(mappedBy = "personCourses")
private List<Person> coursePerson;
Skill.java
#Data
#EqualsAndHashCode(exclude = "courses")
#Entity(name = "skill")
#Table(name = "skill")
public class Skill {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer skill_id;
#NotBlank
private String name;
#NotBlank
private String description;
#JsonIgnore
#ManyToMany(cascade = CascadeType.ALL) // this mapping is referred to in the Course class
#JoinTable(name = "course_skills",
joinColumns = #JoinColumn(name = "fk_skill", referencedColumnName = "skill_id"),
inverseJoinColumns = #JoinColumn(name = "fk_course", referencedColumnName = "course_id"))
private List<Course> courses;
#JsonIgnore
#ManyToMany(mappedBy = "personSkills")
private List<Person> skill_person;
PersonRepository.java
#Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
#Query(value =
"SELECT s " +
"FROM skill s JOIN s.person_skill ps " +
"WHERE ps.fk_person = ?1 ")
List<Skill> getPersonSkills(Integer personID);
}
The issue is with the JPQL statement:
"SELECT s FROM skill s JOIN s.person_skill ps WHERE ps.fk_person = ?1 "
I have tried the following variations:
"SELECT s FROM skill s INNER JOIN s.person_skill ps WHERE ps.fk_person = ?1"
"SELECT s FROM skill s JOIN FETCH s.person p WHERE ps.fk_person = ?1"
"SELECT s FROM skill s JOIN s.person p JOIN s.person_skill ps WHERE ps.fk_person = ?1"
"SELECT s FROM skill s JOIN person p JOIN person_skill ps WHERE ps.fk_person = ?1"
How should I change my JPQL query to return a list of Skills relating to a specific Person?

Change your wrongly typed entity name from person_skill to skill_person defined in Skill.java

You have this error org.hibernate.QueryException because you have wrong name of field in JPQL.
Skill entity has field with name: skill_person.
You have another name of field in your JPQL query: person_skill.

It is possible to acieve the result without writing a custom query.
Spring builds endpoints like "skills" and "persons" by default, when used, they show other endpoints like "http://localhost:8080/skills/{id}/skill_person"
Playing around with those will lead you to find any combination of results.
This is possible since the many to many mappings are defined bidirectionally the way thy are in the current project and spring is awesome.

Related

duplicate [ID] alias error for sql statement with join

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

Spring Boot JPA how to retrieve data from a ManyToMany table?

I am learning Spring boot.
Now I have ManyToMany relationship with a User entity and a Team entity.
So an user can have several teams and a team can have several users.
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id", nullable =false, updatable = false)
private Long id;
#ManyToMany
#JoinTable(
name = "team_have",
joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn(name = "teamId"))
List<Team> haveTeams;
This is the Team entity:
#Entity
#Table(name="TEAM")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long teamId;
#OneToMany(cascade = CascadeType.ALL, mappedBy="team")
private List<Task> tasks;
This is my repository:
public interface TeamRepository extends CrudRepository<Team, Long>{
List<Team>findByName(String name);
}
How can I find all teams which belonged to one user?
User already have mapped with List<Team>, you need to use UserRepository instead of TeamRepository.
public interface UserRepository extends CrudRepository<User, Long>{
User findByName(String name);
}
Here, you will get one user and that user have all teams which he belongs to. (Assuming username is unique)
OR
If you are having bidirectional Many-to-Many (Team also mapped Lis<User>) like following
#Entity
#Table(name="TEAM")
public class Team {
....
#ManyToMany(cascade = CascadeType.ALL, mappedBy="haveTeams")
private List<User> users;
Then you can define query method like following to get all teams for one user,
public interface TeamRepository extends CrudRepository<Team, Long>{
List<Team> findByUsers(User user);
}
The best way to build the connection between them would be to use a bi-directional relationship, like this:
User Entity
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
/* #Column(name = "id", nullable = false, updatable = false) You don't need this,
the name is taken from the name of the variable and it cannot be null since you
have #GeneratedValue */
private Long id;
#ManyToMany(fetch = LAZY, cascade = CascadeType.PERSIST)
#JoinTable(
name = "user_team", // I would name this user_team since the user can have many teams, and vice versa
joinColumns = #JoinColumn(name = "user_id" , referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"))
Set<Team> teams = new HashSet<>();
}
Team Entity
#Entity
#Table(name="team")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long teamId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "team")
private List<Task> tasks;
#ManyToMany(mappedBy = "teams", fetch = LAZY, cascade = CascadeType.PERSIST)
private Set<User> users = new HashSet<>();
}
Team Repo
public interface TeamRepository extends CrudRepository<Team, Long>{
#Query(value = "SELECT * FROM team t1 INNER JOIN user_team t2 ON t1.id = t2.team_id WHERE t2.user_id = ?1 ")
List<Team> findAllByUserId(long userId);
}
Example of use:
List<Team> teams = teamRepo.findAllByUserId(1);
A great tutorial:
https://attacomsian.com/blog/spring-data-jpa-many-to-many-mapping

JPA Query for #JoinTable

Scenario:
I have two entities User and Program
#Entity
#Table(name = "user")
public class UserEntity implements Serializable{
#Id
public Long id;
public String firstName;
public String email;
#OneToOne(cascade = CascadeType.ALL)
#JoinTable(
name = "user_program",
joinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "id")
},
inverseJoinColumns = {
#JoinColumn(name = "program_id", referencedColumnName = "id")
}
)
public ProgramEntity program;
}
JPQL:
SELECT
Program.name,
COUNT(user.id) AS user_count
FROM UserEntity AS user
INNER JOIN ProgramEntity AS program on ________ GROUP BY Program.name
I tried to get the number of users in each program but I couldn't get the result due to the JoinTable (intermediate table) is not an entity. Can anyone suggest a JPQ to connect the join table?
You can join using the entity object, 'ON' is not necessary. For you example,
SELECT prg.name, COUNT(user.id) AS user_count FROM UserEntity AS user INNER JOIN user.program AS prg GROUP BY prg.name

How to create a native query to join related tables using #Query annotation

I am trying to use the #Query annotation with native query to get information from the database which I intend to use on one of the pages of my web shop application. I tried searching for a similar question but with no luck, so here goes...
Here is the part of the data model in question:
The Products class:
#Entity
#Table(name = "products")
public class Products {
public enum EProductType {
FW_FISH, SW_FISH, P_FISH, CRAB_FISH, FW_PLANT, P_PLANT, TANK, FILTER, CO2, FOOD
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "title")
private String title;
#Column(name = "description")
private String description;
#Column(name = "the_type")
#Enumerated(EnumType.STRING)
private EProductType productType;
#OneToMany(cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH }, mappedBy = "product")
private List<Stock> stock;
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH })
#JoinTable(name = "products_order", joinColumns = #JoinColumn(name = "id"), inverseJoinColumns = #JoinColumn(name = "product_id"))
private List<Orders> orders;
The Stock class:
#Entity
#Table(name = "stock")
public class Stock {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "price")
private double price;
#Column(name = "quantity")
private int quantity;
#Column(name = "price_date")
private Date priceDate;
#ManyToOne(cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
#JoinColumn(name = "product_id")
private Products product;
The Order class:
#Entity
#Table(name = "orders")
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "date_time")
private LocalDateTime date;
#Column(name = "total_price")
private double totalPrice;
#ManyToOne(cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
#JoinColumn(name = "user_id")
private Users user;
#ManyToMany
#JoinTable(name = "products_order", joinColumns = #JoinColumn(name = "id"), inverseJoinColumns = #JoinColumn(name = "order_id"))
private List<Products> products;
And here are the repositories(as requested in the comments):
Stock Repository:
public interface IStockRepository extends CrudRepository<Stock, Integer> {}
Products Repository:
public interface IProductsRepository extends CrudRepository<Products, Integer> {
#Query(value = "SELECT title, the_type, price, quantity FROM Products RIGHT OUTER JOIN Stock ON products.id=stock.product_id", nativeQuery = true)
List<Products> getProductsInfoForTheHomePage();
}
The information i need on the html page are: title from the products table and price and quantity from the stock table.
I am trying with this query in my Repository interface:
#Query(value = "SELECT title, the_type, price, quantity FROM Products RIGHT OUTER JOIN Stock ON products.id=stock.product_id", nativeQuery = true)
List<Products> getProductsInfoForTheHomePage();
...but when i try to call the method in my controller class a get this exception:
java.sql.SQLException: Column 'id' not found.
This is what the Hibernate logger shows:
Hibernate: SELECT title, the_type, price, quantity FROM Products RIGHT OUTER JOIN Stock ON products.id=stock.product_id
Since the column is present in the data model I am guessing that the problem is in the query...
Excuse my clumsy english, hope someone can help me out couse I’m stuck on this for a while...

Join queries with JPQL in Spring Data Jpa

I created a left join query with JPQL in spring data jpa but failed in my unit test. There are two entities in the project.
Product entity:
#Entity
#Table(name = "t_goods")
public class Product implements Serializable {
#Id
#GeneratedValue
#Column(name = "id", length = 6, nullable = false)
private Integer id;
#Column(name = "name", length = 20, nullable = false)
private String name;
#Column(name = "description")
private String desc;
#Column(name = "category", length = 20, nullable = false)
private String category;
#Column(name = "price", nullable = false)
private double price;
#Column(name = "is_onSale", nullable = false)
private Integer onSale;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "brand_id")
private Brand brand;
// getter and setter
}
Brand entity:
#Entity
#Table(name = "tdb_goods_brand")
public class Brand implements Serializable {
#Id
#GeneratedValue
#Column(name = "id", length = 6, nullable = false)
private Integer id;
#Column(name = "brand_name", unique = true, nullable = false)
private String name;
#OneToMany(mappedBy = "brand", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Product> products;
// getter and setter
}
And a third class Prod to map the query results to Object:
public class Prod implements Serializable {
private Integer id;
private String name;
private double price;
//private String brandName;
// getter and setter
}
It works fine with this query:
public interface ProductRepository extends JpaRepository<Product, Integer> {
#Query(value = "select new com.pechen.domain.Prod(p.id, p.name, p.price) from Product p ")
Page<Prod> pageForProd(Pageable pageRequest);
}
But if I add new property brandName for Prod and refactor the query with left join, it test fails:
#Query(value = "select new com.pechen.domain.Prod(p.id, p.name, p.price, b.name) from Product p left join com.pechen.domain.Brand b on p.brand_id = b.id")
Page<Prod> pageForProd(Pageable pageRequest);
The problem seems to be here on p.brand_id = b.id because there is not a brand_id property in Product, it's just a column name. So how can I make this work?
Update:
There turned to be some sytax errors in the JPQL query, just fix it as the following:
#Query(value = "select new com.pechen.domain.Prod(p.id, p.name, p.price, b.name) from Product p left join p.brand b")
Page<Prod> pageForProd(Pageable pageRequest);
Besides, it's very troublesome in this way to create another class everytime to map the query results into object(I mean the Prod class). So is there a good way to work with it? Any help would be appreciated.
Instead of p.brand_id = b.id you should do p.brand.id = b.id

Categories