-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Duplicate objects returned in join fetch of composite elem
PostPosted: Wed Jan 31, 2007 5:43 pm 
Newbie

Joined: Wed Jan 24, 2007 11:25 am
Posts: 2
Hi,

I am fairly new to hibernate but have searched extensively to find my answer and came up short. I assume I am making some simple configuration mistake.

I have narrowed this down to a simple Invoice type application to make it easy for everyone to understand, though the code is obviously unrealistic.

I have mapped the invoice and its line items in a single mapping file using composite-element. The mapping file is as follows.

Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.ftid.nplusone">
   <class name="Invoice" table="INVOICE" lazy="false" dynamic-update="true">
         <id name="invoiceId" type="long" column="INVOICE_ID"/>
        <property name="customerName" type="string">
            <column name="CUSTOMER_NAME"></column>
        </property>
        <set name="lineItems" table="INVOICE_ITEM" lazy="false">
            <key column="INVOICE_ID"/>
            <composite-element class="com.ftid.nplusone.InvoiceItem">
                <property name="sequenceNumber" column="SEQ" type="long"/>
                <property name="sku" column="SKU" type="string"/>
                <property name="description" column="DESCRIPTION" type="string"/>
            </composite-element>
        </set>
    </class>
</hibernate-mapping>


My java code looks like this:

Code:
package com.ftid.nplusone;

import java.util.Set;
import java.io.Serializable;

/**
* Invoice is an obviously contrived example of a common and simple concept.
*/
public class Invoice implements Serializable, Comparable<Invoice> {
    private long invoiceId;
    private String customerName;
    private Set<InvoiceItem> lineItems;

    public long getInvoiceId() {
        return invoiceId;
    }

    public void setInvoiceId(long invoiceId) {
        this.invoiceId = invoiceId;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    public Set<InvoiceItem> getLineItems() {
        return lineItems;
    }

    public void setLineItems(Set<InvoiceItem> lineItems) {
        this.lineItems = lineItems;
    }

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

        Invoice invoice = (Invoice) o;

        if (invoiceId != invoice.invoiceId) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        return (int) (invoiceId ^ (invoiceId >>> 32));
    }

    public int compareTo(Invoice that) {
        int retVal = 0;
        if (this.invoiceId < that.invoiceId) {
            retVal = -1;
        }
        else if (this.invoiceId > that.invoiceId) {
            retVal = 1;
        }
        return retVal;  //TODO: Implement this method.
    }
}


Code:
package com.ftid.nplusone;

import java.io.Serializable;

/**
* InvoiceItem is a line item for an invoice in this simplistic example.
*/
public class InvoiceItem implements Serializable, Comparable<InvoiceItem> {
    private long invoiceId;
    private long sequenceNumber;
    private String sku;
    private String description;

    public long getInvoiceId() {
        return invoiceId;
    }

    public void setInvoiceId(long invoiceId) {
        this.invoiceId = invoiceId;
    }

    public long getSequenceNumber() {
        return sequenceNumber;
    }

    public void setSequenceNumber(long sequenceNumber) {
        this.sequenceNumber = sequenceNumber;
    }

    public String getSku() {
        return sku;
    }

    public void setSku(String sku) {
        this.sku = sku;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

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

        InvoiceItem that = (InvoiceItem) o;

        if (invoiceId != that.invoiceId) {
            return false;
        }
        if (sequenceNumber != that.sequenceNumber) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        int result;
        result = (int) (invoiceId ^ (invoiceId >>> 32));
        result = 31 * result + (int) (sequenceNumber ^ (sequenceNumber >>> 32));
        return result;
    }

    public int compareTo(InvoiceItem that) {
        int retVal = 0;
        if (this.invoiceId < that.invoiceId) {
            retVal = -1;
        }
        else if (this.invoiceId > that.invoiceId) {
            retVal = 1;
        }
        else {
            if (this.sequenceNumber < that.sequenceNumber) {
                retVal = -1;
            }
            else if (this.sequenceNumber > that.sequenceNumber) {
                retVal = 1;
            }
        }
        return retVal;
    }
}


I use the followiing query for the fetch.

Code:
select i from Invoice i left outer join fetch i.lineItems lineItems


The problem I have is that for any invoice with N line items, I get N Invoice instances, each fully formed with the complete list of line items.

Note that I am using lazy="false".

Any help you can provide to get this to return a single fully formed instance of each Invoice would be greatly appreciated.

Scott


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 31, 2007 5:46 pm 
Regular
Regular

Joined: Mon Nov 14, 2005 7:33 pm
Posts: 73
Try this query instead:

Code:
select distinct i from Invoice i left join fetch i.lineItems


Top
 Profile  
 
 Post subject: Why??!!?
PostPosted: Wed Jan 31, 2007 6:01 pm 
Newbie

Joined: Wed Jan 24, 2007 11:25 am
Posts: 2
tsar bomba,

Thank you for the help. That does indeed fix the problem. When I run the generated query without hibernate, the result is identical. Can you explain why this made a difference?

Thanks,
Scott


Top
 Profile  
 
 Post subject: Re: Why??!!?
PostPosted: Wed Jan 31, 2007 6:10 pm 
Regular
Regular

Joined: Mon Nov 14, 2005 7:33 pm
Posts: 73
scottsobel wrote:
tsar bomba,

Thank you for the help. That does indeed fix the problem. When I run the generated query without hibernate, the result is identical. Can you explain why this made a difference?

Thanks,
Scott


Np! A one-to-many join on any table will yield "duplicates" simply because you're seeing multiple parent records as there are multiple child records.

In JPA-QL, using "distinct" will ensure you fetch distinct parents and their respective child records as you would expect. I can't speak to the internal mechanics...but the EJB3 folks were smart enough to foresee the need for this convenience.

The SQL query, run manually against the database gave you the same results because the original query was returning distinct parent records, but gives the appearance of "duplicates" due to the joining to multiple child records.

Hopefully I'm not being redundant, redundant. ;)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.