Hibernate - multiple many to many associations - cannot simultaneously fetch multiple bags - java

I'm trying to map two many to many associations in cascade. I have three classes: User, Usergroup and Permission. The first one has a many to many association to the second one while the second one has a many to many association to the third one.
I'm using hibernate 4.2.0
#Entity
#Table(name = "user")
public class User implements Serializable {
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity = org.weedea.bidupsys.user.logic.model.UserGroup.class)
#JoinTable(name = "UserGroupUser", joinColumns = { #JoinColumn(name = "userId") }, inverseJoinColumns = { #JoinColumn(name = "userGroupId") })
private List<UserGroup> userGroupList = null;
}
#Entity
#Table(name = "userGroup")
public class UserGroup implements Serializable {
#ManyToMany(fetch = FetchType.EAGER, targetEntity = org.weedea.bidupsys.user.logic.model.Permission.class, cascade = { CascadeType.ALL })
#JoinTable(name = "UserGroupPermission", joinColumns = { #JoinColumn(name = "userGroupId") }, inverseJoinColumns = { #JoinColumn(name = "permissionId") })
private List<Permission> permissionList = null;
}
With this configuration I get an error, because I try to load simultaneously two eager collections:
javax.servlet.ServletException: cannot simultaneously fetch multiple bags
javax.faces.webapp.FacesServlet.service(FacesServlet.java:229)
If I put fetch = FetchType.LAZY on the second collection, I get another error:
failed to lazily initialize a collection of role: org.weedea.bidupsys.user.logic.model.UserGroup.permissionList, could not initialize proxy - no Session
How could I map this two many to many associations? Thanks for help!

Short answer is you need to map them as java.util.Sets.
Here's a nice blog post explaining the issue: Hibernate Exception - Simultaneously Fetch Multiple Bags

Related

How to avoid removing entity from many to many in jpa

I have this mapping at Person entity:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "PersonAddress", joinColumns = { #JoinColumn(name = "personId") }, inverseJoinColumns = {
#JoinColumn(name = "addressId") })
private Set<Address> addresses;
And this at Address entity:
#ManyToMany(mappedBy = "addresses")
private Set<Person> owners;
I tried all cascade options available, but everytime when I save a Person entity it removes all relations with addresses. Is it possible to keep addresses when saving Person?
its not recommended to use CascadeType.REMOVE or CascadeType.ALL in to-many associations, you should implement the removal of child entities yourself.
Here the link it explains with details how to do the implementation.
https://thorben-janssen.com/avoid-cascadetype-delete-many-assocations/

Hibernate, Abstract classes and common Entity

I got an abstract class called AbstractUser and two subclasses Organisation and User. Organisation got the same as User but with additional features.
I have another class aswell called notes
Organisation can create notes
And user can save notes. So notes belong to one Organisation but can be saved by alot of user objects
Is it possible to properly setup the notes to both have a ManyToMany relation with user and a ManyToOne with organisation?
I'd pretty much like a relation below, where
note_user note_organisation
note_id user_id id(incremented) note_id organisation_id
1 4 1 1 2
1 3 2 2 2
2 3 3 3 3
This is what I've got so far.
Organisation:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long OrganisationId;
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
#JoinTable(name = "organisation_note", joinColumns = { #JoinColumn(name = "OrganisationId") }, inverseJoinColumns = { #JoinColumn(name = "note_id") })
private Set<Note> bookmarks = new HashSet<>();
user:
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long userId;
#ManyToMany( fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST } )
#JoinTable(
name = "user_note",
joinColumns = {#JoinColumn(name = "userId")},
inverseJoinColumns = {#JoinColumn(name = "note_id")})
private Set<Note> notes = new HashSet<>();
note:
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long projectId;
//Is this even necessary? I never save users to notes
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL )
private Set<User> users = new HashSet<>();
Above works. But it's behaving very very strangely. I get duplicated rows all the time when I create a note or save a note with a user. This will also allow me to create two notes with an organisation object, but the third will give me an error saying duplicate key for primary, same primary as the first note created. Is there anything I can do make my code more consistent and reliable?
Saving objects:
session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
session.persist(entity);
try {
session.flush();
} catch (Exception e) {
System.out.println("Error flushing");
}
session.getTransaction().commit();

Many to Many Not Respecting Update and Insert

I am trying to setup and Many to Many relationship between users and roles. I am mostly using JPA Repositories but I also tried using and EntityManger.
I have the following in my main User object.
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "svcAuthUserRolev2", schema="dbo", joinColumns = {
#JoinColumn(name = "user_id", updatable=false,insertable=false, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "role_id",
updatable=false,insertable=false, nullable = false) })
private Set<AuthRoleEntity> roles;
And the following in my Roles object
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
private Set<AuthUserEntity> users;
No matter what I do if I make changes to the roles on a user when saving they are persisted and this is not what I would like. I want roles on the user object to be read only.
I'm not sure why updateable and insertable are not working, I haven't used those attributes much. One possible solution is to make AuthRoleEntity the owning entity of the many-to-many relationship. Just move the #JoinTable annotation to the AuthRoleEntity and put the mappedBy on the AuthUserEntity.

