Критерии .DISTINCT_ROOT_ENTITY не предотвращают дублирование объектов

У меня есть следующий метод dao:

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

На самом деле я хочу получить все записи из базы данных.

Иногда я вижу дубликаты. Это происходит, когда я добавляю пользователя с AdminRole.

Я читал, что это возможно, когда я использую EAGER тип выборки, и это должно быть исправление, добавляющее следующую строку:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Но это мне не помогает.

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();
    }
}

и

@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.

Я не могу переключить тип выборки.

Я не хочу, чтобы этот список был установлен.

Ответ 1

Нет причин использовать DISTINCT_ROOT_ENTITY или что-то подобное, все, что вам нужно:

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

Если вы получаете дубликаты, то вы действительно имеете их в базе данных. Проверьте код, который сохраняет AdminRole либо напрямую, либо путем каскадирования из других объектов.

При каскадировании PERSIST/MERGE операций с другими объектами убедитесь, что операция каскадирована на постоянный/отсоединенный экземпляр AdminRole, а не на переходный (новый).

Ответ 2

Мои деньги на messing с hashCode/equals переопределениями и проксими Hibernate.

Из EqualsandHashCode

Однако, как только вы закрываете сеанс Hibernate, все ставки отключены. [...] Следовательно, если вы сохраняете коллекции объектов между сеансами, вы начнете испытывать нечетное поведение (в основном дублировать объекты в коллекциях).

Сначала я использовал бы org.apache.commons.lang3 так (это, очевидно, слишком дорого для сущностей Hibernate, но работает нормально с ними, и если он работает, это должно подтвердить мою догадку):

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);
}

Если это сработает, вы можете пойти с менее дорогостоящим подходом:

@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();
}