I have an entity class named User which contains this OneToMany column for database:
#Setter
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
#JoinTable(
name = "USER_CARS",
joinColumns = #JoinColumn(name="USER_ID", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name="CAR_ID", referencedColumnName = "id")
)
private List<Car> cars;
When I'm inserting an user into database, everything works fine, and his cars are also added in user_cars table. When retrieving the cars i get this exception:
Method threw 'org.hibernate.LazyInitializationException' exception.
I've searched for other answers but didn't found how to solve it. This is how I'm trying to retrieve the User.
public T findById(Long id) {
em = emf.createEntityManager();
em.getTransaction().begin();
T et = (T) em.find(entityClass, id);
em.getTransaction().commit();
em.close();
return et;
}
What is the problem and how can I fix it? I can't understand what's going on in background.
Based on the exception message, it seems that you are retrieving the content of the List cars but the EntityManager session is already closed. Rather than the default lazy initialization for the collection you could use
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.EAGER)
Related
I have a #ManyToMany table enter image
The role table contains the roles ROLE_USER, ROLE_ADMIN, and so on.
Below I show how I create a relationship between the user and role tables.
Role.class
#ManyToMany(mappedBy = "roleList", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
private List<User> userList = new ArrayList<>();
public void addPerson(User user) {
userList.add( user );
user.getRoleList().add( this );
}
User.class
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private List<RoleUser> roleUserList = new ArrayList<>();
public void addRole(Role role) {
roleList.add(role);
role.getUserList().add( this );
}
Suppose I already have a list of roles in the role table. Now I want to create a user and assign it one of these roles.
In this case, I'm trying to do the following:
I specify FetchType.EAGER (This is not desirable for me. Otherwise it does not work), then I write the following in the controller:
Role role = serviceJpa.findRoleByRoleName("ROLE_USER");
role.addUser(user);
serviceJpa.saveRole(role);
If we take a look, the user has been added perfectly and the role has been successfully assigned to that user.
But as I said earlier, FetchType.EAGER is not desirable for me, because I do not want to get a million users when accessing the role table.
But if I use FetchType.LAZY then an exception is thrown:
Message Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: com.testmany.entity.Role.userList, could
not initialize proxy - no Session
Here I immediately have a question - maybe I am not adding a user correctly and assigning a role to him incorrectly?
If I do it in another way, for example this:
User user = new User("Иван");
Role role = serviceJpa.findRoleByRoleName("ROLE_USER");
user.addRole(role);
serviceJpa.saveUser(user);
then another exception occurs:
Message Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached
entity passed to persist: com.testmany.shop.entity.Role; nested
exception is org.hibernate.PersistentObjectException: detached entity
passed to persist: com.testmany.entity.Role
What am I doing wrong? How do I add a user with FetchType.LAZY, that is, without FetchType.EAGER. What is the correct way to add a user and assign him a role? What am I doing wrong. I have reviewed and re-read a bunch of info, hibernate documentation, and nothing helps. How are you doing, show?
In your user model set lazy fetch as follows:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "app_user_role", joinColumns = #JoinColumn(name = "userid"), inverseJoinColumns = #JoinColumn(name = "roleid"))
private List<Role> roles;
To compensate lazy loading, initialize it somewhere in your controller like below (just to give you hint):
#ModelAttribute("roles")
public List<Role> initializeRoles() {
return (List<Role>) roleJPARepository.findAll();
}
Then, somewhere in your code (say a Service), set the role to the user like below:
entity.setRoles(user.getRoles());
For a full blown example where you set/edit the role to user through UI, is here in my GitHub Repo - https://github.com/ajkr195/springbootrocks
This is pretty much close to what you are looking for.
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "user_role",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")}
)
private List<RoleUser> roleUserList;
you can try this it may be helpful
I've made some research but without any specific answer.
I know how #JsonView... #JsonIgnore works... but my point here is about back end, the point of view from there. I'm working on spring boot and by default OSIV is enabled, so as far as I know, if I'm not wrong, if I make a call in database on an #Entity that has #ManyToMany association it will eagerly fetch everything.
Till there I have no issues, the problem is that the associated Collection also has Collections... And some services need to fetch them and others don't... Then I keep getting LazyInitializationException.
#Entity
#EntityListeners(AuditingEntityListener.class)
#Inheritance(strategy = InheritanceType.JOINED)
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private int id;
private String categoryTitle;
private String categoryDescription;
#ManyToMany
#JoinTable(
name = "Category_Parent",
joinColumns = #JoinColumn(name = "id_category", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "id_category_parent")
)
private Set<Category> parentCategory;
#ManyToMany
#JoinTable(
name = "Category_Parent",
joinColumns = #JoinColumn(name = "id_category_parent", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "id_category")
)
private Set<Category> subCategory;
Then to prevent that error I used #Query like this
#Repository
public interface CategoryRepository extends JpaRepository<Category, Integer> {
#Query("from Category c left join fetch c.subCategory left join fetch c.parentCategory")
List<Category> getAllCategories();
}
Now I'm able to fetch it lazly... I used that #Query way because it is the only one I know to fetch the associated Collections... I heared about EntityGraph and Hibernate.initialize() but have no knowledge on how to proceed (would appreciate some link).
So, then I have Json exception because the json response is infinite. How can I avoid this new issue? Using DTO?
I appreciate.
------ EDIT ------
I've used #JsonView on the properties that I want to send as response, however if I use #JsonView over subCategory only, it works, but if I use on parentCategory I got the infinite loop once again... Can't solve it.
You can add fetch = FetchType.LAZY to your #ManyToMany or #OneToMany annotation like this: #ManyToMany(fetch = FetchType.LAZY). More instruction is at https://www.baeldung.com/hibernate-lazy-eager-loading
I have ManyToMany relationships in Hibernate
Project.class
#ManyToMany
#JoinTable(
name = "user_projects",
joinColumns = { #JoinColumn(name = "project_id")},
inverseJoinColumns = { #JoinColumn(name = "user_id")}
)
private Set<User> projectUsers = new HashSet<>();
User.class
#ManyToMany(fetch = FetchType.EAGER,
mappedBy="projectUsers",
cascade = CascadeType.ALL)
private Set<Project> userProjects = new HashSet<>();
And when I get current user thru
#AuthenticationPrincipal User user
I have Method threw 'org.hibernate.LazyInitializationException' exception when user.getUserProjects();
It's because when using #ManyToMany, first all Projects of user are fetched, then all Users of fetched Projects and so on... This cyclic dependency causes the error, because there is eager fetching missing on Project side.
The solution would be to use:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "user_projects",
joinColumns = { #JoinColumn(name = "project_id")},
inverseJoinColumns = { #JoinColumn(name = "user_id")}
)
private Set<User> projectUsers = new HashSet<>();
Although I do not recommend this solution, because of performance issues (and possible side effects). Better solution would be to have an entity for join table.
Try to use #Transactional maybe can solved it because
the #Transactional annotation on the getFavorites() method indicates that the session will be closed at the end of this method
I am currently using Hibernate in a project and I am having trouble setting up a Many to Many relationship. I think my issue is related to the Cascade types.
The scenario is we have an advert and each advert can have many tags but each tag can be related to many adverts. The ManyToMany annotation of the advert entity is:
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(name = "adMediaAdvertiserTag",
joinColumns = { #JoinColumn(name = "adMediaId") },
inverseJoinColumns = { #JoinColumn(name = "advertiserTagId") })
And the ManyToMany mapping on the tag entity is:
#ManyToMany(mappedBy = "advertiserTags", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
I am seeing two different errors depending on the configuration of the cascade types and if the tag already exists in the database.
If the cascade type on the advert doesn't include PERSIST and the tag doesn't already exist then we get this exception:
TransientObjectException: object references an unsaved transient instance
And if the cascade type on the advert does include PERSIST and the tag does already exist we get:
PersistentObjectException: detached entity passed to persist
The only way I can get this working is not have PERSIST as a cascade type but loop through each tag and save them to the session.
Is this the correct approach? Is there a way that hibernate could automatically handle this for me?
Let me know if this isn't clear or you need any more information.
UPDATE
I am saving the objects using a service that uses the save method the CrudRepository object of the springframework. There is a lot of code so I can't really post it all.
I am currently not calling any merge or persist method for any object I am trying to save. I was under the impression Hibernate would handle that sort of thing for me.
Can you please add the code you are using to persist your Advert and Tag objects. I tested with the following code (which I believe similar to yours) and it works:
User user = new User();
Role role = new Role();
role.setId("TEST_ROLE");
user.getRoles().add(role);
entityManager.persist(user);
User user2 = new User();
user2.getRoles().add(role);
entityManager.persist(user2);
System.out.println(user2.getId());
in User I have:
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinTable(name = "user_roles",
joinColumns = {#JoinColumn(name = "user_id", nullable = false, updatable = true)},
inverseJoinColumns = {#JoinColumn(name = "role_id", nullable = false, updatable = true)})
protected List<Role> roles = new ArrayList<>();
and in Role I have:
#ManyToMany(mappedBy="roles")
protected List<User> users;
I have a many-to-many relationship between user and role entities. when I try to merge a role by adding new user objects to that, after merging i expect a new row in the joined table (in sql server database) to be inserted but nothing happens.
I guess the problem is the direction of the relationship which the owner is User entity now and should be switched to Role. But if I change it then my spring security wont work. Is there any other way to solve this? except changing many-to-many sides?
Thanks in advance.
User class
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "SEC_USER_ROLE",
joinColumns = {
#JoinColumn(name = "SEC_USER_ID", referencedColumnName = "SEC_USERS")},
inverseJoinColumns = {
#JoinColumn(name = "SEC_ROLE_ID", referencedColumnName = "SEC_ROLES")})
private List<Role> roles;
--
Role class
#ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<User> users;
And here is my merge function
#Override
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public T merge(T o) {
o = em.merge(o);
em.flush();
return o;
}