Hibernate - ManyToOne & Inheritance / JOINED / mappedBy

I have some problems with inheritance mapping. Here my database structure:
And associated entities:
AbstractEntity:
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Id #GeneratedValue(strategy = IDENTITY)
#Column(unique = true, updatable = false, nullable = false)
private ID id;
public ID getId() {
return id;
}
#SuppressWarnings("unused")
public void setId(ID id) {
this.id = id;
}
UserAcitvity entity:
#Entity #Table(name = "user_activity")
#Inheritance(strategy = JOINED)
#AttributeOverride(name = "id", column = #Column(name = "ua_id"))
public abstract class UserActivity extends AbstractEntity<Long> {
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "ua_user_id")
private User user;
...
}
Comment entity:
#Entity #Table(name = "comment")
#PrimaryKeyJoinColumn(name = "cm_id")
public class Comment extends UserActivity {
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "cm_question_id")
private Question question;
...
}
Question entity:
#Entity #Table(name = "question")
#PrimaryKeyJoinColumn(name = "qs_id")
public class Question extends UserActivity {
...
#OneToMany(fetch = LAZY, cascade = ALL, mappedBy = "question")
private List<Answer> answers = new ArrayList<>();
#OneToMany(fetch = LAZY, cascade = ALL, mappedBy = "question")
private List<Comment> comments = new ArrayList<>();
...
}
Answer entity:
#Entity #Table(name = "answer")
#PrimaryKeyJoinColumn(name = "asw_id")
public class Answer extends UserActivity {
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "asw_question_id")
private Question question;
...
}
and User entity:
#Entity #Table(name = "user")
#AttributeOverride(name = "id", column = #Column(name = "user_id"))
public class User extends AbstractEntity<Long> {
...
#OneToMany(cascade = REMOVE)
private List<Question> questions = new ArrayList<>();
#OneToMany(cascade = REMOVE)
private List<Answer> answers = new ArrayList<>();
#OneToMany(cascade = REMOVE)
private List<Comment> comments = new ArrayList<>();
...
}
Problem:
When I try to save or delete a User I get an exceptions:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [insert into user_question (user_user_id, questions_qs_id) values (?, ?)]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
and:
org.hibernate.engine.jdbc.spi.SqlExceptionHelper : 147 = user lacks privilege or object not found: USER_ANSWER
Hibernate is trying to create a table: user_question and user_answer which me do not need.
What I should doing for fixes ?
I don't think you can achieve this by mapping the ManyToOne association to User generically in the UserActivity entity. That's probably too confusing for the JPA provider (Hibernate).
Instead, I think you need to map the association to User in each of the Question, Answer and Comment entities. Yes, I know that would be duplicated code, but it looks like the only way you will then be able to qualify the OneToMany mappings in User using the mappedBy reference.
For instance, your Question entity would have an association defined as:
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "ua_user_id")
private User questionUser;
Depending on how clever (or not) Hibernate is about the above association, you may need to specify the table="USER_ACTIVITY" in the JoinColumn annotation.
Then the User would have the OneToMany as:
#OneToMany(mappedBy="questionUser", cascade = REMOVE)
private List<Question> questions = new ArrayList<>();
Similarly for each of Answer and Comment.
Of course, I haven't tried this, so I could be wrong.
It's probably happening because when you set the #OneToMany mapping then the hibernate will create an auxiliary table that will store the id from the entities on the relationship.
In this case you should try the following:
#OneToMany(cascade = REMOVE)
#JoinColumn(name = "answer_id")
private List<Answer> answers = new ArrayList<>();
The #JoinColumn annotation will map the relationship without the creation of the auxiliary table, so it's pretty likely this solution will help you in this situation.
Try this mapping, this should work as you expect according to section 2.2.5.3.1.1 of the documentation:
#Entity
public class User {
#OneToMany(cascade = REMOVE)
#JoinColumn(name="user_fk") //we need to duplicate the physical information
private List<Question> questions = new ArrayList<>();
...
}
#Entity
public class Question {
#ManyToOne
#JoinColumn(name="user_fk", insertable=false, updatable=false)
private User user;
...
}
The reason why the auxiliary association is created, is that there is no way for Hibernate to know that the Many side of the relation (for example Question) has a foreign key back to User that corresponds to the exact same relation as User.questions.
The association Question.user could be a completely different association, for example User.questionCreator or User.previousSuccessfulAnswerer.
Just by looking at Question.user, there is no way for Hibernate to know that it's the same association as User.questions.
So without the mappedBy indicating that the relation is the same, or #JoinColumn to indicate that there is no join table (but only a join column), Hibernate will trigger the generic one-to-many association mapping solution that consists in creating an auxiliary mapping table.
The schema misses such association tables, which causes the error that can be solved with the mapping above.
If you want unidirectional one-to-many usage in your entity relationship.
Try with..JoinTable
#OneToMany(cascade = REMOVE)
#JoinTable(name = "user_question", joinColumns = {
#JoinColumn(name = "user_id")}, inverseJoinColumns = {
#JoinColumn(name = "qs_id")})
private List<Question> questions = new ArrayList<>();

