使用休眠获取重复条目

2022-09-04 08:17:49

我在使用JPA / Hibernate时遇到了一个奇怪的问题。我在测试期间看到重复的条目。

这是我的基实体类:

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity {
    
    @Id
    @Column(name = "ID", updatable = false, nullable = false)
    @GenericGenerator(name = "uniqueIDGenerator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
            @org.hibernate.annotations.Parameter(name = "sequence_name", value = "ID_SEQUENCE"),
            @org.hibernate.annotations.Parameter(name = "increment_size", value = "100"),
            @org.hibernate.annotations.Parameter(name = "optimizer ", value = "pooled") })
    @GeneratedValue(generator = "uniqueIDGenerator")
    @NotNull
    protected int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
    
}

这是我的主要合作伙伴课程:

@Entity
@Table(name = "partner")
public class Partner extends BaseEntity{
    
    @Column(name = "name")
    private String name;
    
    @OneToMany(mappedBy="partner", fetch=FetchType.EAGER)
    @Cascade(CascadeType.DELETE)
    private List<Receipt> receipts;
    
    @OneToMany(mappedBy="partner", fetch=FetchType.EAGER)
    @Cascade(CascadeType.DELETE)
    private List<Delivery> deliveries;
    
    public Partner() {
        setReceipts(new ArrayList<Receipt>());
        setDeliveries(new ArrayList<Delivery>());
    }
    
    public Partner(String name){
        this();
        
        this.setName(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Receipt> getReceipts() {
        return receipts;
    }

    public void setReceipts(List<Receipt> receipts) {
        this.receipts = receipts;
    }

    public List<Delivery> getDeliveries() {
        return deliveries;
    }

    public void setDeliveries(List<Delivery> deliveries) {
        this.deliveries = deliveries;
    }
}

合作伙伴有两个子类,收货和交货。下面是收据类:

@Entity
@Table(name = "receipt")
public class Receipt extends BaseEntity{
    
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="partnerId")
    private Partner partner;
    
    @Column(name = "name")
    private String name;
    @Column(name = "json")
    private String json;

    @Transient
    private ReceiptContainer receiptContainer;
    
    
    public Receipt() {
    }
    public Receipt(String name){
        this();
        
        this.setName(name);
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getJson() {
        return json;
    }

    public void setJson(String json) {
        this.json = json;
    }

    public ReceiptContainer getReceiptContainer() {
        return receiptContainer;
    }

    public void setReceiptContainer(ReceiptContainer receiptContainer) {
        this.receiptContainer = receiptContainer;
    }

    public Partner getPartner() {
        return partner;
    }

    public void setPartner(Partner partner) {
        this.partner = partner;
    }
    
}

下面是交付类:

@Entity
@Table(name = "delivery")
public class Delivery extends BaseEntity{
    
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="partnerId")
    private Partner partner;
    
    @Column(name = "name")
    private String name;
    @Column(name = "json")
    private String json;

    @Transient
    private DeliveryContainer deliveryContainer;
    
    
    public Delivery() {
    }
    public Delivery(String name){
        this();
        
        this.setName(name);
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getJson() {
        return json;
    }

    public void setJson(String json) {
        this.json = json;
    }

    public DeliveryContainer getDeliveryContainer() {
        return deliveryContainer;
    }

    public void setDeliveryContainer(DeliveryContainer deliveryContainer) {
        this.deliveryContainer = deliveryContainer;
    }

    public Partner getPartner() {
        return partner;
    }

    public void setPartner(Partner partner) {
        this.partner = partner;
    }
    
}

我得到的对象是这样的:

@Transactional
public Partner get(int id){
    Partner partner = entityManager.find(Partner.class, id);
    return partner;
}

如您所见,伙伴类包括对象列表和对象列表,它们分别存储在自己的表、收据和传递中。

---在我进一步讨论之前,我想指出,数据库中显示了正确的条目数。仅当获取我的 Java 应用程序中的对象时,才会发生此问题。---

当我添加一个对象并且没有对象存在时,将显示一个对象,而不显示任何对象,这是预期的。如果存在一个对象和一个对象,则再次显示一个对象和一个对象,这是预期的。当存在多个或对象时,会出现此问题。例如,如果添加了两个对象,但只添加了一个对象,则会显示两个对象和两个对象。

为了更好地说明这一点,请参阅下面的测试用例表。意外结果用破折号括起来。

<Receipt> Obj Added | <Delivery> Obj Added | <Receipt> Obj Displayed | <Delivery> Obj Displayed
         0          |          0           |           0             |            0
         0          |          1           |           0             |            1
         1          |          0           |           1             |            0
         1          |          1           |           1             |            1
         1          |          2           |        ---2---          |         ---2---
         2          |          1           |        ---2---          |         ---2---
         2          |          2           |        ---4---          |         ---4---

我的问题是,为什么我会看到这些意想不到的结果?


答案 1

您看到重复项的原因是,您急切地获取子项,即创建外连接。

(您可以使用@Fetch(FetchMode.SELECT),但这将导致额外的查询来检索您的项目)

休眠 API 引用,指示发生外部连接。

以下是对您问题的深入回答:Hibernate Criteria使用FetchType多次返回子项。

您可以从“列表”切换到“设置”(如果这样做,请确保覆盖 equals/compareTo/hashCode)。或者,如果通过条件从数据库中提取这些项目,则可以将结果转换器添加到查询结果中。

就其价值而言,休眠建议使用集合而不是列表


答案 2

也许您可以尝试使用 DISTINCT 属性执行查询。

只需将其添加到您的标准中:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

如果未使用条件:

SELECT DISTINCT <QUERY>

如果它不能解决您的问题,请显示您的查询代码。


推荐