#N+1 Hibernate
137 messages ยท Page 1 of 1 (latest)
โ This post has been reserved for your question.
Hey @vagrant zodiac! Please use
/closeor theClose Postbutton above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
wait
I saw that one
Do you need to access positions of the employees after the findAll()?
nope only when i'm fetching all peoples
not found
You have this in the Employee class:
@OneToMany(mappedBy = "employee")
private List<Position> positions = new ArrayList<>();
it's your Stack Overflow post so only you and reviewers can see it - you need to be logged in to Stack Overflow
(k4rol4)
try using lazy loading there
lol, wife account ๐
so @OneToMany(mappedBy = "employee", fetch = FetchType.LAZY)
actually that should be the default
Is the second SQL statement in the logs occuring multiple times?
๐
if not, where exactly do you have the N+1 problem?
still the same result 2 select Hibernate: select distinct p1_0.id,p1_0.dtype,p1_0.email,p1_0.height,p1_0.name,p1_0.person_number,p1_0.surname,p1_0.type,p1_0.version,p1_0.weight,p1_0.pension_amount,p1_0.worked_years,p1_0.scholarship_amount,p1_0.specialization,p1_0.university_name,p1_0.year_of_study from person p1_0 where 1=1 offset ? rows fetch first ? rows only Hibernate: select p1_0.employee_id,p1_0.id,p1_0.end_date,p1_0.position,p1_0.salary,p1_0.start_date from position p1_0 where p1_0.employee_id=?
2 SELECTs is not an N+1 problem on its own
What exactly is the issue you are facing?
(would be good to add to the SO post as well)
hm, I'v got n+1 problem when i'm going to fetch all people, when I'v got alien, kid, retiree in database everything is ok, but when i'v got employee with position(other entity) i'v got n+1
Are you getting the same SQL statement multiple times?
How exactly do you recognize an N+1 problem here?
And are the repeated queries executed during the findAll() call or are they executed afterwrds
yes, I have only one endpoint to fetch all people - and sorting/filtering,
Can you show the result of that endpoint?
sure
Does the endpoint include positions for employees?
"content": [
{
"id": 1,
"name": "Iza",
"surname": "Izabelowa",
"personNumber": "2434567890",
"weight": 27.0,
"height": 222.0,
"email": "[email protected]",
"type": "Employee",
"version": 0,
"positionCount": 0
},
{
"id": 2,
"name": "Anna",
"surname": "Nowak",
"personNumber": "9876543210",
"weight": 160.0,
"height": 65.0,
"email": "[email protected]",
"type": "Retiree",
"version": 0,
"pensionAmount": 3000.5,
"workedYears": 35
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 40,
"sort": [],
"offset": 0,
"paged": true,
"unpaged": false
},
"last": true,
"totalElements": 2,
"totalPages": 1,
"size": 40,
"number": 0,
"sort": [],
"numberOfElements": 2,
"first": true,
"empty": false
}```
only position count
its about how many positions he have
How are you computing the position count?
in toDto method Employee employee = (Employee) person; employeeDto.setPositionCount(employee.getPositions() != null ? employee.getPositions().size() : 0);
Ok I think the employee.getPositions().size() requests the SELECTs on the positions
In the
@OneToMany(mappedBy = "employee")
private List<Position> positions = new ArrayList<>();
of the Employee class, can you add fetch = FetchType.EAGER? Does that change the SQL statements?
and ideally also try it out with at least 3 employees
w8 ๐
Hibernate: select p1_0.employee_id,p1_0.id,p1_0.end_date,p1_0.position,p1_0.salary,p1_0.start_date from position p1_0 where p1_0.employee_id=?
Hibernate: select p1_0.employee_id,p1_0.id,p1_0.end_date,p1_0.position,p1_0.salary,p1_0.start_date from position p1_0 where p1_0.employee_id=?
Hibernate: select p1_0.employee_id,p1_0.id,p1_0.end_date,p1_0.position,p1_0.salary,p1_0.start_date from position p1_0 where p1_0.employee_id=?
Hibernate: select p1_0.employee_id,p1_0.id,p1_0.end_date,p1_0.position,p1_0.salary,p1_0.start_date from position p1_0 where p1_0.employee_id=?
Hibernate: select p1_0.employee_id,p1_0.id,p1_0.end_date,p1_0.position,p1_0.salary,p1_0.start_date from position p1_0 where p1_0.employee_id=?
Hibernate: select p1_0.employee_id,p1_0.id,p1_0.end_date,p1_0.position,p1_0.salary,p1_0.start_date from position p1_0 where p1_0.employee_id=?```
alternatively you could try annotating positions with
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
as explained in https://stackoverflow.com/a/2913876/10871900
yes, that's how you see the N+1 problem
yeee..
Can you try that?
What fetch type did you use there?
My guess is having one COUNT() statement per element
Hibernate: select count(id) from position where employee_id=?
Hibernate: select count(id) from position where employee_id=?
Hibernate: select count(id) from position where employee_id=?
Hibernate: select count(id) from position where employee_id=?
Hibernate: select count(id) from position where employee_id=?
Hibernate: select count(id) from position where employee_id=?
Hibernate: select count(id) from position where employee_id=?```
ugh, it's crazy
ok that's at least better than before
I guess your wife would be ok with me editing their SO post to include the details you provided and publish it?
hahahahah! Sure!
so it's ok if these are included under the CC-BY-SA license from Stack Overflow?
hm, what is CC-BY-SA license?
I don't know how to reduce that - you can try using views for that
All content on Stack Overflow is under the CC-BY-SA license which allows anyone to irrevokably use it if they state their changes, attribute (cite) it properly and use the same license
License text: https://creativecommons.org/licenses/by-sa/4.0/
Stack Overflow info: https://stackoverflow.com/help/licensing
Hibernate: select distinct p1_0.id,p1_0.dtype,p1_0.email,p1_0.height,p1_0.name,p1_0.person_number,p1_0.surname,p1_0.type,p1_0.version,p1_0.weight,p1_0.pension_amount,p1_0.worked_years,p1_0.scholarship_amount,p1_0.specialization,p1_0.university_name,p1_0.year_of_study from person p1_0 where 1=1 offset ? rows fetch first ? rows only
look, i commented this counting line, and result is ok, now how to count employee position without n+1 problem ๐
You could try to create a view on the person table that includes all fields you need
so kind of a virtual table in the database and an entity for that virtual table but requesting that entity will do a request to the virtual table
my view tables looking like that:
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@SuperBuilder
public abstract class PersonDto {
private int id;
private String name;
private String surname;
private String personNumber;
@NotNull
private double weight;
@NotNull
private double height;
private String email;
private String type;
private Long version;
}@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@ToString
public class EmployeeDto extends PersonDto {
private int positionCount;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class RetireeDto extends PersonDto {
private Double pensionAmount;
private Integer workedYears;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class PositionDto {
private Integer id;
private String position;
private LocalDate startDate;
private LocalDate endDate;
private Double salary;
}
and in that view, it might be possible to change the positions field to an int with the count
but now in EmployeeDto I dont have counting methot how many positions he have
yeah these are the DTOs and not the actual views
intresting how i can do that?
I don't know how to do that with inhertiance to be honest
which is why I think it would be a good SO question
If you are ok with everything from that channel being licensed under that, I can add that to the question (though written in a more compact form but still keeping what the question is asking)
but I think now It's not needed I'm going to delete this post.
that works as well
but I don't know how to use views to do that without running into N+1
I'm looking for best option to count this positions and put to EmployeeDto
I do think it would be a valid SO question but it's your choice if you don't want it
I was able to get you to that point which should be better than before and I think it should be possible to improve it with views but I don't know how
wow
and how would you do that with Spring JPA and entity inheritance as in this case?
or just eager fetching if you will always need the association
let me look at the entity uno momento
eager fetching alone isn't necessarily the issue here
and note that only the COUNT of the positions per employee is of interest (but of all requested employees)
now it's solved we got another questions, like how to count positions and add to EmployeeDto -> without N+1
yep
ok so basically
if you are using eager fetch
you can just stream through the positions
and put them in a dto
then it would request all positions
positions are in a child class of Person and also non-Employee entities are requested
I think I suggested eager fetching for that positions before
like retiree, student, alien, kid
yes
with different params
is this some kind of a project you are doing or is it an exam
project
nope
that still requests all employees which is what we are trying to avoid
why tho
oh where?
eager fetch pulls all the stuff with one request
As I said I could rewrite the SO question to ask for doing the thing with the count properly (or you could rewrite it)
i mean if theres no option to have a query
with counting it
you gotta fetch it
and just count it
@vagrant zodiac can you try what i said
FYI I'm preventing the question from being published in its current state (unless another reviewer overwrites my choice which would be kinda weird)
one of the courses i was on
had a similar thing
but not exactly like that
yes right now i'm trying this
yea it should work properly now
in the employee method you can just do
return positions.size
()
heres a gif so work goes faster
l0l
Before your post will be closed, would you like to express your gratitude to any of the people who helped you? When you're done, click I'm done here. Close this post!.