I'm trying to implement Many-to-many relation using Hibernate and MySQL DB.
I have class User:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "users_nodes",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "node_id")})
private List<Node> nodeList;
and class Node:
#ManyToMany(mappedBy = "nodeList", cascade = CascadeType.ALL)
private List<User> users;
When I'm trying to save a new Node to DB(which already have user in it) it successfully add new entity to "nodes" table, but relation table "users_nodes" is empty.
This is the way I save Node entity:
#Override #Transactional
public void persist(Node entity) {
getCurrentSession().save(entity);
}
Thanks for your help!
You have to update the owner side of the association (User.nodeList), meaning that you have to associate the User with the Node for each associated user. For example:
class Node {
...
public void addUser(User user) {
if (!users.contains(user)) {
users.add(user);
user.addNode(this);
}
}
}
class User {
...
public void addNode(Node node) {
if (!nodeList.contains(node)) {
nodeList.add(node);
node.addUser(this);
}
}
}
If this would be a performance issue (if Users have many Nodes so it would be expensive to load them all when associating a new node with the desired users), you could change your mappings so that Node is the owner of the association and/or you could consider other options and improvements described here.
Related
I'm working on a java spring mvc application with hibernate. I have two Entities Acl and AclGroupand These two entities have Many to Many relationship with a join table. But, when I save an AclGroup object, hibernate doesn't insert any record in join table and just inserts into AclGroup table. Here is structure of my classes:
Acl.java:
public class Acl implements Serializable{
...
#JoinTable(name = "acl_group_acl", joinColumns = {
#JoinColumn(name = "acl_id", referencedColumnName = "id")}, inverseJoinColumns = {
#JoinColumn(name = "acl_group_id", referencedColumnName = "id")})
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Collection<AclGroup> aclGroupCollection;
public Collection<AclGroup> getAclGroupCollection() {
return aclGroupCollection;
}
public void setAclGroupCollection(Collection<AclGroup> aclGroupCollection) {
this.aclGroupCollection = aclGroupCollection;
}
...
}
AclGroup.java:
public class AclGroup implements Serializable{
...
#ManyToMany(mappedBy = "aclGroupCollection",fetch = FetchType.LAZY)
private Collection<Acl> aclCollection;
public Collection<Acl> getAclCollection() {
return aclCollection;
}
public void setAclCollection(Collection<Acl> aclCollection) {
this.aclCollection = aclCollection;
}
}
Here is how I save my object:
AclGroup aclGroup = new AclGroup();
List<Acl> acls = new ArrayList<>();
/*
add some elements to acls
*/
aclGroup.setAclCollection(acls);
/*
hibernate config and opening a session
*/
session.save(aclGroup); //session.persist also did not work
Could anyone help me to solve this problem? Thank you for your attention.
The owner side of the association is Acl. AclGroup is the inverse side (since it has the mappedBy attribute). Hibernate only cares about the owner side.
So make sure to add the group to the acl when you add the acl to the group: that will work whatever the owner side is, and will make your graph coherent. Or, if you absolutely don't want to do that, put the mapping annotations on AclGroup, and make Acl the inverse side.
I have two entities:
parent :
public class UserGroup{
#OneToMany(fetch = FetchType.EAGER, mappedBy = "userGroup", cascade = CascadeType.ALL)
private List<User> users;
}
and child:
public class User{
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.SELECT)
#JoinColumn(name = "user_group_id")
private UserGroup userGroup;
}
when i am trying to save UserGroup with one User in list of users, with this method:
#Transactional
public E save(E e) {
em.persist(e);
em.flush();
em.refresh(e);
return e;
}
my parent and child is getting saved, but user_group_id in child object is null.
Is there any solution?
By considering you are giving UserGroup object along with list of User to save method: so your code should be:
em.save(userGroup);
for(User user : UserGroup.getUsers())
{
user.setuser_group_id(userGroup.getUserGroupId());
em.save(user);
}
You have a bidirectional relationship. The correct way of saving it is putting both references in the entities.
You should do:
userGroup.getUsers().add(user);
user.setUserGroup(userGroup);
entityManager.persist(userGroup);
In your parent setter method of child, do this.
public void setChildren(Collection<Child> children) {
this.children = children;
for(Child child: this.children) {
child.setParent(this)
}
}
This should solve.
I have two entities, User and Event. Each event can have multiple users associated with it, so its a one to many between Event and User.
The way its being stored in the database, is that I have 3 tables, user, event, and event_user. event_user contains 3 fields, id, eventId, userId. So I can do a query like select userId from event_user where eventId = ? to get all the users which are associated with the event.
My question is, how can I map this relationship between the events and users in Hibernate, to get it to auto save/load the users associated with the event? I want to have the following field in the Event class:
Set<User> users = new HashSet<>();
and have hibernate auto load / save the users to this set.
How can I map this (using annotations)?
Use the #ManyToMany annotation.
class Event{
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "EVENT_USER",
joinColumns = { #JoinColumn(name = "EVENT_ID") },
inverseJoinColumns = { #JoinColumn(name = "USER_ID") })
private Set<Users> users = new HashSet<Users>();
}
For more information on many to many associations in JPA check out this video tutorial at my blog.
Hibernate doc on the Bidirectional mapping using annotations should help
Basically you need to do something like this
#Entity
public class User implements Serializable {
#ManyToMany(
targetEntity=org.hibernate.test.metadata.manytomany.Event.class,
cascade={CascadeType.ALL}
)
#JoinTable(
name="USER_EVENT",
joinColumns=#JoinColumn(name="USER_ID"),
inverseJoinColumns=#JoinColumn(name="EVENT_ID")
)
public Set<Event> getEvents() {
return events;
}
...
}
#Entity
public class Event implements Serializable {
#ManyToMany(
cascade = {CascadeType.ALL},
mappedBy = "events",
targetEntity = User.class
)
public Set<User> getUsers() {
return users;
}
}
I am using EclipseLink for this. I have a ternary relation between three entities called Staff, Person and Job. I introduced the Embeddable class StaffItem that consists solely of a Person and Job. Staff has an ElementCollection of StaffItems.
I have no problem persisting new StaffItems to the Database, that were added to a Staff Entity, but whenever I change one item or delete it and try to merge the existing Staff Entity, the EntityManager seems to run into an infinite loop on the flushing. I do not get an error or exception, I simply do not return from the flush().
Staff.java
#Entity
public class Staff {
private List<StaffItem> staffItems;
#ElementCollection
#CollectionTable(name = "staff_items", joinedColumns = #JoinedColumn(name = "staff"))
public List<StaffItem> getStaffItems() { ... }
// setter, etc.
}
StaffItem.java
#Embeddable
public class StaffItem {
private Person person;
private Job job;
#ManyToOne
#JoinColumn(name = "person", referencedColumn = "id")
public Person getPerson() { ... }
#ManyToOne
#JoinColumn(name = "job", referencedColumn = "id")
public Job getJob() { ... }
// setter, etc.
}
I have two tables: Recipe table and Account table. The Recipe table stores a number of recipes. The account table stores a number of user account. A user can be associated with 0 or more recipe. When a user likes a recipe, this user is associated to the recipe. To record this association, I created a table called LikedRecipe table.
These are the columns of each table:
Recipe: id, name. Id is the primary key.
Account: email, password. Email is the primary key.
LikedRecipe: id, name, email. id is the primary key.
#Entity
#Table(name = "Recipe")
public class Recipe {
private Set<Account> account;
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "LikedRecipe", joinColumns = #JoinColumn(name = "recipeId"), inverseJoinColumns = #JoinColumn(name = "email"))
public Set<Account> getAccount() {
return account;
}
public void setAccount(Set<Account> account) {
this.account = account;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
#Entity
#Table(name = "Account")
public class Account implements Serializable {
private Set<Recipe> likedRecipes = new HashSet<Recipe>();
private String email;
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
#JoinTable(name = "LikedRecipe", joinColumns = #JoinColumn(name = "email"), inverseJoinColumns = #JoinColumn(name = "recipeId"))
public Set<Recipe> getLikedRecipes() {
return likedRecipes;
}
public void setLikedRecipes(Set<Recipe> likedRecipes) {
this.likedRecipes = likedRecipes;
}
#Column(name = "email")
#Id
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
I wrote a method to remove the association between an account and a recipe:
public void unlikeARecipe(String email, Long recipeId){
Query query = entityManager
.createQuery("delete from LikedRecipe where recipeId = :recipeId and email = :email");
query.setParameter("recipeId", recipeId);
query.setParameter("email", email);
query.executeUpdate();
}
This method did not delete records from LikedRecipe table, until I added this line of code at the end of the method:
entityManager.clear();
According to JPA API documentation the clear method clears the persistence context, causing all managed entities to become detached.
My question is what does detach means ? And, how does detaching objects made the above method deletes records from LikedRecipe table? Am I using the clear method in the right manner ?
Thank you.
Detached entity is an entity not currently managed by a persistence context but whose id is present in database.
I think you don't get the LikedRecipe entity deleted because you still have it referenced from other persistent entities (Account and Recipe). Indeed it works when you clear the persistence context, detaching all entities that are "keeping alive" the LikedRecipe you wanted to delete.
If you want to keep the many-to-many relationships, you have to clear them as well (i.e. removing the object from Account's and Recipe's collections) when you're about to deleting the LikedRecipe entity.
Shouldn´t be better to make design something like:
- Recipe(id,name)
- User(email, ...)
- LikedRecipe(userEmail, recipeId)?
And don´t make relationship in recipe for users, it´s (in my humble opinion) useless relationship.
To this case it´s enough to make OneToMany relationship from User (/Account) to Recipe and none relation in Recipe for Users.