Issue
I will start with the result. In the next image I am displaying the result content of company.userAccount. In the database I have only two companies assigned to the same user and 7 services attached to one of the companies.
@Entity
@Table(name = "user_account")
@Data
@Introspected
@JsonIgnoreProperties(value = {"id"})
public class UserAccount {
...
@OneToMany(mappedBy = "userAccount", fetch = FetchType.EAGER)
private List<Company> companies = new ArrayList<>();
}
@Entity
@Table(name = "company")
@Data
@Introspected
@ToString(exclude = {"userAccount"})
@JsonIgnoreProperties(value = {"id", "userAccount"})
public class Company {
...
@ManyToOne(optional = false)
@JoinColumn(name = "user_account_id", updatable = false, nullable = false)
private UserAccount userAccount;
}
@Entity
@Table(name = "service")
@Data
@Introspected
@ToString(exclude = {"company"})
@JsonIgnoreProperties(value= {"id", "company"})
public class Service {
...
@ManyToOne(optional = false)
@JoinColumn(name = "company_id", updatable = false, nullable = false)
private Company company;
}
@Transactional
public Service saveService(@Valid @Uuid UUID companyUuid, SaveServiceCommand command) {
Company company = companyRepository.getCompanyByUuid(companyUuid).orElseThrow(() -> new NoSuchElementException("unrecognized company"));
Service service = new Service();
service.setCompany(company);
service.setName(command.getName());
service.setUuid(UUID.randomUUID().toString());
return companyRepository.saveCompanyService(service);
}
@Transactional
public Service saveCompanyService(@Valid Service service) {
entityManager.persist(service);
return service;
}
The generated query (simplified) is
select * from user_account a left outer join company c on a.id=c.user_account_id left outer join service s on c.id=s.company_id;
This will generate
Expected result: I was expecting in the UserAccount to see only 2 companies and in one of the companies to have 6 services. Why do I have this result? Why is the same company object multiple times in the list? How can I avoid this?
I believe that one solution would be to change the fetching type of the @OneToMany relationships to LAZY (as they are default) (already tested this solution and it works) but what if I need this type of scenario?
Solution
Yes, there are 2 companies, just like you said. You can notice, that all objects are the same (hashes match). The reason behind this is that the query generates 6 rows for one company and PersistenceBag
used by hibernate does not remove duplicates by default. Passing distinct
to your sql would not work, because there is only one parent entity.
But this can be achieved by using hibernate's query hints. Passing QueryHints.HINT_PASS_DISTINCT_THROUGH
to your Query
simply removes duplicated children.
For more information you can refer to this source.
Answered By - Andronicus