Fetch Results from All tables in SpringBoot JPA Repository - java

I am trying to fetch data from all below tables using JOIN query for particular productType and Sizes (column in two diff tables).
I am unable to get the desired result so some guidance would be very helpful.
Please find the details below what I have tried till now.
PK: Primary Key, FK: Foreign Key.
Following is the table structure with tables(mentioned) below has OneToOne mapping to another table.
**MC_Product_Type**:
prod_type_id (PK),
prod_type,
description
|
|OnetoOne
|
**MC_Set_Rules**:
set_id (PK),
prod_type_id (FK),
set_name,
set_type,
condition
|
|OneToOne
|
**MC_Size_Rules**:
prod_rule_id (PK),
prod_type,
Sizes,
set_id (FK),
min_qty,
dimension
|
|OneToOne
|
**MC_Product_Rules**:
prod_rule_id (FK),
prod_type,
allowed_type,
availability,
prod_label,
locations
Entity Classes:
#Table(name = "MC_Product_Type")
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Getter
#Setter
#ApiModel
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ProductType {
#Id
private int prodTypeId;
private String prodType;
private String description;
#OneToOne
#JoinColumn(name="prodTypeId", referencedColumnName="prodTypeId", insertable=false, updatable=false)
private SetRules setRules;
}
#Table(name = "MC_Set_Rules")
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Getter
#Setter
public class SetRules {
#Id
private int setId;
private int prodTypeId;
private String setName;
private String setType;
private String condition;
#OneToOne
#JoinColumn(name="setId", referencedColumnName="setId", insertable=false, updatable=false)
private SizeRulesEntity sizeRules;
}
#Table(name = "MC_Size_Rules")
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Getter
#Setter
public class SizeRulesEntity {
#Id
private int prodRuleId;
private String prodType;
private String sizes;
private int setId;
private int minQty;
private String dimension;
#OneToOne
#JoinColumn(name="prodRuleId", referencedColumnName="prodRuleId", insertable=false, updatable=false)
private ProductRules productRules;
}
#Table(name = "MC_Product_Rules")
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Getter
#Setter
public class ProductRules {
#Id
private int prodRuleId;
private String prodType;
private String allowedType;
private String availability;
private String prodLabel;
private String locations;
}.
Repository:
Value for prod_type = "ELLACUST"
Value for Sizes = "XS","S","M","L","XL"
I used these below queries(one by one) , based on prod_type and Size I need to get data from all the tables.
Note: Sizes is not part of table MC_Product_Type. Data team has informed if they include sizes in this table it will be a redundant data. so sizes is part of MC_Size_Rules table.
I am trying to pass prod_type and sizes dynamically in first two query but getting results only for size 'XS' which is value for 1st row in table MC_Size_Rules even though if I pass other sizes values.
In third query, I am getting exception saying sizes is not part of ProductType.
I need help on what should I change in entity classes, mappings or in query to get data from all the tables when prod_type and correct sizes is passed.
public interface ProductRulesRepository extends JpaRepository <ProductType, String> {
#Query("SELECT pt FROM ProductType pt JOIN pt.setRules s ON pt.prodTypeId = s.prodTypeId JOIN s.sizeRules sr ON s.setId = sr.setId JOIN sr.productRules pr ON sr.prodRuleId = pr.prodRuleId where pt.prodType = :prodType AND pt.setRules.sizeRules.sizes = :sizes")
ProductType findAllByProdTypeAndSizes(String prodType, String sizes);
#Query("SELECT pt FROM ProductType pt JOIN pt.setRules s ON pt.prodTypeId = s.prodTypeId JOIN s.sizeRules sr ON s.setId = sr.setId JOIN sr.productRules pr ON sr.prodRuleId = pr.prodRuleId where pt.prodType = ?1 AND pt.setRules.sizeRules.sizes = ?2"))
#Query("SELECT pt FROM ProductType pt JOIN pt.setRules s ON pt.prodTypeId = s.prodTypeId JOIN s.sizeRules sr ON s.setId = sr.setId JOIN sr.productRules pr ON sr.prodRuleId = pr.prodRuleId")
ProductType findAllByProdTypeAndSizes(String prodType, String sizes);
exception : sizes is not defined in ProductType

First of all you are misusing #Query annotation your variable interpolation is wrong cf "?2 you should use it like this
#Query("select from User u where u.id = :id")
findUserById(#Param("id") String id)
Second: the return type of your methods are wrong, you are selecting multiple rows so your retrun type should be a collection
third: Spring provides you with a feature called Spring named queries. It allows spring guessing the associated sql from the method's name
and finally you can try something like this
Collection<ProductType> findByProdTypeAndSizeRulesSizes(String prodType, String sizes);
hibernate orm will handle fetches for you the associated data in the related objects (#OneToOne relations)

Related

Springboot jpa : Entity can't bind data from custom query that's not in table column

I use java Springboot JPA, mysql.
I need to get data from my custom query and some of the data (nextBno, beforeBno, etc) are not DB table column. They are from my query like below:
(
SELECT boardNo FROM table_news
WHERE boardNo IN (
SELECT MIN(boardNo)
FROM table_news A
WHERE status=0 AND boardNo > 65
)
) AS nextBNo,
So when I fetch data from the query, data fetch well because query itself is fine. but result is is filled as null.
I think I should fix something from JPA Model(Entity) file so that the data can bind well but don't know exactly how.
I tried to add #Transient annotation but it doesn't seem to work.
NewsModel.java
#Data
#Entity
#Table(name = "table_news")
#NoArgsConstructor
#AllArgsConstructor
#DynamicInsert
#DynamicUpdate
public class NewsModel {
#Id
#Column(name = "boardNo")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer boardNo;
(...)
#Transient
private Integer nextBNo;
#Transient
private Integer beforeBNo;
(...)
}
NewsResponse.java
#Data
#NoArgsConstructor
#AllArgsConstructor
public class NewsResponse {
private Integer boardNo;
(...)
#Transient
private Integer nextBNo;
#Transient
private Integer beforeBNo;
(...)
public NewsResponse(NewsModel model){
this.boardNo = model.getBoardNo();
(...)
this.nextBNo = model.getNextBNo();
this.beforeBNo = model.getBeforeBNo();
}
NewsRepository.java -- interface
#Repository
public interface NewsRepository extends JpaRepository<NewsModel, Integer> {
#Query(value = "(MY CUSTOM QUERY)", nativeQuery = true)
NewsModel getNewsWithNextAndBefore(Integer boardNo);
}
Have you tried mentioning the "Schema" name under the Entity class? Maybe table is not getting mapped properly, that could be a problem.

How to join 4 tables in 1 DTO with Jpa Query

So I have 4 tables
An Employer
#Entity
#EqualsAndHashCode(callSuper = false)
#Table(name = "employers")
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "jobPostings"})
#PrimaryKeyJoinColumn(name="employer_id", referencedColumnName = "id")
public class Employer extends User {
#Column(name = "company_name")
private String companyName;
#Column(name = "website")
private String website;
#Column(name = "phone_number")
private String phoneNumber;
#OneToMany(mappedBy="employer")
private List<JobPosting> jobPostings;
}
A City
#Data
#Entity
#Table(name="cities")
#AllArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "jobPostings"})
public class City {
#Id
#Column(name="id")
private int id;
#Column(name="city_name")
private String cityName;
#OneToMany(mappedBy="city")
private List<JobPosting> jobPostings;
}
A Job Position
#AllArgsConstructor
#Data
#Entity
#Table(name="job_positions")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "jobPostings"})
public class JobPosition {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="job_position_id")
private int id;
#Column(name="title")
private String title;
#OneToMany(mappedBy="jobPosition")
private List<JobPosting> jobPostings;
}
And A JobPosting(like a job advertisement)
#Entity
#Table(name="job_postings")
#Data
#NoArgsConstructor
#AllArgsConstructor
//#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "city", "jobPosition","employer"})
public class JobPosting {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="job_requirements")
private String jobRequirements;
#Column(name="salary_min")
private int salaryMin;
#Column(name="salary_max")
private int salaryMax;
#Column(name="application_deadline")
private LocalDate applicationDeadline;
#Column(name="number_of_openings")
private int numberOfOpenings;
#Column(name="stream_date")
private LocalDate streamDate;
#ManyToOne()
#JoinColumn(name="city_id")
private City city;
#ManyToOne()
#JoinColumn(name="job_position_id")
private JobPosition jobPosition;
#ManyToOne()
#JoinColumn(name= "employer_id")
private Employer employer;
}
I have implemented the necessary mapping for all of them and it is working fine.
However, I want to Join them in a DTO like:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class JobPostingWithJobPositionCityEmployerDto {
private int id;
private String jobRequirements;
private int salaryMin;
private int salaryMax;
private LocalDate applicationDeadline;
private int numberOfOpenings;
private LocalDate streamDate;
private String cityName;
private String title;
private String companyName;
}
to get the fields I want in a more clean way, I am trying to use #Query annotation of springframework.jpa but I can not quite manage it since I just learned about this, the query I am using is:
#Query(value ="Select new kodlamaio.hrms.entities.dtos.JobPostingWithJobPositionCityEmployerDto"
+ "(j.id, j.jobRequirements, j.salaryMin, j.salaryMax, j.numberOfOpenings, j.streamDate, j.applicationDeadline, c.cityName, p.title, e.companyName)"
+ " From Employer e Inner Join e.jobPostings j, "
+ "From City c Inner Join c.jobPostings j, "
+ "From JobPosition p Inner Join p.jobPostings j", nativeQuery = true)
List<JobPostingWithJobPositionCityEmployerDto> getJobPostings();
I dont even know if this is the correct way to do this, I keep getting syntax error, I looked up answers but couldnt quite grasp what they were saying, there were a lot of different scenarios.
So if anybody could help me with this Query and recommend some sources to learn about different commands, I would appreciate it so much, Thanks in advance.
Mapping the result of a query with a DTO using the new keyword in a query only works for JPQL, it will not work for SQL (which is what you are using).
It also looks to me if you are trying to write a too complex query as everything can be achieved/reached through the JobPosting class, which will implicitly do the join when using JPQL.
So instead of your native SQL writing a JPQL should fix it.
Something like
#Query(value ="Select new kodlamaio.hrms.entities.dtos.JobPostingWithJobPositionCityEmployerDto"
+ "(jp.id, jp.jobRequirements, jp.salaryMin, jp.salaryMax, jp.numberOfOpenings, jp.streamDate, jp.applicationDeadline, jp.city.cityName, jp.jobPosition.title, jp. employer.companyName)"
+ " From JobPosting jp)
Which should do the trick. Your JPA provider should be smart enough to figure out what to join and retrieve.
Generaly this is named Projection, I believe that you have already created JobPostingWithJobPositionCityEmployerDto with a constructor of 10 args, with their respective data type of course. And since it is custom to jpa you can't use nativeQuery = true. These modifications should be fine.
#vlad-mihalcea to the rescue
https://vladmihalcea.com/the-best-way-to-map-a-projection-query-to-a-dto-with-jpa-and-hibernate/

