What is difference between codes?
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(mappedBy = "siteesTypeSite", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
public Set<Sites> getSitees() {
return sitees;
}
and
#OneToMany(mappedBy = "siteesTypeSite", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
public Set<Sites> getSitees() {
return sitees;
}
As for me both of them makes similar result, but second case is more cleaner. If i mistook correct me please.
The main difference between the annotations is that #OneToMany is a pure JPA annotation. Whereas #LazyCollection is Hibernate specific.
So if you want your code to be portable across various JPA providers you should use JPA annotations.
Update
To explain between those two annotation, consider the OneToMany relationship between Department -> Employee
Case 1:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "DEPARTMENT_ID")
private List<Employee> employees = new ArrayList<>();
if you fetch a Department object from the db using :
entityManager.find(Department.class, 1L);
following query gets fired to fetch the data
SELECT department0_.DEPARTMENT_ID AS DEPARTMENT_ID1_0_0_,
department0_.DEPARTMENT_NAME AS DEPARTMENT_NAME2_0_0_,
department0_.LOCATION AS LOCATION3_0_0_,
employees1_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_1_,
employees1_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_1_,
employees1_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_2_,
employees1_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_2_,
employees1_.EMPLOYEE_NAME AS EMPLOYEE_NAME2_1_2_
FROM DEPARTMENT department0_
LEFT OUTER JOIN EMPLOYEE employees1_
ON department0_.DEPARTMENT_ID =employees1_.DEPARTMENT_ID
WHERE department0_.DEPARTMENT_ID=?
so it means it will fetch all the data in a single query at once.
Case 2:
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(name = "DEPARTMENT_ID")
private List<Employee> employees = new ArrayList<>();
similary if you fetch a Department object from the db using :
entityManager.find(Department.class, 1L);
following queries gets fired to fetch the data :
SELECT department0_.DEPARTMENT_ID AS DEPARTMENT_ID1_0_0_,
department0_.DEPARTMENT_NAME AS DEPARTMENT_NAME2_0_0_,
department0_.LOCATION AS LOCATION3_0_0_
FROM DEPARTMENT department0_
WHERE department0_.DEPARTMENT_ID=?
SELECT employees0_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_0_,
employees0_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_0_,
employees0_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_1_,
employees0_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_1_,
employees0_.EMPLOYEE_NAME AS EMPLOYEE_NAME2_1_1_
FROM EMPLOYEE employees0_
WHERE employees0_.DEPARTMENT_ID=?
So to summarize, in first case since the FetchType is EAGER Employees are fetched eagerly along with Department in a single JOIN query.
And,
In second case, Employees are fetched with Department but since the FetchType is LAZY a seperate query will be fired to fetch Employees. And if you remove #LazyCollection(LazyCollectionOption.FALSE) Employees wont be fetched at all until you access Employees on Department instance.
Related
I have major performance issues when I try to map an entity into a response.
This is the entity:
#Entity
#Table(name = "MyEntity")
public class MyEntity extends BaseEntity {
#Column(name = "someOtherId", nullable = false)
private String someOtherId;
#ElementCollection
#CollectionTable(name = "Phones", joinColumns = #JoinColumn(name = "myEntityId"))
#Column(name = "phone")
private List<String> phones; // <------- we care about this
#ElementCollection
#CollectionTable(name = "Websites", joinColumns = #JoinColumn(name = "myEntityId"))
#Column(name = "websites")
private List<String> websites; // <------- we care about this
#Fetch(FetchMode.SUBSELECT)
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "myEntity")
private List<ContactEntity> bbb;
#Fetch(FetchMode.SUBSELECT)
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "myEntity")
private List<AddressEntity> ccc;
}
This is how I use the DAL to fetch it:
List<MyEntity> findByTenantIdAndIdIn(String someOtherId, Set<String> MyEntityIds);
Now when I iterate over List<MyEntity> to map it, and call myEntity.getPhones(), I see that a DB call is being made, which is what causes the slowdown, a 70 seconds slowdown on 1000 entities.
So what can I do to force it to join in the first query it did when I called findByTenantIdAndIdIn?
Notes:
Phones is a simple table with columns: [myEntityId, phone]
The same problem happens with Websites
This has nothing to do with it being an #ElementCollection. Like you figured out, you can use subselect fetching or could also use a batch size for select fetching(the default strategy). Another possibility is to use a fetch join in the query, but be careful when fetch joining multiple collections as that might create a cartesian product which leads to a performance problem cause by too many rows being transfered. A fetch join example HQL query looks like this:
SELECT e FROM MyEntity e LEFT JOIN FETCH e.phones LEFT JOIN FETCH e.websites
I solved it right after I posted this.
I annotated phones and websites with #Fetch(FetchMode.SUBSELECT) which creates a parallel subquery.
Another way to solve this is simply to not use #ElementCollection because it has bad performance, use an Entity instead as they recommend in the video here: https://thorben-janssen.com/hibernate-tips-query-elementcollection/
I have two entities:
User
Work
they have many-to-many relationship with each-over.
#ManyToMany(
fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
#JoinTable(
name = "user_work",
joinColumns = #JoinColumn(name = "work_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
private List<User> workUsers;
when i add new work using JPA :
List<UserDto> users = new ArrayList<>();
users.add(new User(something something));
WorkDto work1 = new WorkDto(1, users));
workRepository.save(workMapper.fromDto(work1));
problem : when i save my work entity in the database it does not save user, so when i extract it , work says that is has no users.
How can i insert work into database and add users to it as well? I have mutual table with work_id and user_id of course
Are you executing it within a transaction with #Transactional? Have you #Override the equals method?
More detail is needed like the relationship in the User class
We are using Spring Data repositories with Hibernate 5.x
We have a entity graph with a deep hierarchy.
The mapping looks like this:
#Entity
public class FooBar {
#OneToMany(mappedBy = "fooBar", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
...
}
#Entity
public class Foo {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "foobar_id")
private FooBar fooBar;
#OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Bar> chassis = new HashSet<>(0);
...
}
#Entity
public class Bar {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "foo_id")
private FooBar foo;
...
}
As you can see the FooBar entity has a set of Foo entities. Each Foo entity contains more Bar entities and so on.
We use the Fetchgraph feature to load the FooBar entity with the relations we need during runtime to avoid n+1 query issue when fetching lazy associations.
After the service call to load the entity graph the transaction has ended and the entity is detached.
When calling save on the FooBar entity at a later time, this causes multiple select statements. Each fetching one of the child entities.
I know that this comes from the entitymanager merge() call which fetches the object graph from the db before copying state changes from the detached objects.
I have two questions:
Why is hibernate not able to join these statements to one big select like what happens when using the fetchgraph?
When i remove all cascade options from the relations it still causes multiple selects but only attributes of the top, FooBar entity, will be updated. Why is hibernate still fetching all loaded child entites during merge even with no cascade merge?
Thanks
You can use session.update instead of merge to overcome this issue.
Session session = entityManager.unwrap(Session.class);
for (Post post: posts) {
session.update(post);
}
I have similar issue with your case, and the reason is the setting of cascading CascadeType.ALL on the #OneToMany association. Updating and merging the parent entity cause a lot of select on the child association.
#Entity
public class FooBar {
#OneToMany(mappedBy = "fooBar", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
...
}
I fix my case by reducing the scope of cascading, only PERSIST and REMOVE is sufficient
#OneToMany(mappedBy = "fooBar", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
I have entity ServiceConfig with to OneToMany relationship:
#OneToMany(mappedBy = "serviceConfig", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<NotificationItem> notificationItems;
#OneToMany(mappedBy = "serviceConfig", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<QosItem> qosItems;
And in each child class, I have:
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "TD_SERVICE_CONFIG_ID")
private ServiceConfig serviceConfig;
When I fetch new ServiceConfig - I have 3 tables, ServiceConfig , NotificationItem and QosItem. In NotificationItem and QosItem tables, I have empty column TD_SERVICE_CONFIG_ID but all data inserted.
How can I config hibernate(annotations or another configs) for successful insert parent id to TD_SERVICE_CONFIG_ID column of child?
Your configuration looks fine.
I suspect (although obviously can't tell for sure without seeing your code) that you are not setting the inverse side of the relationship, which is required.
You would add a NotificationItem as follows:
NotificationItem item = new NotificationItem();
item.setServiceConfig(service); //set the other side of the bi-directional relationship
servive.getNotificationItems().add(item);
The middle line is the important one here.
I'm working on some personal project but i have a question about hibernate.
I have a class structure like this :
#Entity
public class User {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fkIdCompanyUser")
private Company company = new Company();
}
But inside the company i have another join.
#Entity
public class Company {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fkIdCompanyEstimateOption")
private EstimateOptions estimateOptions = new EstimateOptions();
}
Now i do a query to get the estimate options.
But if i do it like this it loads lots of unnecessary stuff .
#RequestMapping(value = "/estimateoptions")
public EstimateOptions getCompanyEstimateOptions(#AuthenticationPrincipal Principal user) {
User getuser = userDao.findByEmail(user.getName());
EstimateOptions estimateOptions = getuser.getCompany().getEstimateOptions();
return estimateOptions;
}
is there a better approach for this ?
There are a lot of ways to do such optimization. The simplest one is add bidirectional associations to Company and EstimateOptions with lazy loading.
An example for Company ( I don't test. It is just a sketch.)
#Entity
public class Company {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fkIdCompanyEstimateOption")
private EstimateOptions estimateOptions = new EstimateOptions();
#OneToOne(mappedBy="company", fetch = FetchType.LAZY)
private User user;
}
And do something like this (this is HQL but you can use a criteria API too)
from EstimateOptions options inner join options.company company inner join company.user user where user.name = :userName
You can see HQL joined query to eager fetch a large number of relationships for additional thoughts.
Updated
I am not sure but may be you can do something like this (without additional associations)
select options from User user inner join user.company company inner join company.estimateOptions options where user.name = :userName