HHH-9597 : Criteria creates invalid column aliases (test case)

This commit is contained in:
Gail Badner 2015-02-12 13:23:27 -08:00
parent 264b0be40e
commit c9df7589f2
6 changed files with 440 additions and 10 deletions

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.criteria;
/**
* @author Gail Badner
*/
public class Address {
private int addressId;
public int getAddressId() {
return addressId;
}
private String addressText;
public String getAddressText() {
return addressText;
}
public void setAddressText(String addressText) {
this.addressText = addressText;
}
}

View File

@ -6,10 +6,15 @@
<id name="orderId" column="order_id" type="int" unsaved-value="0" access="field" > <id name="orderId" column="order_id" type="int" unsaved-value="0" access="field" >
<generator class="increment" /> <generator class="increment" />
</id> </id>
<many-to-one name="orderAddress" cascade="all"/>
<set name="orderLines" cascade="all-delete-orphan" access="field" inverse="true" fetch="select"> <set name="orderLines" cascade="all-delete-orphan" access="field" inverse="true" fetch="select">
<key column="order_id" /> <key column="order_id" />
<one-to-many class="OrderLine" /> <one-to-many class="OrderLine" />
</set> </set>
<set name="orderContacts" cascade="all-delete-orphan" access="field" fetch="select">
<key column="order_id" />
<many-to-many class="OrderContact" />
</set>
</class> </class>
<class name="OrderLine" table="order_line"> <class name="OrderLine" table="order_line">
<id name="lineId" column="order_line_id" type="int" unsaved-value="0" access="field" > <id name="lineId" column="order_line_id" type="int" unsaved-value="0" access="field" >
@ -18,5 +23,31 @@
<many-to-one name="order" column="order_id" class="Order" /> <many-to-one name="order" column="order_id" class="Order" />
<property name="articleId" column="article_id" type="string" /> <property name="articleId" column="article_id" type="string" />
</class> </class>
<class name="OrderContact" table="order_contact">
<id name="contactId" column="order_contact_id" type="int" unsaved-value="0" access="field" >
<generator class="increment" />
</id>
<property name="contact" column="contact" type="string" />
<set name="orders" access="field" inverse="true" fetch="select">
<key column="contact_id" />
<many-to-many class="Order" />
</set>
</class>
<class name="OrderAddress">
<id name="orderAddressId" type="int" unsaved-value="0" access="field" >
<generator class="increment" />
</id>
<many-to-one name="deliveryAddress" class="Address" cascade="all" />
<set name="notifiedAddresses" cascade="all" inverse="false" fetch="select" >
<key column="orderAddressId"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="addressId" column="address_id" type="int" unsaved-value="0" access="field" >
<generator class="increment" />
</id>
<property name="addressText"/>
</class>
</hibernate-mapping> </hibernate-mapping>

View File

@ -29,24 +29,45 @@ import java.util.Set;
public class Order { public class Order {
private int orderId; private int orderId;
public int getOrderId() { public int getOrderId() {
return orderId; return orderId;
} }
private Set<OrderLine> orderLines = new HashSet<OrderLine>(); private Set<OrderLine> orderLines = new HashSet<OrderLine>();
public Set<OrderLine> getLines() { public Set<OrderLine> getLines() {
return Collections.unmodifiableSet(orderLines); return Collections.unmodifiableSet(orderLines);
} }
public void addLine(OrderLine orderLine){ public void addLine(OrderLine orderLine){
orderLine.setOrder(this); orderLine.setOrder(this);
this.orderLines.add(orderLine); this.orderLines.add(orderLine);
} }
public String toString() { private Set<OrderContact> orderContacts = new HashSet<OrderContact>();
public Set<OrderContact> getContacts() {
return Collections.unmodifiableSet(orderContacts);
}
public void addContact(OrderContact orderContact){
orderContact.getOrders().add( this );
this.orderContacts.add(orderContact);
}
public OrderAddress orderAddress;
public OrderAddress getOrderAddress() {
return orderAddress;
}
public void setOrderAddress(OrderAddress orderAddress) {
this.orderAddress = orderAddress;
}
public String toString() {
return "" + getOrderId() + " - " + getLines(); return "" + getOrderId() + " - " + getLines();
} }
} }

View File

@ -0,0 +1,60 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.criteria;
import java.util.HashSet;
import java.util.Set;
public class OrderAddress {
private int orderAddressId;
public int getOrderAddressId() {
return orderAddressId;
}
private Address deliveryAddress;
public Address getDeliveryAddress() {
return deliveryAddress;
}
public void setDeliveryAddress(Address deliveryAddress) {
this.deliveryAddress = deliveryAddress;
}
private Set<Address> notifiedAddresses = new HashSet<Address>();
public Set<Address> getNotifiedAddresses() {
return notifiedAddresses;
}
public void setNotifiedAddresses(Set<Address> notifiedAddresses) {
this.notifiedAddresses = notifiedAddresses;
}
public String toString() {
return "" + orderAddressId + " - " + getDeliveryAddress() + " - " + getNotifiedAddresses();
}
}