How to ignore some column when join tables in Hibernate?

Hello This is my 2 tables:
record and submission.
In submission, it has 1 composite primary key:(submission_id, question_id). One submission number can have several questions number. For example:
And as for record, it has a composite primary key:(student_id, exam_id). It looks like this:
I want to join these 2 tables like MySQL:
select * from record
left join submission
on record.submission_id = submission.submission_id.
But in hibernate, I have successfully join these 2 tables, but it gives me the following hql:
Hibernate:
select
...all columns...
from
record record0_
inner join
submission submission1_
on record0_.submission_id=submission1_.submission_id
and record0_.question_id=submission1_.question_id
where
1=1
In this case, I will get 0 rows in the result.
I don't want it use "and record0_.question_id=submission1_.question_id" after on clause, because there is no question_id in my record table.
But I have to add all primary keys into the #joinColumns() when I add Submission attribute in Record class, like this:
// Record class
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "record")
public class Record implements java.io.Serializable{
private static final long serialVersionUID = 1L;
// Other columns I don't need to show
#Column(name = "submission_id")
private Integer submissionId;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "submission_id", referencedColumnName = "submission_id",insertable=false, updatable=false),
#JoinColumn(name = "question_id", referencedColumnName = "question_id",insertable=false, updatable=false)
})
private Submission submission;
}
My Submission class like this:
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "submission")
public class Submission implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "submission_id")
private Integer submissionId;
#Id
#Column(name = "question_id")
private Integer questionId;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "submission")
private Record record;
}
Anyone can give me some advice?
-------- How I combine these tables-------
Actually, I join 4 tables and all these joins have the same problem declared above.
Code below is how i combine these 4 tables (record, submission, question, optional)
#Override
public List<RcdSubQuesOpt> getRcdSubQuesOpt(int studentID, int examId) {
Session session = this.getSession();
// RcdSubQuesOpt --> this is a class to store attributes from different tables(classes)
List<RcdSubQuesOpt> results;
Transaction transaction = null;
transaction = session.beginTransaction();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<RcdSubQuesOpt> criteriaQuery = criteriaBuilder.createQuery(RcdSubQuesOpt.class);
// To combine these tables use join
Root<Record> pRoot = criteriaQuery.from(Record.class);
Join<Record, Submission> rcd2sub = pRoot.join(Record_.submission);
Join<Submission, Question> sub2que = rcd2sub.join(Submission_.question);
Join<Question, Optional> que2opt = sub2que.join(Question_.optional);
// Attributes in RcdSubQuesOpt class
// get these columns from result and assign them to RcdSubQuesOpt class
criteriaQuery.multiselect(
pRoot.get("studentId"),
pRoot.get("examId"),
rcd2sub.get("questionId"),
rcd2sub.get("stuAnswer"),
sub2que.get("content"),
que2opt.get("content"),
que2opt.get("answer"));
// Predicate predicate = pRoot.get("examId").equals(1);
criteriaQuery.where();
results = session.createQuery(criteriaQuery).getResultList();
transaction.commit();
return results;
}
You haven't mentioned how you retrieve that data using hibernate. Have you tried trying to use #Query (select r from Record left join Submission sub on r.submissionId = sub.id where ...") ?
you have defined a #OneToOne relation in your record class. Apparantly thats wrong, since there exists more then one entry in your submission table for one record. So change this to #OneToMany and the respective relation in the submission class to #ManyToOne.
Besides your entities are not well named and mapped. Submission is in fact more of a question or an answer to it, because a line in that table does not represent one submission, which would be the expected meaning.

