Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate returns duplicate because another table has duplicate values

So I have a Users table, Projects table and User_Roles table. I have one users which is connected to 2 projects, but when I have just one role for him it returns those 2 projects, but if I have 2 roles for him it returns 4 roles (2x2 projects). How do I prevent this

This is my code for returning the list of projects for user

@Override
public List<Project> retrieve(User user) {
    Criteria criteria = super.createCriteria();
    criteria.addOrder(Order.desc("date"));
    criteria.createCriteria("users").add(Restrictions.eq("id", user.getId()));

    return (List<Project>) criteria.list();
}

mappings in User class

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "Users_Projects",
           joinColumns = @JoinColumn(name = "UserID"), inverseJoinColumns =   @JoinColumn(name = "ProjectID"))
public List<Project> getProjects() {
    return projects;
} 

@ElementCollection(fetch = FetchType.EAGER)
@Enumerated(EnumType.STRING)
@Column(name = "RoleType")
@JoinTable(name = "User_Roles", joinColumns = @JoinColumn(name = "UserID"))
public Set<Role> getRoles() {
    return roles;
}

Any suggestions what is the problem here?

tnx

like image 859
Boris Horvat Avatar asked Jul 03 '12 21:07

Boris Horvat


1 Answers

The classic "fix" for issues of duplicate rows from criteria queries, which will likely work as long as you're not trying to combine it with pagination, is to add

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

I believe this is happening in your situation because of the eager loading of the user roles. Changing that to be lazy might also work, and continue to work if you need pagination.

Setting fetch = FetchType.EAGER instructs hibernate that you want to always load all the roles for the user whenever you retrieve a user, which it accomplishes by always doing a join to the roles table.

Unfortunately, that sql leads to multiple records with the same user (one for each role), and it then has to be straightened out in Java after the sql is run, which is what the result transformer does.

Much more unfortunately, if you want to use paging in the most natural way with simple hibernate criteria, using criteria.setFirstResult and criteria.setMaxResults, things happen in the wrong order, so these queries that need the result transformer to avoid duplicates just don't page right.

To understand all the hibernate sql generation a bit better, I'd advise turning on logging of the generated sql as described in this answer to another SO hibernate question.

like image 123
Don Roby Avatar answered Oct 20 '22 17:10

Don Roby