-->
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.  [ 2 posts ] 
Author Message
 Post subject: LEFT OUTER JOIN subcriteria filters children - bug???
PostPosted: Fri Sep 01, 2006 6:32 am 
Regular
Regular

Joined: Thu Nov 13, 2003 2:55 am
Posts: 71
Location: Sweden
When using the Criteria API with LEFT OUTER JOIN to add criterias to the children, the collections will only contain those children matching the criteria. (I am *not* using any filters)

Why is this?
Am I doing something wrong, or is this a bug?
Is there a workaround?

Hibernate version: 3.1.3

Name and version of the database you are using: hsqldb

Mapping documents:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
  <class name="foo.Order" table="t_order">
    <id name="orderId" column="order_id" type="int" unsaved-value="0" access="field" >
      <generator class="identity" />
    </id>
    <set name="orderLines" cascade="all-delete-orphan" access="field" inverse="true" fetch="select">
      <key column="order_id" />
      <one-to-many class="foo.OrderLine" />
    </set>
  </class>
  <class name="foo.OrderLine" table="order_line">
    <id name="lineId" column="order_line_id" type="int" unsaved-value="0" access="field" >
      <generator class="identity" />
    </id>
    <many-to-one name="order" column="order_id" class="foo.Order" />
    <property name="articleId" column="article_id" type="string" />
  </class>
</hibernate-mapping>

Entity classes
Code:
package foo;

import java.util.*;

public class Order {

  private int orderId;

  public int getOrderId() {
    return orderId;
  }

  private Set<OrderLine> orderLines = new HashSet<OrderLine>();

  public Set<OrderLine> getLines() {
    return Collections.unmodifiableSet(orderLines);
  }

  public void addLine(OrderLine orderLine){
    orderLine.setOrder(this);
    this.orderLines.add(orderLine);
  }
 
  public String toString() {
    return "" + getOrderId() + " - " + getLines();
  }
}

Code:
package foo;

public class OrderLine {

  private int lineId = 0;
 
  private Order order;
 
  private String articleId;

 
  public int getLineId() {
    return lineId;
  }

  public Order getOrder() {
    return order;
  } 

  public String getArticleId() {
    return articleId;
  }

  public void setOrder(Order order) {
    this.order = order;
  }

  public void setArticleId(String articleId) {
    this.articleId = articleId;
  }
 
  public String toString() {
    return "[" + getLineId() + ":" + getArticleId() + "]";
  }
}


Test case to show the problem:
Code:
package foo;

import org.hibernate.test.TestCase;
import org.hibernate.Session;
import org.hibernate.Criteria;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Expression;
import org.hibernate.sql.JoinFragment;

import java.util.List;

public class SubcriteriaTest extends TestCase {
 
  public SubcriteriaTest(String s) {
    super(s);
  }

  protected String getBaseForMappings() {
    return "foo/";
  }

  protected String[] getMappings() {
    return new String[] { "Order.hbm.xml" };
  }

  public void testSubcriteria() {
    Session s = openSession();
    s.getTransaction().begin();
   
    // Order with one mathing line and one non-matching
    Order order = new Order();
    OrderLine line = new OrderLine();
    line.setArticleId("1000");
    order.addLine(line);
    line = new OrderLine();
    line.setArticleId("3000");
    order.addLine(line);
    s.persist(order);
   
    // Order with no lines
    order = new Order();
    s.persist(order);
   
    // Order with non-matching line
    order = new Order();
    line = new OrderLine();
    line.setArticleId("3000");
    order.addLine(line);
    s.persist(order);
   
    s.flush();
    s.clear();
   

    // Get order, to verify persist()
    order = (Order) s.get(Order.class, 1);
    assertEquals("Order 1 has 2 lines", 2, order.getLines().size());
    s.clear(); // (Removing this makes the problem disappear!!!)


    // Search orders with specified article
    List orders = findOrders(s, "3000", "3000");
    assertEquals(2, orders.size());
    order = (Order) orders.get(0);
    assertEquals("Order 1 has 2 lines", 2, order.getLines().size()); // FAILS! Only contains matching line
   

    // Search order with no lines, or 
    orders = findOrders(s, null, "1000");
    assertEquals(2, orders.size());
    order = (Order) orders.get(1);
    assertTrue("No lines on order no 2", order.getLines().isEmpty());
    order = (Order) orders.get(0);
    assertEquals("Order 1 has 2 lines", 2, order.getLines().size()); // FAILS! Only contains matching line

    s.close();
  }
 
  /**
   * Find orders by criteria in search form.
   * @param articleIdFrom Start of selection interval. If not provided, include orders
   * with no lines.
   * @param articleIdto End of selection interval.
   */
  private List findOrders(Session s, String articleIdFrom, String articleIdto) {
    Criteria criteria = s.createCriteria(Order.class);
    // criteria.setFetchMode("orderLines", FetchMode.SELECT); // Note: Does not help
    // Note: JoinFragment.LEFT_OUTER_JOIN must be used to include orders with no lines
    Criteria linesCritera = criteria.createCriteria("orderLines", JoinFragment.LEFT_OUTER_JOIN);

    /* Same result:
    criteria.createAlias("orderLines", "lines", JoinFragment.LEFT_OUTER_JOIN);
    */
   
    if(! isBlank(articleIdFrom) || ! isBlank(articleIdto)) {
      if(! isBlank(articleIdFrom)) {
        linesCritera.add(Expression.ge("articleId", articleIdFrom)); // >=
        if(! isBlank(articleIdto)) // To also
          linesCritera.add(Expression.le("articleId", articleIdto)); // <=
      }
      else if(! isBlank(articleIdto)) { // Only to
        linesCritera.add(Expression.or(
          Expression.isNull("articleId"),          // Allow null
          Expression.le("articleId", articleIdto)) // <=
        );
      }
    }
   
    criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); // Needed b.o. left join

    return criteria.list();
   
  }
 
  private static boolean isBlank(String s) {
    return s == null || s.trim().length() == 0;
  }
}


Top
 Profile  
 
 Post subject: JIRA
PostPosted: Fri Aug 10, 2007 1:14 am 
Regular
Regular

Joined: Thu Nov 13, 2003 2:55 am
Posts: 71
Location: Sweden
For those stumpling across this, here is the link to the JIRA issue: http://opensource.atlassian.com/projects/hibernate/browse/HHH-2049


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 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.