diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java index 03981b8a6..ea7375978 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java @@ -112,7 +112,7 @@ public class QueryCacheStoreQuery * READ_SERIALIZABLE -- to do so, we'd just return false when in * a transaction. */ - private List checkCache(QueryKey qk) { + private List checkCache(QueryKey qk, FetchConfiguration loadFc) { if (qk == null) return null; FetchConfiguration fetch = getContext().getFetchConfiguration(); @@ -161,7 +161,7 @@ public class QueryCacheStoreQuery return null; } } - return new CachedList(res, projs != 0, _sctx); + return new CachedList(res, projs != 0, _sctx, loadFc); } /** @@ -179,7 +179,7 @@ public class QueryCacheStoreQuery /** * Copy a projection element for caching / returning. */ - private static Object copyProjection(Object obj, StoreContext ctx) { + private static Object copyProjection(Object obj, StoreContext ctx, FetchConfiguration fc) { if (obj == null) return null; switch (JavaTypes.getTypeCode(obj.getClass())) { @@ -202,7 +202,7 @@ public class QueryCacheStoreQuery return ((Locale) obj).clone(); default: if (obj instanceof CachedObjectId) - return fromObjectId(((CachedObjectId) obj).oid, ctx); + return fromObjectId(((CachedObjectId) obj).oid, ctx, fc); Object oid = ctx.getObjectId(obj); if (oid != null) return new CachedObjectId(oid); @@ -213,11 +213,11 @@ public class QueryCacheStoreQuery /** * Return the result object based on its cached oid. */ - private static Object fromObjectId(Object oid, StoreContext sctx) { + private static Object fromObjectId(Object oid, StoreContext sctx, FetchConfiguration fc) { if (oid == null) return null; - Object obj = sctx.find(oid, null, null, null, 0); + Object obj = sctx.find(oid, fc, null, null, 0); if (obj == null) throw new ObjectNotFoundException(oid); return obj; @@ -324,18 +324,42 @@ public class QueryCacheStoreQuery _fc = fc; } - public ResultObjectProvider executeQuery(StoreQuery q, Object[] params, - Range range) { + public ResultObjectProvider executeQuery(StoreQuery q, Object[] params, Range range) { QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q; - QueryKey key = QueryKey.newInstance(cq.getContext(), - _ex.isPacking(q), params, _candidate, _subs, range.start, - range.end); - List cached = cq.checkCache(key); - if (cached != null) - return new ListResultObjectProvider(cached); - ResultObjectProvider rop = _ex.executeQuery(cq.getDelegate(), - params, range); + QueryKey key = + QueryKey.newInstance(cq.getContext(), _ex.isPacking(q), params, _candidate, _subs, range.start, + range.end); + + // Create a new FetchConfiguration that will be used to ensure that any JOIN FETCHed fields are loaded + StoreContext store = q.getContext().getStoreContext(); + FetchConfiguration cacheFc = store.pushFetchConfiguration(); + + // OPENJPA-2586: If the FetchConfig for this executor contains fields, + // then add them to the new FetchConfig. + if (!_fc.getFields().isEmpty()) { + cacheFc.addFields(_fc.getFields()); + } + + for (QueryExpressions qe : _ex.getQueryExpressions()) { + for (String fetchFields : qe.fetchPaths) { + cacheFc.addField(fetchFields); + } + for (String fetchFields : qe.fetchInnerPaths) { + cacheFc.addField(fetchFields); + } + } + try { + List cached = cq.checkCache(key, cacheFc); + if (cached != null) { + return new ListResultObjectProvider(cached); + } + } finally { + store.popFetchConfiguration(); + } + + ResultObjectProvider rop = _ex.executeQuery(cq.getDelegate(), params, range); + if (_fc.getQueryCacheEnabled()) return cq.wrapResult(rop, key); else @@ -482,7 +506,7 @@ public class QueryCacheStoreQuery } /** - * Result list implementation for a cached query result. Package-protected + * Result list implementation for a cached query result. Public * for testing. */ public static class CachedList extends AbstractList @@ -491,23 +515,25 @@ public class QueryCacheStoreQuery private final QueryResult _res; private final boolean _proj; private final StoreContext _sctx; - - public CachedList(QueryResult res, boolean proj, StoreContext ctx) { + private final FetchConfiguration _fc; + + public CachedList(QueryResult res, boolean proj, StoreContext ctx, FetchConfiguration fc) { _res = res; _proj = proj; _sctx = ctx; + _fc = fc; } public Object get(int idx) { if (!_proj) - return fromObjectId(_res.get(idx), _sctx); + return fromObjectId(_res.get(idx), _sctx, _fc); Object[] cached = (Object[]) _res.get(idx); if (cached == null) return null; Object[] uncached = new Object[cached.length]; for (int i = 0; i < cached.length; i++) - uncached[i] = copyProjection(cached[i], _sctx); + uncached[i] = copyProjection(cached[i], _sctx, _fc); return uncached; } @@ -592,7 +618,7 @@ public class QueryCacheStoreQuery Object[] arr = (Object[]) obj; Object[] cp = new Object[arr.length]; for (int i = 0; i < arr.length; i++) - cp[i] = copyProjection(arr[i], _sctx); + cp[i] = copyProjection(arr[i], _sctx, null); cached = cp; } if (cached != null) diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Department.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Department.java new file mode 100644 index 000000000..3719f3f4d --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Department.java @@ -0,0 +1,105 @@ +/* + * 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; + +import java.io.Serializable; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Version; + +/** + * Entity implementation class for Entity: Parent + * + */ +@Entity +@Table(name="FETCHDEPT") +public class Department implements Serializable { + + private static final long serialVersionUID = -5537435298484817651L; + + @Id + private int deptno; + @Version + private int version; + private String name; + @OneToMany(mappedBy="dept", cascade=CascadeType.ALL) + private List employees; + @OneToMany(mappedBy="dept", cascade=CascadeType.ALL) + private List employee2s; + + public Department() { + super(); + } + + public Department(int deptno, String name) { + super(); + this.deptno = deptno; + this.name = name; + } + + public int getDeptno() { + return this.deptno; + } + + public void setDeptno(int deptno) { + this.deptno = deptno; + } + + public int getVersion() { + return this.version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public List getEmployees() { + return this.employees; + } + + public void setEmployees(List employees) { + this.employees = employees; + } + + public List getEmployee2s() { + return this.employee2s; + } + + public void setEmployee2s(List employees) { + this.employee2s = employees; + } + + public String toString() { + return "[Department:depno=" + deptno + ", version=" + version + ", name=" + name + + ", employees=" + employees + ", employee2s=" + employee2s+ ']'; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Department_.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Department_.java new file mode 100644 index 000000000..51e21d110 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Department_.java @@ -0,0 +1,36 @@ +/* + * 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; + +import java.lang.Integer; +import java.lang.String; +import javax.persistence.metamodel.ListAttribute; +import javax.persistence.metamodel.SingularAttribute; + +@javax.persistence.metamodel.StaticMetamodel +(value=org.apache.openjpa.persistence.jpql.joins.Department.class) +@javax.annotation.Generated +(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Tue Jun 03 09:13:08 CDT 2014") +public class Department_ { + public static volatile SingularAttribute deptno; + public static volatile ListAttribute employee2s; + public static volatile ListAttribute employees; + public static volatile SingularAttribute name; + public static volatile SingularAttribute version; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee.java new file mode 100644 index 000000000..91ca1d7af --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee.java @@ -0,0 +1,91 @@ +/* + * 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; + +import java.io.Serializable; +import java.lang.String; +import javax.persistence.*; + +/** + * Entity implementation class for Entity: Child + * + */ +@Entity +@Table(name = "FETCHEMPL") +public class Employee implements Serializable { + + private static final long serialVersionUID = -5155314943010802723L; + + @Id + private int empno; + private String name; + @Version + private int version; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + private Department dept; + + public Employee() { + super(); + } + + public Employee(int empno, String name, Department dept) { + super(); + this.empno = empno; + this.name = name; + this.dept = dept; + } + + public int getEmpno() { + return this.empno; + } + + public void setEmpno(int empno) { + this.empno = empno; + } + + public int getVersion() { + return this.version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Department getDept() { + return dept; + } + + public void setDept(Department dept) { + this.dept = dept; + } + + public String toString() { + return "[Employee:id=" + empno + ", version=" + version + ", name=" + + name + ']'; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee_.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee_.java new file mode 100644 index 000000000..6803e0501 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee_.java @@ -0,0 +1,34 @@ +/* + * 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; + +import java.lang.Integer; +import java.lang.String; +import javax.persistence.metamodel.SingularAttribute; + +@javax.persistence.metamodel.StaticMetamodel +(value=org.apache.openjpa.persistence.jpql.joins.Employee.class) +@javax.annotation.Generated +(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Tue Jun 03 09:14:37 CDT 2014") +public class Employee_ { + public static volatile SingularAttribute dept; + public static volatile SingularAttribute empno; + public static volatile SingularAttribute name; + public static volatile SingularAttribute version; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/TestJoinFetchWithQueryDataCache.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/TestJoinFetchWithQueryDataCache.java new file mode 100644 index 000000000..e8e946ca6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/TestJoinFetchWithQueryDataCache.java @@ -0,0 +1,89 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; + +import junit.framework.Assert; + +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +public class TestJoinFetchWithQueryDataCache extends SQLListenerTestCase { + EntityManager em; + + public void setUp() { + super.setUp(DROP_TABLES, Employee.class, Department.class, "openjpa.QueryCompilationCache", "all", + "openjpa.DataCache", "true", "openjpa.RemoteCommitProvider", "sjvm", "openjpa.QueryCache", "true" + ); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + + Department dept; + dept = new Department(10, "department 10"); + dept.setEmployees(new ArrayList()); + dept.getEmployees().add(new Employee(11, "Emp11", dept)); + dept.getEmployees().add(new Employee(12, "Emp12", dept)); + dept.setEmployee2s(new ArrayList()); + dept.getEmployee2s().add(new Employee(211, "Emp211", dept)); + dept.getEmployee2s().add(new Employee(212, "Emp212", dept)); + em.persist(dept); + + dept = new Department(20, "department 20"); + dept.setEmployees(new ArrayList()); + dept.getEmployees().add(new Employee(21, "Emp21", dept)); + dept.getEmployees().add(new Employee(22, "Emp22", dept)); + dept.setEmployee2s(new ArrayList()); + dept.getEmployee2s().add(new Employee(221, "Emp221", dept)); + dept.getEmployee2s().add(new Employee(222, "Emp222", dept)); + em.persist(dept); + + em.getTransaction().commit(); + + em.close(); + } + + public void testConsecutiveJPQLJoinFetchCall() { + doQuery(emf, false); + doQuery(emf, true); + } + + private void doQuery(EntityManagerFactory emf, boolean cached) { + String query = "select o from Employee o " + "left join fetch o.dept " + "where o.dept.deptno = 10"; + EntityManager em = emf.createEntityManager(); + + sql.clear(); + List emps = em.createQuery(query, Employee.class).getResultList(); + Assert.assertEquals(4, emps.size()); + for (Employee emp : emps) { + em.detach(emp); + + Assert.assertNotNull(emp.getDept()); + Assert.assertEquals(2, emp.getDept().getEmployees().size()); + } + em.close(); + if (cached) { + assertTrue(sql.size() == 0); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntity.java new file mode 100644 index 000000000..040c84e71 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntity.java @@ -0,0 +1,75 @@ +/* + * 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.querycache; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +@Entity +@NamedQueries({ + @NamedQuery(name = "QCEntity.getByAmount", query = "SELECT o from QCEntity o WHERE o.amount=:amount") +}) +public class QCEntity { + @Id + @Column(name = "PK") + private String pk; + + @Column(name = "DESCRIPTION") + private String description; + + @Column(name = "AMOUNT") + private Long amount; + + public QCEntity() { + + } + + public QCEntity(String pk, String description, Long amount) { + this.pk = pk; + this.description = description; + this.amount = amount; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntityM2O.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntityM2O.java new file mode 100644 index 000000000..3a8c96cf6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntityM2O.java @@ -0,0 +1,57 @@ +/* + * 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.querycache; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +@Entity +public class QCEntityM2O { + @Id + @Column(name = "PK") + private String pk; + + @ManyToOne(fetch = FetchType.LAZY) + private QCEntity qc; + + public QCEntityM2O(String pk) { + this.pk = pk; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public void setQc(QCEntity qc) { + this.qc = qc; + } + + public QCEntity getQc() { + return qc; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/TestQueryCacheWithDataCache.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/TestQueryCacheWithDataCache.java new file mode 100644 index 000000000..202cafa55 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/TestQueryCacheWithDataCache.java @@ -0,0 +1,88 @@ +/* + * 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.querycache; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.apache.openjpa.persistence.FetchPlan; +import org.apache.openjpa.persistence.OpenJPAQuery; +import org.apache.openjpa.persistence.querycache.QCEntityM2O; +import org.apache.openjpa.persistence.querycache.QCEntity; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestQueryCacheWithDataCache extends SingleEMFTestCase { + + public void setUp() { + super.setUp(DROP_TABLES, QCEntityM2O.class, QCEntity.class, "openjpa.DataCache", "true", + "openjpa.RemoteCommitProvider", "sjvm", "openjpa.QueryCache", "true"); + } + + /* + * Test for OPENJPA-2586 + */ + public void testWithFetchPlan() { + populate(); + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + doQueryWithFetchPlan(em); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + doQueryWithFetchPlan(em); + em.getTransaction().commit(); + em.close(); + } + + public void doQueryWithFetchPlan(EntityManager em) { + String jpql = "Select e1 from QCEntityM2O e1"; + + Query q = em.createQuery(jpql); + FetchPlan fetchPlan = q.unwrap(OpenJPAQuery.class).getFetchPlan(); + fetchPlan.addField(QCEntityM2O.class, "qc"); + List results = (List) q.getResultList(); + + em.clear(); + + assertTrue("No results returned!", !results.isEmpty()); + for (QCEntityM2O e1 : results) { + assertNotNull("A 'QCEntity' should have been returned!", e1.getQc()); + } + } + + public void populate() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + QCEntityM2O e1 = new QCEntityM2O("aQCEntityM2O"); + QCEntity e2 = new QCEntity("aQCEntityM2O", "test", 2L); + e1.setQc(e2); + + em.persist(e1); + em.persist(e2); + + em.getTransaction().commit(); + em.close(); + } +}