mirror of https://github.com/apache/openjpa.git
OPENJPA-2475: A query with LEFT FETCH JOIN returns incorrect results - applied fix to trunk.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1580432 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
fa027f605b
commit
873daf3531
|
@ -221,11 +221,7 @@ public abstract class StoreCollectionFieldStrategy
|
||||||
joins = sel.outer(joins);
|
joins = sel.outer(joins);
|
||||||
if (!selectOid) {
|
if (!selectOid) {
|
||||||
Column[] refs = getJoinForeignKey(elem).getColumns();
|
Column[] refs = getJoinForeignKey(elem).getColumns();
|
||||||
if (requiresOrderBy()) {
|
sel.orderBy(refs, true, joins, true);
|
||||||
sel.orderBy(refs, true, joins, true);
|
|
||||||
} else {
|
|
||||||
sel.select(refs, joins);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
field.orderLocal(sel, elem, joins);
|
field.orderLocal(sel, elem, joins);
|
||||||
}
|
}
|
||||||
|
@ -630,10 +626,6 @@ public abstract class StoreCollectionFieldStrategy
|
||||||
return getJoinForeignKey(getDefaultElementMapping(false));
|
return getJoinForeignKey(getDefaultElementMapping(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean requiresOrderBy() {
|
|
||||||
return List.class.isAssignableFrom(field.getProxyType());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the identity value of the given instance that is suitable to join to the given foreign key.
|
* Gets the identity value of the given instance that is suitable to join to the given foreign key.
|
||||||
* The special case of the foreign key being a relation identifier will encode the value.
|
* The special case of the foreign key being a relation identifier will encode the value.
|
||||||
|
|
|
@ -39,7 +39,16 @@ public class TestMultipleLevelDerivedIdentity1 extends SQLListenerTestCase {
|
||||||
private static String BOOK_NAME = "foo";
|
private static String BOOK_NAME = "foo";
|
||||||
private static int NUM_PAGES = 3;
|
private static int NUM_PAGES = 3;
|
||||||
private static int NUM_LINES = 20;
|
private static int NUM_LINES = 20;
|
||||||
|
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
setSupportedDatabases(org.apache.openjpa.jdbc.sql.DerbyDictionary.class,
|
||||||
|
org.apache.openjpa.jdbc.sql.DB2Dictionary.class,
|
||||||
|
org.apache.openjpa.jdbc.sql.OracleDictionary.class);
|
||||||
|
|
||||||
|
if (isTestsDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
super.setUp(DROP_TABLES, Library1.class, Book1.class, Page1.class,
|
super.setUp(DROP_TABLES, Library1.class, Book1.class, Page1.class,
|
||||||
BookId1.class, PageId1.class, Line1.class, LineId1.class,
|
BookId1.class, PageId1.class, Line1.class, LineId1.class,
|
||||||
"openjpa.RuntimeUnenhancedClasses", "unsupported");
|
"openjpa.RuntimeUnenhancedClasses", "unsupported");
|
||||||
|
@ -215,7 +224,7 @@ public class TestMultipleLevelDerivedIdentity1 extends SQLListenerTestCase {
|
||||||
EntityManager em = emf.createEntityManager();
|
EntityManager em = emf.createEntityManager();
|
||||||
Library1 lib = em.find(Library1.class, LIBRARY_NAME);
|
Library1 lib = em.find(Library1.class, LIBRARY_NAME);
|
||||||
assertNotNull(lib);
|
assertNotNull(lib);
|
||||||
assertSQLFragnments(sql, "ORDER BY t1.LIBRARY_NAME ASC, t1.BOOK_NAME ASC");
|
assertSQLFragnments(sql, "ORDER BY", "t1.LIBRARY_NAME ASC, t1.BOOK_NAME ASC");
|
||||||
em.close();
|
em.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.jpql.joins.leftfetch;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OrderBy;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class DepartmentTest{
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private String primaryKey;
|
||||||
|
|
||||||
|
@OrderBy("name")
|
||||||
|
@OneToMany(mappedBy = "departmentTest")
|
||||||
|
private Set<PersonTest> persons = new HashSet<PersonTest>();
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Set<PersonTest> getPersons() {
|
||||||
|
return persons;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPersons(Set<PersonTest> persons) {
|
||||||
|
this.persons = persons;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrimaryKey() {
|
||||||
|
return this.primaryKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryKey(String primaryKey) {
|
||||||
|
this.primaryKey = primaryKey;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.jpql.joins.leftfetch;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.jdbc.ForeignKey;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class PersonTest {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private String primaryKey;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@ForeignKey
|
||||||
|
private DepartmentTest departmentTest;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public DepartmentTest getDepartmentTest() {
|
||||||
|
return departmentTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDepartmentTest(DepartmentTest departmentTest) {
|
||||||
|
this.departmentTest = departmentTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrimaryKey() {
|
||||||
|
return this.primaryKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryKey(String primaryKey) {
|
||||||
|
this.primaryKey = primaryKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(this.getName()).append(" - ").append(this.getPrimaryKey()).append(" ");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.jpql.joins.leftfetch;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAQuery;
|
||||||
|
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||||
|
|
||||||
|
public class TestJoinLeftFetch extends SingleEMFTestCase {
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
setUp(DROP_TABLES, DepartmentTest.class, PersonTest.class);
|
||||||
|
createTestData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test fails (prior to OJ-2475) because the
|
||||||
|
* DepartmentTests are not populated with the correct
|
||||||
|
* number of PersonTests
|
||||||
|
*/
|
||||||
|
public void testReadDepartmentsWithLeftJoinFetch() {
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
|
||||||
|
String qStrDIST = "SELECT DISTINCT dept FROM DepartmentTest "
|
||||||
|
+ "dept LEFT JOIN FETCH dept.persons";
|
||||||
|
|
||||||
|
Query query = em.createQuery(qStrDIST);
|
||||||
|
List<DepartmentTest> depts = query.getResultList();
|
||||||
|
verifySize(depts);
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verifySize(List<DepartmentTest> depts){
|
||||||
|
for (DepartmentTest department : depts) {
|
||||||
|
if (department.getPrimaryKey().equals("001")) {
|
||||||
|
// System.out.println("Dept: " + department.getName());
|
||||||
|
// Iterator i = department.getPersons().iterator();
|
||||||
|
// while (i.hasNext()){
|
||||||
|
// System.out.println("i.next() = " + i.next());
|
||||||
|
// }
|
||||||
|
assertEquals("Size should be 3", 3, department.getPersons().size());
|
||||||
|
}
|
||||||
|
if (department.getPrimaryKey().equals("002")) {
|
||||||
|
// System.out.println("Dept: " + department.getName());
|
||||||
|
// Iterator i = department.getPersons().iterator();
|
||||||
|
// while (i.hasNext()){
|
||||||
|
// System.out.println("i.next() = " + i.next());
|
||||||
|
// }
|
||||||
|
assertEquals("Size should be 2", 2, department.getPersons().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test works as expected.
|
||||||
|
*/
|
||||||
|
public void testReadDepartmentsWithFetchPlan() {
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
|
||||||
|
OpenJPAQuery<DepartmentTest> query = OpenJPAPersistence.cast(em.createQuery(" SELECT dept FROM "
|
||||||
|
+ " DepartmentTest dept "));
|
||||||
|
query.getFetchPlan().addField(DepartmentTest.class, "persons");
|
||||||
|
|
||||||
|
verifySize(query.getResultList());
|
||||||
|
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test works as expected.
|
||||||
|
*/
|
||||||
|
public void testReadDepartmentsWithLeftJoinFetchAndOrderBy() {
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
|
||||||
|
Query query = em.createQuery(" SELECT dept FROM " + " DepartmentTest dept "
|
||||||
|
+ " LEFT JOIN FETCH dept.persons ORDER BY dept.primaryKey");
|
||||||
|
verifySize(query.getResultList());
|
||||||
|
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createTestData() {
|
||||||
|
// NOTE: This test depends upon the the PersonTest
|
||||||
|
// to be un-ordered w.r.t the DepartmentTest FK.
|
||||||
|
// I've executed a flush after each entity creation
|
||||||
|
// in an attempt that the FKs will not be ordered.
|
||||||
|
// @OrderBy is used in the DepartmentTest in order
|
||||||
|
// to ensure things aren't orderd by the FK.
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
|
||||||
|
DepartmentTest dt1 = new DepartmentTest();
|
||||||
|
dt1.setPrimaryKey("001");
|
||||||
|
dt1.setName("Dept001");
|
||||||
|
em.persist(dt1);
|
||||||
|
|
||||||
|
DepartmentTest dt2 = new DepartmentTest();
|
||||||
|
dt2.setPrimaryKey("002");
|
||||||
|
dt2.setName("Dept002");
|
||||||
|
em.persist(dt2);
|
||||||
|
|
||||||
|
PersonTest pt = new PersonTest();
|
||||||
|
pt.setPrimaryKey("1");
|
||||||
|
pt.setName("John");
|
||||||
|
pt.setDepartmentTest(dt1);
|
||||||
|
em.persist(pt);
|
||||||
|
em.flush();
|
||||||
|
|
||||||
|
pt = new PersonTest();
|
||||||
|
pt.setPrimaryKey("2");
|
||||||
|
pt.setName("Mark");
|
||||||
|
pt.setDepartmentTest(dt1);
|
||||||
|
em.persist(pt);
|
||||||
|
em.flush();
|
||||||
|
|
||||||
|
pt = new PersonTest();
|
||||||
|
pt.setPrimaryKey("3");
|
||||||
|
pt.setName("Stuart");
|
||||||
|
pt.setDepartmentTest(dt2);
|
||||||
|
em.persist(pt);
|
||||||
|
em.flush();
|
||||||
|
|
||||||
|
pt = new PersonTest();
|
||||||
|
pt.setPrimaryKey("4");
|
||||||
|
pt.setName("Jim");
|
||||||
|
pt.setDepartmentTest(dt1);
|
||||||
|
em.persist(pt);
|
||||||
|
em.flush();
|
||||||
|
|
||||||
|
pt = new PersonTest();
|
||||||
|
pt.setPrimaryKey("5");
|
||||||
|
pt.setName("Fred");
|
||||||
|
pt.setDepartmentTest(dt2);
|
||||||
|
em.persist(pt);
|
||||||
|
em.flush();
|
||||||
|
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue