I update hibernate from 3.5.6 to 4.10, I have a problem with many-to-many relationship:
User Class
#Entity
#Table(name="user")
public class User {
#ManyToMany(fetch=FetchType.LAZY)
#Cascade(value={CascadeType.PERSIST, CascadeType.MERGE,CascadeType.REFRESH})
#JoinTable(name="usersroles", joinColumns = { #JoinColumn(name ="usersroles_user" )}, inverseJoinColumns = { #JoinColumn(name = "usersroles_role") })
#JSON(serialize=false)
public Set<Role> getRoles() {
return roles;
}
}
Role Class
#Entity
#Table(name="role")
public class Role {
#ManyToMany(cascade {CascadeType.REFRESH},fetch=FetchType.LAZY,mappedBy="roles")
#JSON(serialize=false)
public Set<User> getUsers() {
return users;
}
}
That is a query method:
public List<User> getUserAndRoleList() {
String jpql = "SELECT DISTINCT u FROM User u , Role r, UserRole ur WHERE u.id = ur.userId AND r.id = ur.roleId ORDER BY u.username";
QueryResult<User> qr = super.getDataWithOriginalJPQL(jpql);
userList = qr.getResultList();
return userList;
}
When I want to get the Role:
userList = userService.getUserAndRoleList();
for(int i=0;i<userList.size();i++){
Iterator<Role> it = userList.get(i).getRoles().iterator();
Role role ;
}
This works. But when code executes
userList.get(i).getRoles()
Hibernate 4.1 will automatically delete User and Role relationship in usersroles table. But with Hibernate 3.6.5, it didn't delete the relationship.
In fact, I don't delete anything at all and I don't ask hibernate to delete anything. Something that I don't understand might occur. How do I prevent Hibernate from deleting? Or is there anything that I did wrong?
Related
I have two entities User and Attribute and they have Many to Many relation. like below.
public class User implements java.io.Serializable {
private Set<Attribute> attributes = new HashSet<>();
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "user_attributes", joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "attribute_id") })
public Set<Attribute> getAttributes() {
return attributes;
}
}
User_Attributes table may or may not contain data. I am trying to fetch user like below
public User getUserByUserNameAndStatus(String userName, int status) {
Criteria criteria = getSession().createCriteria(User.class);
criteria.createAlias("attributes", "attr", JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("roles", FetchMode.JOIN);
criteria.add(Restrictions.eq("attr.status", 1));
criteria.add(Restrictions.eq("userName", userName));
criteria.add(Restrictions.eq("status", status));
User user = (User) criteria.uniqueResult();
return user;
}
This code return me user object only if there is an entry in User_Attributes table otherwise it is returning me null. But what I want to fetch user object also if there is no entry in User_Attributes table.
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
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 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.
#Entity
public class User{
#ElementCollection
#Enumerated(EnumType.STRING)
#CollectionTable(name = "SEC_USER_ROLES",
joinColumns =
#JoinColumn(name = "USER_ID", referencedColumnName = "ID"))
#Column(name = "ROLE_NAME")
private List<Role> roles;
[...]
}
public enum Role {
ROLE_SUPER_ADMIN,
ROLE_ADMIN,
ROLE_ARB,
ROLE_AP;
[...]
}
With this mapping, when I try do delete one ROLE, for example ROLE_ARB, it always ends up with deleting the role and inserting it once again.
DELETE FROM SEC_USER_ROLES WHERE ((USER_ID = ?) AND (ROLE_NAME = ?))
bind => [9451, ROLE_ADMIN]
INSERT INTO SEC_USER_ROLES (USER_ID, ROLE_NAME) VALUES (?, ?)
bind => [9451, ROLE_ADMIN]
I tried to solve the problem with #OrderColumn (name="USER_ID") but then the mapping of the User_id is not correct.
Any idea would be appreciated.
The Roles are represented as selectManyCheckbox
The ManagedBean prepares the entity (User)
...
List<String> selectedroles = this.getSelectedItems();
List<Role> newroles = new ArrayList<Role>();
if (selectedroles != null) {
for (String r : selectedroles) {
newroles.add(Role.valueOf(r));
}
getEntity().setRoles(newroles);
...
security.save(getEntity());
and the EJB makes updates if it is an existing Entity
EntityManager em;
...
this.em.merge(user);
So when someone deselects all (previous selected) Roles there is always one Role left in the database which is not deleted, because of the delete/insert behavior I described before.
#OrderColumn solved the problem