How to implement ligh Entity version with Jpa repository?

Have a "full Entity" class:
#Entity(name = "vacancy_dec_to_words")
public class VacancyDescriptionToWords {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#JoinColumn(name = "vacancy_description_id")
#ManyToOne(cascade = CascadeType.ALL)
private VacancyDescription vacancyDescription;
#JoinColumn(name = "words_id")
#ManyToOne
private Words words;
#Column(name = "qty")
private int qty;
#Column(name = "create_date")
private Date date;
//...getters and setters
In some methods I need use only 2 column from this database: word_id and qty
I try the follow way:
Projections
https://docs.spring.io/spring-data/jpa/docs/2.1.2.RELEASE/reference/html/#projections
public interface QtyWords {
Long getWords();
Integer getQty();
}
JpaReposytory:
*Query, that I use tested and it workable, I use him in JpaRepository:
#Repository
public interface SmallVDTWRepository extends JpaRepository<VacancyDescriptionToWords, Long> {
#Query(nativeQuery = true,
value = "SELECT sum(qty), words_id FROM vacancy_desc_to_words WHERE vacancy_description_id IN (" +
"SELECT id FROM vacancy_description WHERE vacancy_id IN (" +
"SELECT id FROM vacancy WHERE explorer_id = :exp))" +
"GROUP BY words_id")
List<QtyWords> getDistinctWordsByExplorer(#Param("exp") long exp);
}
But I get some interesting result when I get list of entities:
List<QtyWords> list = vdtwService.getByExplorerId(72);
I am not get any exceptions, but I have the list with are unknowns objects. This objects contains my data, which I need(qty and words_id), but I cannot get them from him.
Can I use this method (Projection) to implement this task and, in general, how to correctly implement the 'Light Entity' in this case?
Spring provides two mechanisms that can be used to limit data to be fetched.
Projections
Projections can help you to reduce data, retrieved from database, by setting what exactly attributes you want to fetch.
Example:
#Entity
class Person {
#Id UUID id;
String firstname, lastname;
#OneToOne
Address address;
}
#Entity
static class Address {
#Id UUID id;
String zipCode, city, street;
}
interface NamesOnly {
String getFirstname();
String getLastname();
}
#Repository
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(String lastname);
}
Entity graph
Annotation EntityGraph can help you to reduce amount of queries to database, by setting what exactly related entities you need to fetch.
Example:
#Entity
#NamedEntityGraph(name = "GroupInfo.detail", attributeNodes = #NamedAttributeNode("members"))
public class GroupInfo {
#Id UUID id;
#ManyToMany //default fetch mode is lazy.
List<GroupMember> members = new ArrayList<GroupMember>();
}
#Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
#EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
GroupInfo getByGroupName(String name); //Despite of GroupInfo.members has FetchType = LAZY, it will be fetched because of using EntityGraph
}
There are two types of EntityGraph:
EntityGraphType.LOAD - is used to specify an entity graph, attributes that are specified by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are treated according to their specified or default FetchType.
EntityGraphType.FETCH - is used to specify an entity graph, attributes that are specified by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are treated as FetchType.LAZY.
PS: Also remember that you can set lazy fetch type: #ManyToOne(fetch = FetchType.LAZY) and JPA will not fetching child entities when parent is being fetched.

org.springframework.orm.jpa.JpaSystemException: ERROR: missing FROM-clause entry for table "attributeid"

Query to fetch data:
JPA not able to read attributeId table.
-- select query to fetch data
select r,a from data r ,
Attributes a
where a.attributeId.type != 'test'
and r.typeid = a.attributeId.typeid
and r.deviceid=:deviceid order by r.typeid;
-- table1
#Entity
#Table(name = "data")
public class data {
#Id
#Column(name = "typeid")
private Integer typeid;
--- table 2
#Entity
#Table(name = "attributes")
public class Attributes implements Serializable {
#EmbeddedId
private Attributeid attributeId;
#Column
private String value;
-- Class with composite keys
#Embeddable
public class Attributeid implements Serializable {
#Column
private Integer typeid;
#Column
private String type;
#Column
private String attributename;
Your query is wrong. It's JPQL not SQL so you have to join with on clause.
It should be
select r, a from data r join Attributes a on r.typeid = a.attributeId.typeid
where a.attributeId.type != 'test'
and r.deviceid=:deviceid order by r.typeid;
Is the relationship to Attributes oneToOne or onToMany?
The question is why you don't map the relationship but the only the attributes?

Categories

Resources