View File

@ -0,0 +1,56 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.criteria;
import java.util.HashSet;
import java.util.Set;
public class OrderContact {
private int contactId = 0;
private Set<Order> orders = new HashSet<Order>();
private String contact;
public int getContactId() {
return contactId;
}
public Set<Order> getOrders() {
return orders;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
public String toString() {
return "[" + getContactId() + ":" + getContact() + "]";
}
}

View File

@ -30,9 +30,13 @@ import java.util.Map;
import org.junit.Test; import org.junit.Test;
import org.hibernate.Criteria; import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinFragment; import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -381,6 +385,190 @@ public class OuterJoinCriteriaTest extends BaseCoreFunctionalTestCase {
s.close(); s.close();
} }
@Test
@TestForIssue( jiraKey = "HHH-9597")
public void testMultipleSubCriteriaRestrictionsOnCollections() {
Session s = openSession();
s.getTransaction().begin();
Criteria rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createCriteria( "order.orderLines", "line", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("line.articleId", "3000"));
rootCriteria.createCriteria( "order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("contact.contact", "Contact1"));
// result should be order1, because that's the only Order with:
// 1) orderLines containing an OrderLine with articleId == "3000"
// and 2) orderContacts containing an OrderContact with contact == "Contact1"
// Since both restrictions are in subcriteria, both collections should be non-filtered
// (i.e. includes all elements in both collections)
Order result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 2, result.getContacts().size() );
assertEquals( 2, result.getLines().size() );
s.getTransaction().commit();
s.close();
}
@Test
@TestForIssue( jiraKey = "HHH-9597")
public void testMultipleRootCriteriaRestrictionsOnCollections() {
Session s = openSession();
s.getTransaction().begin();
Criteria rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createAlias( "order.orderLines", "line", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("line.articleId", "3000"));
rootCriteria.createAlias("order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN)
.add( Restrictions.eq( "contact.contact", "Contact1" ) );
// result should be order1, because that's the only Order with:
// 1) orderLines containing an OrderLine with articleId == "3000"
// and 2) orderContacts containing an OrderContact with contact == "Contact1"
// Since both restrictions are in root criteria, both collections should be filtered
// to contain only the elements that satisfy the restrictions.
Order result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 1, result.getLines().size() );
assertEquals( "3000", result.getLines().iterator().next().getArticleId() );
assertEquals( 1, result.getContacts().size() );
assertEquals( "Contact1", result.getContacts().iterator().next().getContact() );
s.getTransaction().commit();
s.close();
}
@Test
@TestForIssue( jiraKey = "HHH-9597")
public void testRootAndSubCriteriaRestrictionsOnCollections() {
// the result of all Criteria in this test will be order1, because that's the only Order with:
// 1) orderLines containing an OrderLine with articleId == "3000"
// and 2) orderContacts containing an OrderContact with contact == "Contact1"
Session s = openSession();
s.getTransaction().begin();
Criteria rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createCriteria("order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN)
.add( Restrictions.eq( "contact.contact", "Contact1" ) );
rootCriteria.createAlias("order.orderLines", "line", JoinType.LEFT_OUTER_JOIN)
.add(Restrictions.eq("line.articleId", "3000"));
// Since restriction on orderLines is on root criteria, that collection should be filtered.
// Since restriction on orderContacts is on a subcriteria, that collection should be
// non-filtered (contain all its elements)
Order result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 1, result.getLines().size() );
assertEquals( "3000", result.getLines().iterator().next().getArticleId() );
assertEquals( 2, result.getContacts().size() );
s.getTransaction().commit();
s.close();
// The following should have the same result as the previous, since it has the same
// restrictions applied in reverse order.
s = openSession();
s.getTransaction().begin();
rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createCriteria("order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN)
.add(Restrictions.eq("contact.contact", "Contact1"));
rootCriteria.createAlias( "order.orderLines", "line", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("line.articleId", "3000"));
// Since restriction on orderLines is on root criteria, that collection should be filtered.
// Since restriction on orderContacts is on a subcriteria, that collection should be
// non-filtered (contain all its elements)
result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 1, result.getLines().size() );
assertEquals( "3000", result.getLines().iterator().next().getArticleId() );
assertEquals( 2, result.getContacts().size() );
s.getTransaction().commit();
s.close();
// Even though the following seem redundant, there was a failure due to HHH-9597
// that reproduced when filtering Order.orderContacts, but not order.orderLines.
s = openSession();
s.getTransaction().begin();
rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createAlias( "order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("contact.contact", "Contact1"));
rootCriteria.createCriteria( "order.orderLines", "line", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("line.articleId", "3000"));
// Since restriction on orderContacts is on root criteria, that collection should be filtered.
// Since restriction on orderLines is on a subcriteria, that collection should be
// non-filtered (contain all its elements)
result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 2, result.getLines().size() );
assertEquals( 1, result.getContacts().size() );
assertEquals( "Contact1", result.getContacts().iterator().next().getContact() );
s.getTransaction().commit();
s.close();
// The following should have the same result as the previous, since it has the same
// restrictions applied in reverse order.
s = openSession();
s.getTransaction().begin();
rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createCriteria( "order.orderLines", "line", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("line.articleId", "3000"));
rootCriteria.createAlias( "order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN )
.add(Restrictions.eq("contact.contact", "Contact1"));
result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 2, result.getLines().size() );
assertEquals( 1, result.getContacts().size() );
assertEquals( "Contact1", result.getContacts().iterator().next().getContact() );
s.getTransaction().commit();
s.close();
}
@Test
@TestForIssue( jiraKey = "HHH-9597")
public void testSubCriteriaRestrictionsOnCollectionsNestedInManyToOne() {
// the result of all Criteria in this test will be order1, because that's the only Order with:
// 1) orderContacts containing an OrderContact with contact == "Contact1"
// and 2) orderAddress.notifiedAddresses containing an Address with addressText == "over the rainbow"
Session s = openSession();
s.getTransaction().begin();
Criteria rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createCriteria("order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN)
.add( Restrictions.eq( "contact.contact", "Contact1" ) );
rootCriteria.createCriteria( "order.orderAddress", "orderAddress", JoinType.LEFT_OUTER_JOIN )
.createCriteria( "orderAddress.notifiedAddresses", "notifiedAddress", JoinType.LEFT_OUTER_JOIN )
.add( Restrictions.eq( "notifiedAddress.addressText", "over the rainbow" ) );
// Since restrictions are on subcriteria, the collections should n on orderLines is on root criteria, that collection should be filtered.
// Since restriction on orderContacts is on a subcriteria, that collection should be
// non-filtered (contain all its elements)
Order result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 2, result.getContacts().size() );
assertEquals( 2, result.getOrderAddress().getNotifiedAddresses().size() );
s.getTransaction().commit();
s.close();
// The following should have the same result as the previous, since it has the same
// restrictions applied in reverse order.
s = openSession();
s.getTransaction().begin();
rootCriteria = s.createCriteria(Order.class, "order");
rootCriteria.createCriteria( "order.orderAddress", "orderAddress", JoinType.LEFT_OUTER_JOIN )
.createCriteria( "orderAddress.notifiedAddresses", "notifiedAddress", JoinType.LEFT_OUTER_JOIN )
.add( Restrictions.eq( "notifiedAddress.addressText", "over the rainbow" ) );
rootCriteria.createCriteria("order.orderContacts", "contact", JoinType.LEFT_OUTER_JOIN)
.add( Restrictions.eq( "contact.contact", "Contact1" ) );
// Since restrictions are on subcriteria, the collections should n on orderLines is on root criteria, that collection should be filtered.
// Since restriction on orderContacts is on a subcriteria, that collection should be
// non-filtered (contain all its elements)
result = (Order) rootCriteria.uniqueResult();
assertEquals( order1.getOrderId(), result.getOrderId() );
assertEquals( 2, result.getContacts().size() );
assertEquals( 2, result.getOrderAddress().getNotifiedAddresses().size() );
s.getTransaction().commit();
s.close();
}
protected void prepareTest() { protected void prepareTest() {
Session s = openSession(); Session s = openSession();
s.getTransaction().begin(); s.getTransaction().begin();
@ -395,8 +583,30 @@ public class OuterJoinCriteriaTest extends BaseCoreFunctionalTestCase {
order1.addLine( line ); order1.addLine( line );
s.persist( order1 ); s.persist( order1 );
OrderContact contact = new OrderContact();
contact.setContact( "Contact1" );
order1.addContact( contact );
contact = new OrderContact();
contact.setContact( "Contact2" );
order1.addContact( contact );
OrderAddress orderAddress = new OrderAddress();
Address address = new Address();
address.setAddressText( "over the rainbow" );
orderAddress.setDeliveryAddress( address );
Address otherAddress = new Address();
otherAddress.setAddressText( "other place" );
orderAddress.getNotifiedAddresses().add( address );
orderAddress.getNotifiedAddresses().add( otherAddress );
order1.setOrderAddress( orderAddress );
s.persist( order1 );
// Order with no lines // Order with no lines
order2 = new Order(); order2 = new Order();
contact = new OrderContact();
contact.setContact( "Contact1" );
order2.addContact( contact );
s.persist( order2 ); s.persist( order2 );
// Order with non-matching line // Order with non-matching line
@ -414,6 +624,13 @@ public class OuterJoinCriteriaTest extends BaseCoreFunctionalTestCase {
Session s = openSession(); Session s = openSession();
s.getTransaction().begin(); s.getTransaction().begin();
List orders = s.createQuery( "from Order" ).list();
for( Object order : orders ) {
s.delete( order );
}
s.createQuery( "delete from OrderContact" ).executeUpdate();
s.createQuery( "delete from OrderLine" ).executeUpdate(); s.createQuery( "delete from OrderLine" ).executeUpdate();
s.createQuery( "delete from Order" ).executeUpdate(); s.createQuery( "delete from Order" ).executeUpdate();