diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/Address.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/Address.java new file mode 100644 index 0000000000..34bc0d192b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/Address.java @@ -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; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.hbm.xml index 73652724ba..8842bfae5e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.hbm.xml @@ -6,10 +6,15 @@ + + + + + @@ -18,5 +23,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.java index e6998e35de..1f63110007 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.java +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/Order.java @@ -29,24 +29,45 @@ import java.util.Set; public class Order { - private int orderId; + private int orderId; - public int getOrderId() { + public int getOrderId() { return orderId; } - private Set orderLines = new HashSet(); + private Set orderLines = new HashSet(); - public Set getLines() { + public Set getLines() { return Collections.unmodifiableSet(orderLines); } - public void addLine(OrderLine orderLine){ - orderLine.setOrder(this); - this.orderLines.add(orderLine); - } - - public String toString() { + public void addLine(OrderLine orderLine){ + orderLine.setOrder(this); + this.orderLines.add(orderLine); + } + + private Set orderContacts = new HashSet(); + + public Set 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(); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/OrderAddress.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/OrderAddress.java new file mode 100644 index 0000000000..fae6e5e477 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/OrderAddress.java @@ -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
notifiedAddresses = new HashSet
(); + + public Set
getNotifiedAddresses() { + return notifiedAddresses; + } + + public void setNotifiedAddresses(Set
notifiedAddresses) { + this.notifiedAddresses = notifiedAddresses; + } + + public String toString() { + return "" + orderAddressId + " - " + getDeliveryAddress() + " - " + getNotifiedAddresses(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/OrderContact.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/OrderContact.java new file mode 100644 index 0000000000..941e5d9e9f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/OrderContact.java @@ -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 orders = new HashSet(); + + private String contact; + + + public int getContactId() { + return contactId; + } + + public Set getOrders() { + return orders; + } + + public String getContact() { + return contact; + } + + public void setContact(String contact) { + this.contact = contact; + } + + public String toString() { + return "[" + getContactId() + ":" + getContact() + "]"; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/criteria/OuterJoinCriteriaTest.java b/hibernate-core/src/test/java/org/hibernate/test/criteria/OuterJoinCriteriaTest.java index e17e6a150a..8ec996d993 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/criteria/OuterJoinCriteriaTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/criteria/OuterJoinCriteriaTest.java @@ -30,9 +30,13 @@ import java.util.Map; import org.junit.Test; import org.hibernate.Criteria; +import org.hibernate.FetchMode; import org.hibernate.Session; +import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.Restrictions; import org.hibernate.sql.JoinFragment; +import org.hibernate.sql.JoinType; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; @@ -381,6 +385,190 @@ public class OuterJoinCriteriaTest extends BaseCoreFunctionalTestCase { 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() { Session s = openSession(); s.getTransaction().begin(); @@ -395,8 +583,30 @@ public class OuterJoinCriteriaTest extends BaseCoreFunctionalTestCase { order1.addLine( line ); 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 order2 = new Order(); + contact = new OrderContact(); + contact.setContact( "Contact1" ); + order2.addContact( contact ); s.persist( order2 ); // Order with non-matching line @@ -414,6 +624,13 @@ public class OuterJoinCriteriaTest extends BaseCoreFunctionalTestCase { Session s = openSession(); 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 Order" ).executeUpdate();