Wrong update while cascading many-to-many relationship

I have a many-to-many relationship defined on hibernate like this:
User
public class User{
private List<UserCustomer> userCustomerList;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id.user", cascade=CascadeType.ALL)
public List<UserCustomer> getUserCustomerList() {
return userCustomerList;
}
}
UserCustomer
#Entity
#Table(name = "RTDB_USER_CUSTOMER")
#Component("userCustomerEntity")
#AssociationOverrides({
#AssociationOverride(name = "id.user",
joinColumns = #JoinColumn(name = "ID_USER")),
#AssociationOverride(name = "id.customer",
joinColumns = #JoinColumn(name = "ID_CUSTOMER")) })
public class UserCustomer {
#EmbeddedId
public UserCustomerId getId() {
return id;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({ #JoinColumn(name = "ID_ROLE_CUSTOMER", referencedColumnName = "ID") }) public RoleCustomer getRoleCustomer() {
return roleCustomer;
}
}
So a user has a list of UserCustomer, that represent roles of users over customers. The problem is, that when we change a role over a customer and call update(), instead of one row updated we get all the rows updated with the same role. When we call merge() it starts to perform a lots of queries and then gives stackoverflow exception ¿Could this be a mapping problem?
Can you post the tables and updation code?
I think you are updating the role directly from UserCustomer which should be updating all the roles, as far as my understanding goes you do not want to update UserCustomer but only RoleCustomer. Try to fetch RoleCustomer and update it not the UserCustomer.

Categories

Resources