使用Hibernate和TreeSet不能使用remove()和contains()方法

时间:2017-05-24 18:12:56

标签: java hibernate set contains treeset

我希望按年龄排序;

在这种情况下 compareTo()的方法运行正常,但问题是 remove() contians()方法总是返回

有趣:如果我从compareTo()方法取消注释行,remove()和contains()方法工作正常;但是我想用另一个字段作为排序。

有人知道为什么不能正常工作;发现旧的Hibernate问题:https://hibernate.atlassian.net/browse/HHH-2634; 这已经修好了吗?

Bellow是使用过的课程:

@Entity(name = "CAMPAIGN")
public class Campaign implements Identifiable, Serializable {
    public static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;


    @OneToMany(mappedBy = "campaign", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @OrderBy("age ASC")
    private SortedSet<MailingAddress> mailingAddresses = new TreeSet<>();

    ...

    public void removeMailingAddress(MailingAddress mailingAddress) {
        this.mailingAddresses.remove(mailingAddress);
        //this.mailingAddresses.contains(mailingAddress);

        mailingAddress.setCampaign(null);
    }
}

@Entity(name = "MAILING_ADDRESS")
public class MailingAddress implements Identifiable, Comparable, Serializable {
    public static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "CAMPAIGN_ID")
    private Campaign campaign;

    @Column(name = "AGE")
    private Integer age;

    @Override
    public int compareTo(Object o) {
        if (o == null) {
            return 1;
        }

        if (!(o instanceof MailingAddress)) {
            throw new ClassCastException("Cannot compare MailingAddress with " + o.getClass());
        }

        MailingAddress o1 = (MailingAddress) o;
        int comparison;

        // comparison for id
        /*comparison = compareFields(this.id, o1.id);
        if (comparison != 0) {
            return comparison;
        }*/

        // comparison for ageBand
        comparison = compareFields(this.age, o1.age);
        if (comparison != 0) {
            return comparison;
        }

        return 0;
    }

    private int compareFields(Comparable field1, Comparable field2) {
        if (field1 == null && field2 == null) {
            return 0;
        } else if (field1 == null && field2 != null) {
            return -1;
        } else if (field1 != null && field2 == null) {
            return 1;
        }
        return field1.compareTo(field2);
    }

    @Override
    public boolean equals(Object o) {
        return this.compareTo(o) == 0;
    }

}

更新

发现使用 SortedSet 作为 TreeSet 的界面并结合 Hibernate ,方法remove()和contains()无法正常工作。 &#34; SortedSet mailingAddresses = new TreeSet&lt;&gt;();&#34;

将定义更改为&#34;设置mailingAddresses = new TreeSet&lt;&gt;();&#34; 以及方法 remove() contains()工作正常;此外,使用compareTo()的排序也适用于除 id 之外的其他字段。

TreeSet,SortedSet和Hibernate 的组合可能存在一个错误。如果有人找到了这个问题的解释&#34; bug&#34;请告诉我。

这是一个工作版本:

@Entity
public class MailingAddress implements Identifiable, Comparable, Serializable {
    public static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;

    private Integer age;

    @Override
    public int compareTo(Object o) {
        if (o == null) {
            return 1;
        }

        if (!(o instanceof MailingAddress)) {
            throw new ClassCastException("Cannot compare MailingAddress with " + o.getClass());
        }

        MailingAddress o1 = (MailingAddress) o;
        int comparison = compareFields(this.age, o1.age);
        if (comparison != 0) {
            return comparison;
        }

        return 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MailingAddress that = (MailingAddress) o;

        if (id != null ? !id.equals(that.id) : that.id != null) return false;
        return age != null ? age.equals(that.age) : that.age == null;
    }

    @Override
    public int hashCode() {
        return 31;
    }

    private int compareFields(Comparable field1, Comparable field2) {
        if (field1 == null && field2 == null) {
            return 0;
        } else if (field1 == null && field2 != null) {
            return -1;
        } else if (field1 != null && field2 == null) {
            return 1;
        }
        return field1.compareTo(field2);
    }
}

@Entity
public class Campaign implements Identifiable, Serializable {
    public static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;


    @OneToMany(mappedBy = "campaign", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @OrderBy("age ASC")
    private Set<MailingAddress> mailingAddresses = new TreeSet<>();

    ...
}

1 个答案:

答案 0 :(得分:2)

这里的问题是你重写equals而不重写hashCode。正如this article中所述,您需要提供两者。此外,引用检查不适用于合并entity state transition

由于MailingAddress中没有自然的业务键,因此您需要使用如下的实体标识符:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof MailingAddress)) return false;
    MailingAddress ma = (MailingAddress) o;
    return getId() != null && Objects.equals(getId(), ma.getId());
}

@Override
public int hashCode() {
    return 31;
}

检查this article以了解为什么hashCode在这种情况下需要返回一个常量值。

但是,并非全部。

为什么使用TreeSet@OrderBy("age ASC")。订单在查询时给出,然后在Java中覆盖它。由于您使用@OrderBy,因此在执行SELECT语句时完成排序后使用List会更有意义。