I'm still new to JPA and Hibernate and I get following warnings:
WARN [org.hibernate.engine.loading.internal.LoadContexts] (default task-4) HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext#50dab521<rs=com.mysql.jdbc.JDBC42ResultSet#44f85804>
WARN [org.hibernate.engine.loading.internal.CollectionLoadContext] (default task-4) HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [3] entries
The thing is: I have different users, and for some of them (with very small data), I am able log in, but for the others (with a bit more data, but still small) I am not and then the warnings come instead. My debug logs show, that when the user should be loaded, it's null. (google says, it might come from my entity relationships and the fetch types and modes). Hope someone can help me, I have no idea so far.
The following shows my two main entities and the method, which is invoked after subit login button in the view.
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
#Entity
public class UserProfile extends StringIdEntity implements Serializable {
private Address address;
private String fname;
private String lname;
#OneToOne(cascade = {CascadeType.ALL}, fetch= FetchType.EAGER)
private PhysicalProfile physicalProfile;
#OneToOne(cascade = {CascadeType.ALL}, fetch= FetchType.EAGER)
private DietPlan dietPlan;
#OneToMany(mappedBy = "user", fetch= FetchType.EAGER, cascade = CascadeType.ALL)
private List<DiaryPage> diaryPages;
#OneToMany(mappedBy = "user",fetch= FetchType.LAZY, cascade = CascadeType.ALL)
private List<Meal> meals;
#OneToMany(mappedBy = "user",fetch= FetchType.LAZY, cascade = CascadeType.ALL)
private List<Invoice> invoices;
#OneToMany(mappedBy = "user",fetch= FetchType.EAGER, orphanRemoval = true)
#Fetch(value = FetchMode.SUBSELECT)
private List<GroceryOrder> orders;
#Entity #IdClass(DiaryPageID.class)
public class DiaryPage implements Comparable{ //extends StringIdEntity {
#Id #ManyToOne
private UserProfile user;
#Id #Temporal(TemporalType.DATE)
private Date date;
private KcalRevenue kcalRevenue;
#OneToMany(mappedBy = "diaryPage", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private List<Meal> meals;
#OneToMany(mappedBy = "page", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private List<Grocery> groceries;
#OneToMany(mappedBy = "diaryPage", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private List<Workout> workouts;
#OneToMany(mappedBy = "diaryPage", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private List<Activity> activities;
public UserProfile checkUserLogin(String email, String password) {
UserProfile user = em.find(UserProfile.class, email);
if(user != null) {
try {
if(EntityUtils.hashPassword(password, user.getSalt(), UserProfile.HASH_ALGORITHM).equals(user.getPassword())) {
return user;
} else return null;
} catch (EntityUtils.EntityUtilException e) {
throw new RuntimeException("Passwort konnte nicht gehashed werden.");
}
} else return null;
}
I solved the problem this way:
I changed all collections of entity diaryPage and the orders collection from entity userProfile to be fetched lazily. Probably in the beginning it was okay with less data, but now it's too much to be fetched eagerly in subselects.
Because: UserProfile -> n diaryPages -> each page hast 4 collections
You get the same error, when you load existing data that uses an enum value you've removed in your code. E.g. if you previously had your enum defined as:
public enum SomeType {
A,
B,
C
}
Then having stored entities such that there are entries with each enum.
If you then say change your enum to:
public enum SomeType {
A,
B,
D
}
Now when you load your existing persisted entities, this same error is thrown because it tries to instantiate your entity with enum value C which is no longer valid.
Sadly this is not clearly communicated in the logs when this is the cause.
If you put a break point in your debugger at org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing (from spring-tx version 5.3.14 / spring boot version 2.6.2)
You'll see that the throwable param contains a message such as this: No enum constant <your enum>.OLD_VALUE
Related
I know this question has been asked many times but none of the solution is working for me.
So I have a Parent class :
class User{
#Id
#NotNull
#Column(name = "`UserId`", nullable = false)
private Long userId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "`UserId`")
private Set<Phone> phoneList;
}
And a child class:
class Phone {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "`UserId`")
private User user;
}
Now when I received a update for User class with new phone list, I want to remove all the old phones and add new phones. Please note that this all operation is happening in same #Transactional.
Solution I tried:
user.getPhoneList().clear()
user.getPhoneList().addAll(new phone list)
When I try the above logic, Hibernate is trying to set old phone with userId as null. At this position I am getting DataIntegrityViolation as userId in Phone table is non null column.
Please provide any appropriate solution which can work here.
Hhhmmm... I have the exact same logic and it works fine by me. Here are my classes
#Data
#Entity
public class ProductReference {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToMany(mappedBy = "productReference", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, orphanRemoval = true)
private Set<Attribute> attributes = new HashSet<>();
}
The only difference I see is the CascadeType.REMOVE
#Data
#Entity
public class Attribute {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne
private ProductReference productReference;
}
My deletion:
productReference.getAttributes().clear();
Which Hibernate version you have? by me it is org.hibernate.Version - HHH000412: Hibernate Core {5.4.10.Final}
I have a set entities such as:
public class EntityToDrop {
#NaturalId
#ManyToOne(fetch = FetchType.EAGER)
private ParentEntity parentEntity;
#OneToMany(mappedBy = "entityToDrop", cascade = {CascadeType.REMOVE, CascadeType.REFRESH}, orphanRemoval = true)
private Set<OtherEntity> otherEntities= new HashSet<>();
[...] (other fields and relationships)
}
public class ParentEntity {
#OneToMany(mappedBy = "parentEntity", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<EntityToDrop> childEntities = new HashSet<>();
[...]
}
public class OtherEntity{
#ManyToOne(optional = true,fetch = FetchType.EAGER)
#NaturalId
private EntityToDrop entityToDrop;
[...]
}
In some part of the code I'm calling
parentEntityInstance.getChildEntities().remove(entityToDrop);
[...]
entityManager.persist(parentEntityInstance);
Which I expect should always remove entityToDrop from the database, since it's marked with orphanRemoval. However, when the relationship with OtherEntity exists it's not getting removed, and more interestingly, if I remove the relationship with OtherEntity then try to remove it from the ParentEntity in the same transaction, it's not getting dropped either.
Is there any case where orphanRemoval = true will not work as I expect it to? And as a followup how would I figure out what's stopping my entity from being removed in this case?
I have an issue with a delete to a Many side of a ManyToOne relationship. I've already removed all CascadeTypes from the relationship but the issue still remains. The entry won't be removed (only selects are executed and no delete query). I'm trying to delete it through a CRUD repository call to delete. It calls the method and executes successfully but nothing happens.
The relationship goes as follows: an Activity has an assigned Course, a course can have many activities assigned to it. An Activity has a specific ActivityType.
The classes are as below.
Activity
public class Activity implements Item, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#ManyToOne
#JoinColumn(name = "type_id", nullable = false)
private ActivityType type;
#ManyToOne
#JoinColumn(name = "course_id", nullable = false)
#JsonSerialize(using = CustomCourseSerializer.class)
private Course course;
...
}
Course
public class Course implements Item, Serializable {
...
#OneToMany(mappedBy = "course", fetch = FetchType.EAGER, targetEntity = Activity.class) //cascade = { CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH, CascadeType.REMOVE}
#Fetch(value = FetchMode.SUBSELECT)
private List<Activity> activities;
...
}
Activity Type (has no reference to Activity)
public class ActivityType implements Item, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", nullable = false, unique = true)
private String name;
...
}
Any ideas how can I solve this issue or at least debug it? Thank you.
Add orphanRemoval = true attribute in the #OneToMany annotation in your Course entity.
public class Course implements Item, Serializable {
...
#OneToMany(mappedBy = "course", fetch = FetchType.EAGER, targetEntity = Activity.class, orphanRemoval = true, cascade = CascadeType.REMOVE )
#Fetch(value = FetchMode.SUBSELECT)
private List<Activity> activities;
...
}
Try to delete reference to Activity from Course. It seems unnecessary to me
I have three separate entities in my Spring JPA application - User, Department, Role
I have a single join table in my database to relate each of these Entities: USER_DEPARTMENT_ROLE
My question is, how can I define this relation in my entity classes? Do I have to define a #ManyToMany relationship in each of the separate entities? I know how to define this relationship between two tables, but for more than two I'm not sure where to start.
Any help is appreciated!
If you have more than two relations mapped in your join table then i would suggest creating a separate entity which would be used for mapping that particular table.
The question is whether you can have a distinct id column which would serve as an artificial primary key or you have to stick with the composite primary key build from the three foreign keys.
If you can add that artificial id (which is the modern way of designing your database) then your mapping should look something like the following:
Option 1
class User {
#OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Department{
#OneToMany(mappedBy = "department", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Role{
#OneToMany(mappedBy = "role", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class UserDepartmentRoleLink {
#Id
#GeneratedValue
private Long id;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "department_id")
private Department department;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "role_id")
private Role role;
}
Regarding setting the cascade types for the many to many relatioship is tricky and for many to many involving three tables is even trickier as every entity can play a role of parent or child depending on the circumstances.. i would suggest sticking only with the cascade = {CascadeType.PERSIST, CascadeType.MERGE} and handling other operations manually.
If you have to stay with the composite primary key then you should add additional Id class and change the link entity to the following:
Option 2
class User {
#OneToMany(mappedBy = "linkPk.user", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Department{
#OneToMany(mappedBy = "linkPk.department", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
class Role{
#OneToMany(mappedBy = "linkPk.role", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserDepartmentRoleLink> userDepartmentRoleLinks;
}
Linkage table
class UserDepartmentRoleLink {
#EmbeddedId
private UserDepartmentRoleLinkId linkPk
= new UserDepartmentRoleLinkId();
#Transient
public User getUser() {
return getLinkPk().getUser();
}
#Transient
public User getDepartment() {
return getLinkPk().getDepartment();
}
#Transient
public User getRole() {
return getLinkPk().getRole();
}
}
#Embeddable
public class UserDepartmentRoleLinkId implements java.io.Serializable {
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "department_id")
private Department department;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "role_id")
private Role role;
The bottom line is that you can use Many To Many here like outlined in this post -> example. But in my opinion you would save yourself a lot of headache if you map that link table as above. In the end the call is yours..
I have the following Schema:
#Entity
public class A{
#OneToMany(mappedBy = "dest",cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<C> myDest; //Owns these
#OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<C> myParents; //can be reached from these
#ManyToMany
private Collection<B> myOwnB;;
}
#Entity
public class B{
#OneToMany(mappedBy = "forB",cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<C> associatedC;
#ManyToMany(mappedBy = "myOwnB")
private Collection<A> associatedA;
}
#Entity
public class C{
#JoinColumn(referencedColumnName = "myDest")
#ManyToOne
private A dest;
#JoinColumn(referencedColumnName = "myParents")
#ManyToOne
private A owner;
#JoinColumn(referencedColumnName = "associatedC")
#ManyToOne
private B forB;
}
Here, if I delete a record of A or B, all the associated Cs get deleted. Perfect.
So my problem is this:
If I delete a reference of B from A (in that ManyToMany relation), I need all the Cs deleted as well for that A and B. (All Cs where forB=removedB and owner=sourceA). I have now written a similar query and it works fine now that I explicitly execute in my EJB. Is there a workaround or a annotation saying that a record of C gets deleted if one of the attributes(column) become null?