diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java index dcbe31ecc..d94604e14 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java @@ -18,6 +18,7 @@ */ package org.apache.openjpa.kernel; +import java.util.BitSet; import java.util.Collection; import java.util.Set; @@ -446,7 +447,15 @@ public class DelegatingFetchConfiguration throw translate(re); } } - + + public BitSet requiresFetch(Set fgs, FieldMetaData[] fmds ){ + try { + return _fetch.requiresFetch(fgs, fmds); + } catch (RuntimeException re) { + throw translate(re); + } + } + public boolean requiresLoad() { try { return _fetch.requiresLoad(); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java index 1fdfce43a..40c00ca4f 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java @@ -19,6 +19,7 @@ package org.apache.openjpa.kernel; import java.io.Serializable; +import java.util.BitSet; import java.util.Collection; import java.util.Set; @@ -345,6 +346,18 @@ public interface FetchConfiguration */ public int requiresFetch(FieldMetaData fm); + /** + * Affirms if the given fields require to be fetched in the context of + * the given fetch group set. Returns a BitSet that contains one of + * {@link #FETCH_NONE}, {@link #FETCH_LOAD}, {@link FETCH_REF} for each + * field. + * + * @param fgs fetch group set + * @param fmds array of fields to be examined + * @return BitSet that indicates whether fetches are required or not + */ + public BitSet requiresFetch(Set fgs, FieldMetaData[] fmds ); + /** * Return false if we know that the object being fetched with this * configuration does not require a load, because this configuration came diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java index 0975f875f..17e0cc5d2 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java @@ -21,6 +21,7 @@ package org.apache.openjpa.kernel; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -92,6 +93,7 @@ public class FetchConfigurationImpl private boolean _load = true; private int _availableRecursion; private int _availableDepth; + private Map _lfgFields = null; public FetchConfigurationImpl() { this(null); @@ -510,6 +512,10 @@ public class FetchConfigurationImpl if (!includes(fm)) return FETCH_NONE; + return indirectFetch(fm); + } + + private int indirectFetch(FieldMetaData fm){ Class type = getRelationType(fm); if (type == null) return FETCH_LOAD; @@ -529,6 +535,15 @@ public class FetchConfigurationImpl return FETCH_REF; return FETCH_LOAD; } + + public BitSet requiresFetch(Set fgs, FieldMetaData[] fmds) { + BitSet fields = new BitSet(fgs.size()); + Iterator itr = fgs.iterator(); + while (itr.hasNext()) { + fields = includes((FieldMetaData) itr.next(), fmds, fields); + } + return fields; + } public boolean requiresLoad() { return _load; @@ -567,16 +582,73 @@ public class FetchConfigurationImpl || hasField(fmd.getFullName(false))) return true; String[] fgs = fmd.getCustomFetchGroups(); - for (int i = 0; i < fgs.length; i++) + for (int i = 0; i < fgs.length; i++){ if (hasFetchGroup(fgs[i])) return true; + } return false; } + private BitSet includes(FieldMetaData fmd, FieldMetaData[] fmds, + BitSet fields) { + if ((fmd.isInDefaultFetchGroup() && hasFetchGroup(FetchGroup.NAME_DEFAULT)) + || hasFetchGroup(FetchGroup.NAME_ALL) + || hasField(fmd.getFullName(false))) { + if (indirectFetch(fmd) != FETCH_NONE) + fields.set(fmd.getIndex()); + return fields; + } + // now we need to see if this field associates with + // any fetch groups + String[] fgs = fmd.getCustomFetchGroups(); + for (int i = 0; i < fgs.length; i++) { + if (hasFetchGroup(fgs[i])) { + if (indirectFetch(fmd) != FETCH_NONE) + fields.set(fmd.getIndex()); + // check whether this field has a loadFetchGroup + // if it has a LoadFetchGroup, then we need to get + // all the fields that associate with this LoadFetchGroup + String fg = fmd.getLoadFetchGroup(); + if (fg != null) { + BitSet fldIndex = getLoadFetchGroupFields(fg, fmds); + // merge the loadFetchGroup fields to the retuned fields. + if (fldIndex != null && !fldIndex.isEmpty()) { + for (int j = 0; j < fldIndex.length(); j++) + if (fldIndex.get(j)) + fields.set(j); + } + } + } + } + return fields; + } + /** - * Return the available recursion depth via the given field for the + * Search the fields for this loadFetchGroup. It basically searches from + * each field and check whether this field has the same fetchgroup. If it + * has then this field is required to load. + */ + private BitSet getLoadFetchGroupFields(String fg, FieldMetaData[] fmds) { + BitSet rtnField = null; + if (_lfgFields != null && _lfgFields.containsKey(fg)) + return (BitSet)_lfgFields.get(fg); + for (int i = 0; i < fmds.length; i++) { + if (fmds[i].isInFetchGroup(fg)){ + if (rtnField == null) + rtnField = new BitSet(fmds.length); + rtnField.set(i); + } + } + if (_lfgFields == null) + _lfgFields = new HashMap(fmds.length); + _lfgFields.put(fg,rtnField); + return rtnField; + } + + /** + * Return the available recursion depth via the given field for the * given type. - * + * * @param traverse whether we're traversing the field */ private int getAvailableRecursionDepth(FieldMetaData fm, Class type, diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java index 6d7486bb2..e86fb9262 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java @@ -427,18 +427,22 @@ public class StateManagerImpl BitSet fields = null; FieldMetaData[] fmds = _meta.getFields(); + Set fgs = null; boolean load; + for (int i = 0; i < fmds.length; i++) { - if (_loaded.get(i) || (exclude != null && exclude.get(i))) - continue; + if (exclude != null && exclude.get(i)) + continue; switch (mode) { case LOAD_SERIALIZE: load = !fmds[i].isTransient(); break; - case LOAD_FGS: - load = fetch == null || fetch.requiresFetch(fmds[i]) - != FetchConfiguration.FETCH_NONE; + case LOAD_FGS: + load = false; + if (fgs == null) + fgs = new HashSet(fmds.length); + fgs.add(fmds[i]); break; default: // LOAD_ALL load = true; @@ -449,6 +453,22 @@ public class StateManagerImpl fields = new BitSet(fmds.length); fields.set(i); } + // post process for the fetchGroup: if there is a + // fetchgroup field, then go to the FetchConfiguration + // to get the required fetch fields. + if (fgs != null) { + if (fields == null) + fields = new BitSet(fmds.length); + BitSet fgFields = fetch.requiresFetch(fgs, fmds); + // merge the fetchgroup required fields to the original + // fields only the fields are not already loaded and + // are not in the original fields. + for (int j = 0; j < fgFields.length(); j++) { + if (fgFields.get(j) && !fields.get(j) && !_loaded.get(j)) + fields.set(j); + } + } + } return fields; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGAddress.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGAddress.java new file mode 100644 index 000000000..aa5d0b57b --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGAddress.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.fetchgroups; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class FGAddress { + @Id + private int id; + + @Basic + private String street; + + @Basic + private String city; + + @Basic + private String state; + + @Basic + private int zip; + + public FGAddress() { + + } + + public FGAddress(int id, String street, String city, String state, int zip) { + this.id = id; + this.street = street; + this.city = city; + this.state = state; + this.zip = zip; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public int getZip() { + return zip; + } + + public void setZip(int zip) { + this.zip = zip; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("FGAddress(id=").append(this.id).append(")"); + sb.append(": street=").append(getStreet()); + sb.append(": city=").append(getCity()); + sb.append(": state=").append(getState()); + sb.append(": zip=").append(getZip()); + + return new String(sb); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGDepartment.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGDepartment.java new file mode 100644 index 000000000..38bc351e0 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGDepartment.java @@ -0,0 +1,60 @@ +/* + * 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.fetchgroups; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class FGDepartment { + @Id + private int id; + + private String name; + + public FGDepartment() { + + } + + public FGDepartment(int id, String name) { + this.id = id; + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String toString() { + return new String("FGDepartment(id=" + this.id + ")"); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGEmployee.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGEmployee.java new file mode 100644 index 000000000..51457097f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGEmployee.java @@ -0,0 +1,174 @@ +/* + * 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.fetchgroups; + +import javax.persistence.Basic; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; + +import org.apache.openjpa.persistence.FetchAttribute; +import org.apache.openjpa.persistence.FetchGroup; +import org.apache.openjpa.persistence.FetchGroups; +import org.apache.openjpa.persistence.LoadFetchGroup; + +@Entity +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +// Default inheritance strategy +@DiscriminatorColumn(name = "EMP_TYPE", discriminatorType = DiscriminatorType.INTEGER) +@DiscriminatorValue("0") +@FetchGroups( { + @FetchGroup(name = "AddressFetchGroup", attributes = { @FetchAttribute(name = "address") }), + @FetchGroup(name = "RatingFetchGroup", attributes = { @FetchAttribute(name = "rating") }), + @FetchGroup(name = "ManagerFetchGroup1A", attributes = { @FetchAttribute(name = "manager", recursionDepth = 1) }), + @FetchGroup(name = "ManagerFetchGroup1B", attributes = { @FetchAttribute(name = "manager", recursionDepth = -1) }), + @FetchGroup(name = "ManagerFetchGroup2", attributes = { @FetchAttribute(name = "manager", recursionDepth = 2) }), + @FetchGroup(name = "DescFetchGroup", attributes = { @FetchAttribute(name = "description") }), + + @FetchGroup(name = "DepartmentFetchGroup", attributes = { @FetchAttribute(name = "dept") }), + + @FetchGroup(name = "AggregateEmployeeFetchGroup1", attributes = { + @FetchAttribute(name = "dept"), + @FetchAttribute(name = "address"), + @FetchAttribute(name = "manager", recursionDepth = 1) }), + @FetchGroup(name = "AggregateEmployeeFetchGroup2", fetchGroups = { "AggregateEmployeeFetchGroup1" }), + @FetchGroup(name = "AggregateEmployeeFetchGroup3", fetchGroups = { + "DepartmentFetchGroup", "AddressFetchGroup", + "ManagerFetchGroup1A" }), + @FetchGroup(name = "AggregateEmployeeFetchGroup4", attributes = { + @FetchAttribute(name = "dept"), + @FetchAttribute(name = "address") }, fetchGroups = { "ManagerFetchGroup1A" }) }) +public class FGEmployee { + @Id + private int id; + + private String lastName; + + private String firstName; + + @Basic(fetch = FetchType.LAZY) + private String description; + + @ManyToOne(fetch = FetchType.LAZY) + private FGDepartment dept; + + @OneToOne(fetch = FetchType.LAZY) + private FGAddress address; + + @ManyToOne(fetch = FetchType.LAZY) + private FGManager manager; + + @Basic(fetch = FetchType.LAZY) + @LoadFetchGroup("AddressFetchGroup") + private String rating; + + public FGEmployee() { + + } + + public FGEmployee(int id, String firstName, String lastName, String desc, + FGDepartment dept, FGAddress address, FGManager manager, + String rating) { + this.id = id; + this.lastName = lastName; + this.firstName = firstName; + this.description = desc; + this.dept = dept; + this.address = address; + this.manager = manager; + this.rating = rating; + } + + public FGAddress getAddress() { + return address; + } + + public void setAddress(FGAddress address) { + this.address = address; + } + + public FGDepartment getDept() { + return dept; + } + + public void setDept(FGDepartment dept) { + this.dept = dept; + } + + public String getDescription() { + return description; + } + + public void setDescription(String desc) { + this.description = desc; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public FGManager getManager() { + return manager; + } + + public void setManager(FGManager manager) { + this.manager = manager; + } + + public String getRating() { + return rating; + } + + public void setRating(String rating) { + this.rating = rating; + } + + public String toString() { + return new String(this.getClass().getSimpleName() + "(id=" + this.id + + ")"); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGManager.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGManager.java new file mode 100644 index 000000000..8c69edf69 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/FGManager.java @@ -0,0 +1,72 @@ +/* + * 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.fetchgroups; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.persistence.Basic; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToMany; + +import org.apache.openjpa.persistence.FetchAttribute; +import org.apache.openjpa.persistence.FetchGroup; +import org.apache.openjpa.persistence.FetchGroups; + +@Entity +@DiscriminatorValue("1") +@FetchGroups( { + @FetchGroup(name = "MDataFetchGroup", attributes = { @FetchAttribute(name = "mData") }), + @FetchGroup(name = "EmployeesFetchGroup", attributes = { @FetchAttribute(name = "employees") }) }) +public class FGManager extends FGEmployee { + @OneToMany(mappedBy = "manager", fetch = FetchType.LAZY) + Collection employees; + + @Basic(fetch = FetchType.LAZY) + private String mData; + + public FGManager() { + super(); + employees = new ArrayList(); + } + + public FGManager(int id, String firstName, String lastName, String desc, + FGDepartment dept, FGAddress address, FGManager manager, + String rating, Collection employees, String mData) { + super(id, firstName, lastName, desc, dept, address, manager, rating); + this.employees = new ArrayList(); + this.employees.addAll(employees); + this.mData = mData; + } + + public String getMData() { + return mData; + } + + public void setMData(String data) { + mData = data; + } + + public Collection getEmployees() { + return employees; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/TestFetchGroup.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/TestFetchGroup.java new file mode 100644 index 000000000..2f1e9be10 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/TestFetchGroup.java @@ -0,0 +1,564 @@ +/* + * 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.fetchgroups; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.OpenJPAQuery; +import org.apache.openjpa.persistence.test.SingleEMTestCase; + +public class TestFetchGroup extends SingleEMTestCase { + public void setUp() { + setUp(FGEmployee.class, FGDepartment.class, FGManager.class, + FGAddress.class); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + + // Populate database as denoted in Entity Data + boolean errors = initializeDatabase(oem); + assertFalse(errors); + } + + // Test no fetch group is added. + public void testFetchGroup001() { + // System.out.println("***********************************************"); + // System.out.println("******** 001 ==> test no fetch group is added + // thru API"); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + + FGEmployee emp = findEmployee(oem, 1, false, null); + + // Examine Employee(id=1).rating, data should be available + // System.out.println("Assert Employee(id=1).rating should be null. + // Result ==>"); + assertNull(emp.getRating()); + // Examine Employee(id=1).description, data should NOT be available + // System.out.println("Assert Employee(id=1).description is null. Result + // ==>"); + assertNull(emp.getDescription()); + // Examine Employee(id=1).address, data should not be available + // System.out.println("Assert Employee(id=1).address is null"); + assertNull(emp.getAddress()); + // Examine Employee(id=1).dept, data should NoTbe available + // System.out.println("Assert Employee(id=1).dept is null "); + assertNull(emp.getDept()); + // Examine Employee(id=1).manager, data should NOT be available. + // System.out.println("Assert Employee(id=1).manager is null "); + assertNull(emp.getManager()); + + } + + // Test no fetch group is added and restFetchGroup is called. + public void testFetchGroup002() { + // System.out.println("***********************************************"); + // System.out.println("*****************002 ==> test no fetch group is + // added thru API and "); + // System.out.println(" resetFetchGroup is called."); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + + FGEmployee emp = findEmployee(oem, 1, true, null); + // Examine Employee(id=1).rating, data should not be available + // System.out.println("Assert Employee(id=1).rating is null."); + assertNull(emp.getRating()); + // Examine Employee(id=1).description, data should NOT be available + // System.out.println("Assert Employee(id=1).description is null. Result + // ==>"); + assertNull(emp.getDescription()); + // Examine Employee(id=1).address, data should not be available + // System.out.println("Assert Employee(id=1).address is null "); + assertNull(emp.getAddress()); + // Examine Employee(id=1).dept, data should NoT be available + // System.out.println("Assert Employee(id=1).dept is null "); + assertNull(emp.getDept()); + // Examine Employee(id=1).manager, data should NOT be available. + // System.out.println("Assert Employee(id=1).manager is null "); + assertNull(emp.getManager()); + + } + + // Test Rating fetch group is added and restFetchGroup is called. + public void testFetchGroup003() { + // System.out.println("***********************************************"); + // System.out.println("****************003 ==> test RatingFetchGroup is + // added thru API and "); + // System.out.println(" resetFetchGroup is called."); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + + String[] arr = { "RatingFetchGroup" }; + FGEmployee emp = findEmployee(oem, 1, true, arr); + // Examine Employee(id=1).rating, data should be available + // System.out.println("Assert Employee(id=1).rating is not null. Result + // ==> "); + assertNotNull(emp.getRating()); + // Examine Employee(id=1).description, data should NOT be available + // System.out.println("Assert Employee(id=1).description is null. Result + // ==>"); + assertNull(emp.getDescription()); + // Examine Employee(id=1).address, data should be available + // System.out.println("Assert Employee(id=1).address is not null because + // of @LoadFetchGroup on Rating ==> "); + assertNotNull(emp.getAddress()); + // Examine Employee(id=1).dept, data should NoTbe available + // System.out.println("Assert Employee(id=1).dept is null = "); + assertNull(emp.getDept()); + // Examine Employee(id=1).manager, data should NOT be available. + // System.out.println("Assert Employee(id=1).manager is null ="); + assertNull(emp.getManager()); + // em.getTransaction().commit(); + + } + + // Test Address and Rating fetch groups are added and restFetchGroup is + // called. + public void testFetchGroup004() { + // System.out.println("***********************************************"); + // System.out.println("***************004 ==> test RatingFetchGroup and + // AddressFetchGroup" + + // "are added thru API and " + + // "resetFetchGroup is called."); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + + String[] arr = { "RatingFetchGroup", "AddressFetchGroup" }; + FGEmployee emp = findEmployee(oem, 1, true, arr); + // Examine Employee(id=1).rating, data should be available + // System.out.println("Assert Employee(id=1).rating is not null. Result + // ==> "); + assertNotNull(emp.getRating()); + // Examine Employee(id=1).description, data should NOT be available + // System.out.println("Assert Employee(id=1).description is null. Result + // ==>"); + assertNull(emp.getDescription()); + // Examine Employee(id=1).address, data should be available + // System.out.println("Assert Employee(id=1).address is not null ==> "); + assertNotNull(emp.getAddress()); + // Examine Employee(id=1).dept, data should NoTbe available + // System.out.println("Assert Employee(id=1).dept is null = "); + assertNull(emp.getDept()); + // Examine Employee(id=1).manager, data should NOT be available. + // System.out.println("Assert Employee(id=1).manager is null ="); + assertNull(emp.getManager()); + + } + + // Test aggregateEmployeeFetchGroup2 only + public void testFetchGroup005() { + // System.out.println("***********************************************"); + // System.out.println("***************005 ==> test + // aggregateEmployeeFetchGroup2 only"); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + + String[] arr = { "AggregateEmployeeFetchGroup2" }; + FGEmployee emp = findEmployee(oem, 1, true, arr); + // Examine Employee(id=1).address, data should be available + + // System.out.println("Assert Employee(id=1).address is not null "); + assertNotNull(emp.getAddress()); + // Examine Employee(id=1).dept, data should NoTbe available + // System.out.println("Assert Employee(id=1).dept is not null = "); + assertNotNull(emp.getDept()); + // Examine Employee(id=1).manager, data should NOT be available. + // System.out.println("Assert Employee(id=1).manager is not null ="); + assertNotNull(emp.getManager()); + + } + + public void testFetchGroup006() { + // System.out.println("***********************************************"); + // System.out.println("************006 ==> test + // aggregateEmployeeFetchGroup1 and "+ + // " aggregateEmployeeFetchGroup2 - expect address, dept and manager are + // not null"); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + // em.getTransaction().begin(); + + String[] arr = { "AggregateEmployeeFetchGroup1", + "AggregateEmployeeFetchGroup2" }; + FGEmployee emp = findEmployee(oem, 1, true, arr); + // Examine Employee(id=1).address, data should be available + // System.out.println("Assert Employee(id=1).address is not null "); + assertNotNull(emp.getAddress()); + // Examine Employee(id=1).dept, data should NoTbe available + // System.out.println("Assert Employee(id=1).dept is not null "); + assertNotNull(emp.getDept()); + // Examine Employee(id=1).manager, data should NOT be available. + // FGManager mgr = emp.getManager(); + // System.out.println("assert manager is not null"); + assertNotNull(emp.getManager()); + assertNotNull(emp.getManager().getId()); + assertNotNull(emp.getManager().getFirstName()); + + // Verify that Manager(id=101).manager is not available, as the + // recursion depth should have retrieved only the Employee and its + // manager. + // System.out.println("Verify that Manager(id=101).manager is no + // available, as the recursion depth should have retrieved only the + // Employee and its manager."); + // System.out.println("Assert Employee(id=1).manager.manager == null + // ==>"+ mgrMgr); + assertNull(emp.getManager().getManager()); + + // System.out.println("Verify that Manager(id=201).manager is not + // available, as the recursion depth should have retrieved only the + // Employee and its manager."); + // assertNull(emp.getManager().getManager().getManager()); + + } + + public void testFetchGroup007() { + // System.out.println("***********************************************"); + // System.out.println("***********007 ==> test one fetch group attribute + // is associated"+ + // "multiple fetch groups"); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + + String[] arr = { "ManagerFetchGroup1A" }; + FGEmployee emp = findEmployee(oem, 1, true, arr); + // Examine Employee(id=1).address, data should be available + // FGAddress addr = emp.getAddress(); + // System.out.println("Assert Employee(id=1).address is null "); + assertNull(emp.getAddress()); + // Examine Employee(id=1).dept, data should NoTbe available + // System.out.println("Assert Employee(id=1).dept is null"); + assertNull(emp.getDept()); + // Examine Employee(id=1).manager, data should NOT be available. + // FGManager mgr = emp.getManager(); + + // System.out.println("Assert manager is not null"); + assertNotNull(emp.getManager()); + assertNotNull(emp.getManager().getId()); + assertNotNull(emp.getManager().getFirstName()); + + // Verify that Manager(id=101).manager is not available, as the + // recursion depth should have retrieved only the Employee and its + // manager. + // System.out.println("Verify that Manager(id=101).manager is not + // available, as the recursion depth should have retrieved only the + // Employee and its manager."); + assertNull(emp.getManager().getManager()); + + // System.out.println("Verify that Manager(id=201).manager is not + // available, as the recursion depth should have retrieved only the + // Employee and its manager."); + // assertNull(emp.getManager().getManager().getManager()); + + } + + public void testFetchGroup008() { + // System.out.println("***********************************************"); + // System.out.println("***********007 ==> test one fetch group attribute + // is associated"+ + // "multiple fetch groups"); + EntityManager em = emf.createEntityManager(); + OpenJPAEntityManager oem = OpenJPAPersistence.cast(em); + OpenJPAEntityManager oem1 = OpenJPAPersistence.cast(em); + Query q = oem1.createQuery("SELECT e FROM FGEmployee e WHERE e.id = 1"); + OpenJPAQuery oq = (OpenJPAQuery) q; + oem1.clear(); + + // use the default, address and description should be null + FGEmployee emp = findEmployeeForQuery(oem, oq, 1, true, null, null); + oem1.clear(); + assertNull(emp.getAddress()); + assertNull(emp.getDescription()); + assertNull(emp.getManager()); + + // add fetch fields to the fetch plan - address and description should + // not be null + String[] str = { + "org.apache.openjpa.persistence.fetchgroups.FGEmployee.description", + "org.apache.openjpa.persistence.fetchgroups.FGEmployee.address" }; + FGEmployee emp2 = findEmployeeForQuery(oem, oq, 1, true, str, null); + oem1.clear(); + assertNotNull(emp2.getAddress()); + assertNotNull(emp2.getDescription()); + assertNull(emp2.getManager()); + + // remove fetch fields again - address and description should be null + FGEmployee emp3 = findEmployeeForQuery(oem, oq, 1, false, null, str); + oem1.clear(); + assertNull(emp3.getAddress()); + assertNull(emp3.getDescription()); + assertNull(emp3.getManager()); + } + + private FGEmployee findEmployee(OpenJPAEntityManager oem, Object id, + boolean reset, String[] fetchGroups) { + oem.getTransaction().begin(); + // System.out.println("findEmployoee starts and check the fetchGroup + // info:"); + // int sz = oem.getFetchPlan().getFetchGroups().size(); + // String arr = + // Arrays.toString(oem.getFetchPlan().getFetchGroups().toArray()); + // System.out.println("fetchGroup = "+arr+ " and fetch Group size + // ="+sz); + // reset fetchGroup if necessary: + if (reset) { + oem.getFetchPlan().resetFetchGroups(); + // assertEquals(1, oem.getFetchPlan().getFetchGroups().size()); + // arr = + // Arrays.toString(oem.getFetchPlan().getFetchGroups().toArray()); + // System.out.println("after resetFetchGroup, fetchGroup="+arr); + // assertEquals("[default]",arr); + } + if (fetchGroups != null) { + // System.out.println("input fetchGroup = "+fetchGroups); + for (String fg : fetchGroups) + oem.getFetchPlan().addFetchGroup(fg); + // arr = + // Arrays.toString(oem.getFetchPlan().getFetchGroups().toArray()); + // System.out.println("after addFetchGroup, fetchGroups = "+arr); + } + // System.out.println("Finding Employee(id=1)..."); + FGEmployee emp = oem.find(FGEmployee.class, id); + // System.out.println("Employee found ="+emp); + oem.getTransaction().commit(); + // oem.clear(); + oem.close(); + return emp; + + } + + private static FGEmployee findEmployeeForQuery(OpenJPAEntityManager oem, + OpenJPAQuery oq, Object id, boolean reset, String[] fetchGroups, + String[] removes) { + oem.getTransaction().begin(); + // reset fetchGroup if necessary: + if (reset) { + oem.getFetchPlan().resetFetchGroups(); + oq.getFetchPlan().resetFetchGroups(); + } + if (fetchGroups != null) { + for (String fg : fetchGroups) + oq.getFetchPlan().addField(fg); + // arr = + // Arrays.toString(oq.getFetchPlan().getFetchGroups().toArray()); + // arr = Arrays.toString(oq.getFetchPlan().getFields().toArray()); + // System.out.println("after addFetchfields, fetch fields = "+arr); + } + if (removes != null) { + oq.getFetchPlan().removeFields(removes); + // arr = Arrays.toString(oq.getFetchPlan().getFields().toArray()); + // System.out.println("after removeFetchGroup, fetch fields = + // "+arr); + } + // System.out.println("Finding Employee(id=1)..."); + FGEmployee emp = (FGEmployee) oq.getSingleResult(); + oem.getTransaction().commit(); + oem.clear(); + // oem.close(); + return emp; + + } + + private static void cleanDatabase(EntityManager em) { + // Clean out the database + em.clear(); + + String entityNames[] = { "FGEmployee", "FGAddress", "FGDepartment" }; + + // System.out.println("Cleaning database."); + try { + // System.out.println("Starting transaction..."); + em.getTransaction().begin(); + // if (persistenceContextType == PERSISTENCECONTEXTTYPE_APPMGD) + // em.joinTransaction(); + + for (int index = 0; index < entityNames.length; index++) { + String query = "SELECT a FROM " + entityNames[index] + " a"; + List entityAList = em.createQuery(query).getResultList(); + + // Nothing returned, go to the next entity + if (entityAList.size() == 0) + continue; + + // System.out.println("Removing " + entityNames[index] + " data + // from the database..."); + + Iterator i = entityAList.iterator(); + while (i.hasNext()) { + Object entity = i.next(); + // System.out.println("Removing entity " + entity.toString() + // + " ..."); + em.remove(entity); + } + } + + // System.out.println("Committing transaction..."); + em.getTransaction().commit(); + } catch (Throwable t) { + System.out.println("Caught exception during db cleanup" + t); + } finally { + try { + if (em.getTransaction().isActive()) + em.getTransaction().rollback(); + } catch (Throwable t) { + System.out + .println("Caught exception transaction rollback in db cleanup failure recovery" + + t); + // throw t; + } + } + + // System.out.println("Done cleaning database."); + } + + private static boolean initializeDatabase(EntityManager em) { + // Clean the database first + cleanDatabase(em); + + // System.out.println("Creating entities..."); + boolean errors = false; + try { + // Persist all entities to the database + // System.out.println("Starting transaction..."); + em.getTransaction().begin(); + // if (persistenceContextType == PERSISTENCECONTEXTTYPE_APPMGD) + // em.joinTransaction(); + + // Addreesses + FGAddress[] addresses = new FGAddress[11]; + addresses[0] = new FGAddress(1, "1010 29th Ave NW", "Rochester", + "MN", 55901); + addresses[1] = new FGAddress(2, "2020 29th Ave NW", "Rochester", + "MN", 55901); + addresses[2] = new FGAddress(3, "5000 Pilot Knob", "Rochester", + "MN", 55902); + addresses[3] = new FGAddress(4, "8192 Galaxie Avenue", + "Apple Valley", "MN", 55209); + addresses[4] = new FGAddress(5, "9100 Knight Drive", "Fargo", "ND", + 58202); + addresses[5] = new FGAddress(6, "312 Sioux Lane", "Bismarck", "ND", + 58102); + addresses[6] = new FGAddress(7, "5124 Grinch Circle", "Mason City", + "IA", 24241); + addresses[7] = new FGAddress(8, "1201 Citrus Lane", "Raleigh", + "NC", 12345); + addresses[8] = new FGAddress(9, "1501 Lemon Lane", "Raleigh", "NC", + 12345); + addresses[9] = new FGAddress(10, "2903 Orange Drive", "Raleigh", + "NC", 12345); + addresses[10] = new FGAddress(11, "1511 Kiwi Circle", "Raleigh", + "NC", 12345); + + // System.out.println("Persisting Address entities..."); + for (int index = 0; index < addresses.length; index++) { + em.persist(addresses[index]); + } + + // Departments + FGDepartment[] departments = new FGDepartment[7]; + for (int index = 0; index < 7; index++) { + departments[index] = new FGDepartment(index + 1, "Department " + + (index + 1)); + } + + // System.out.println("Persisting Department entities..."); + for (int index = 0; index < departments.length; index++) { + em.persist(departments[index]); + } + + // Managers + Collection emptyCollection = new ArrayList(); + FGManager[] managers = new FGManager[6]; + managers[0] = new FGManager(301, "Elric", "Scotch", + "Description MMM1", departments[6], addresses[10], + (FGManager) null, "Good", emptyCollection, "MData301"); + managers[1] = new FGManager(202, "Cedric", "Clue", + "Description MM2", departments[5], addresses[9], + managers[0], "Good", emptyCollection, "MData202"); + managers[2] = new FGManager(201, "Bill", "Editor", + "Description MM1", departments[5], addresses[8], + managers[0], "Good", emptyCollection, "MData201"); + managers[3] = new FGManager(103, "Sue", "Taylor", "Description M3", + departments[4], addresses[8], managers[1], "Good", + emptyCollection, "MData103"); + managers[4] = new FGManager(102, "Alfred", "Newmann", + "Description M2", departments[3], addresses[7], + managers[2], "Good", emptyCollection, "MData102"); + managers[5] = new FGManager(101, "Jim", "Mitternacht", + "Description M1", departments[3], addresses[6], + managers[2], "Good", emptyCollection, "MData101"); + + // System.out.println("Persisting Manager entities..."); + for (int index = 0; index < managers.length; index++) { + em.persist(managers[index]); + } + + // Employees + FGEmployee[] employees = new FGEmployee[8]; + employees[0] = new FGEmployee(1, "John", "Doe", "Description 1", + departments[0], addresses[0], managers[5], "Good"); + employees[1] = new FGEmployee(2, "Jane", "Doe", "Description 2", + departments[0], addresses[0], managers[5], "Good"); + employees[2] = new FGEmployee(3, "Steve", "Martin", + "Description 3", departments[0], addresses[1], managers[5], + "Good"); + employees[3] = new FGEmployee(4, "Mark", "Scrabble", + "Description 4", departments[1], addresses[2], managers[4], + "Good"); + employees[4] = new FGEmployee(5, "Stacy", "Life", "Description 5", + departments[1], addresses[3], managers[4], "Good"); + employees[5] = new FGEmployee(6, "Alx", "Indigo", "Description 6", + departments[2], addresses[5], managers[3], "Good"); + employees[6] = new FGEmployee(7, "John", "Einstein", + "Description 7", departments[2], addresses[5], managers[3], + "Good"); + employees[7] = new FGEmployee(8, "Max", "Headroom", + "Description 7", departments[5], addresses[3], managers[2], + "Good"); + + // System.out.println("Persisting Employee entities..."); + for (int index = 0; index < employees.length; index++) { + em.persist(employees[index]); + } + + // System.out.println("Committing transaction..."); + em.getTransaction().commit(); + } catch (Throwable t) { + // System.out.println("Caught exception during db populating"+ t); + errors = true; + } finally { + try { + if (em.getTransaction().isActive()) + em.getTransaction().rollback(); + } catch (Throwable t) { + // System.out.println("Caught exception transaction rollback in + // db population failure recovery"+ t); + } + } + + return errors; + } + +} // end of TestFetchGroup