Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Criteria.DISTINCT_ROOT_ENTITY doesn't prevent duplicated objects

I have following dao method:

@Override
public List<AdminRole> findAll() {
    Session session = sessionFactory.getCurrentSession();
    Criteria criteria = session.createCriteria(AdminRole.class);
    criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
    return criteria.list();
}

Actually I want to retrieve all entries from database.

Sometimes I see duplicates. This happens when I add user with AdminRole.

I have read that it is possible when I use EAGER fetch type and this should be fix adding following line:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

But this doesn't help me.

my mapping:

@Entity
@Table(name = "terminal_admin_role")
public class AdminRole {

    @Id
    @Column(name = "role_id", nullable = false, unique = true)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id")
    @SequenceGenerator(name = "user_id", sequenceName = "user_id")
    private Long adminId;

    @Column(name = "role")
    private String role;

    public AdminRole(String role) {
        this.role = role;
    }

    public AdminRole() {
    }

    // get set

    @Override
    public String toString(){
        return role;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof AdminRole)) {
            return false;
        }

        AdminRole adminRole = (AdminRole) o;

        if (!role.equals(adminRole.role)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        return role.hashCode();
    }
}

and

@Entity
@Table(name = "terminal_admin")
public class TerminalAdmin {
    @ManyToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
    @JoinTable(name = "admin_role", joinColumns = { 
        @JoinColumn(name = "admin_id", nullable = false) }, 
        inverseJoinColumns = { @JoinColumn(name = "role_id", 
                nullable = false) })
    private Set<AdminRole> adminRoles;      
    //...
}

P.S.

I cannot switch fetch type.

I don't want to put this list into set.

like image 486
gstackoverflow Avatar asked Oct 14 '15 14:10

gstackoverflow


2 Answers

There is no reason to use DISTINCT_ROOT_ENTITY or anything similar, all you need is:

session.createCriteria(AdminRole.class).list();

If you get duplicates, then you really have them in the database. Check the code which saves AdminRoles either directly or by cascading from other entities.

When cascading PERSIST/MERGE operations from other entities, make sure that the operation is cascaded to a persistent/detached AdminRole instance, not to a transient (new) one.

like image 89
Dragan Bozanovic Avatar answered Sep 22 '22 16:09

Dragan Bozanovic


My money's on messing with hashCode/equals overrides and Hibernate proxies.

From EqualsandHashCode

However, once you close the Hibernate session, all bets are off. [...] Hence, if you keep collections of objects around between sessions, you will start to experience odd behavior (duplicate objects in collections, mainly).

First I'd use org.apache.commons.lang3 like so (it's obviously way too expensive for Hibernate entities but works ok w/ them and if it works that should validate my hunch):

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;


@Override
public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

@Override
public int hashCode() {
    return HashCodeBuilder.reflectionHashCode(this);
}

@Override
public boolean equals(Object other) {
    return EqualsBuilder.reflectionEquals(this, other);
}

If this works you could go with a less expensive approach like so:

@Override
public int hashCode() {
    HashCodeBuilder hcb = new HashCodeBuilder();
    hcb.append(role);
    return hcb.toHashCode();
}

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (!(obj instanceof AdminRole)) {
        return false;
    }
    AdminRole that = (AdminRole) obj;
    EqualsBuilder eb = new EqualsBuilder();
    eb.append(role, that.role);
    return eb.isEquals();
}
like image 22
Filip Avatar answered Sep 22 '22 16:09

Filip