blob: 4b382e7378ea645995bfc98ac90a31fa074d4883 [file] [log] [blame]
/*
* Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// 05/19/2010-2.1 ailitchev - Bug 244124 - Add Nested FetchGroup
package org.eclipse.persistence.testing.tests.jpa.fetchgroups;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import junit.framework.TestSuite;
import org.eclipse.persistence.config.CacheUsage;
import org.eclipse.persistence.config.QueryHints;
import org.eclipse.persistence.indirection.IndirectList;
import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.helper.IdentityHashSet;
import org.eclipse.persistence.internal.helper.SerializationHelper;
import org.eclipse.persistence.internal.jpa.EntityManagerImpl;
import org.eclipse.persistence.internal.queries.AttributeItem;
import org.eclipse.persistence.internal.queries.EntityFetchGroup;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaEntityManager;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.queries.AttributeGroup;
import org.eclipse.persistence.queries.FetchGroup;
import org.eclipse.persistence.sessions.CopyGroup;
import org.eclipse.persistence.sessions.server.ServerSession;
import org.eclipse.persistence.testing.models.jpa.advanced.Address;
import org.eclipse.persistence.testing.models.jpa.advanced.Department;
import org.eclipse.persistence.testing.models.jpa.advanced.Employee;
import org.eclipse.persistence.testing.models.jpa.advanced.EmploymentPeriod;
import org.eclipse.persistence.testing.models.jpa.advanced.PhoneNumber;
import org.eclipse.persistence.testing.models.jpa.advanced.Project;
import org.junit.Test;
/**
* Simple tests to verify the functionality of {@link FetchGroup} when the
* entities are detached through serialization.
*
* @author dclarke
* @since EclipseLink 2.1
*/
public class SimpleSerializeFetchGroupTests extends BaseFetchGroupTests {
public SimpleSerializeFetchGroupTests() {
super();
}
public SimpleSerializeFetchGroupTests(String name) {
super(name);
}
public static junit.framework.Test suite() {
TestSuite suite = new TestSuite();
suite.setName("SimpleSerializeFetchGroupTests");
suite.addTest(new SimpleSerializeFetchGroupTests("testSetup"));
suite.addTest(new SimpleSerializeFetchGroupTests("verifyWriteReplaceOnFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("verifyAddAttributeInDetachedEntityFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("singleResultEmptyFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("resultListEmptyFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("resultListPeriodFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("managerFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("managerFetchGroupWithJoinFetch"));
suite.addTest(new SimpleSerializeFetchGroupTests("employeeNamesFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("joinFetchEmployeeAddressWithDynamicFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("joinFetchEmployeeAddressPhoneWithDynamicFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("verifyFetchedRelationshipAttributes"));
suite.addTest(new SimpleSerializeFetchGroupTests("attrAndVHContainSameObjectAfterGetRealAttributeValue"));
if (!isJPA10()) {
suite.addTest(new SimpleSerializeFetchGroupTests("findMinimalFetchGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("findEmptyFetchGroup_setUnfetchedSalary"));
suite.addTest(new SimpleSerializeFetchGroupTests("verifyUnfetchedAttributes"));
suite.addTest(new SimpleSerializeFetchGroupTests("simpleSerializeAndMerge"));
suite.addTest(new SimpleSerializeFetchGroupTests("partialMerge"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyGroupObjectGraph"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyGroupMerge"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyGroupMerge2"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyWithPk"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyWithPkUseFullGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyWithoutPk"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyWithoutPkUseFullGroup"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyNoCascade"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyCascadePrivateParts"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyCascadeAllParts"));
suite.addTest(new SimpleSerializeFetchGroupTests("copyEmbedded"));
}
return suite;
}
@Test
public void verifyWriteReplaceOnFetchGroup() throws Exception {
EntityFetchGroup fg = new EntityFetchGroup(new String[]{"basic", "a"});
//fg.addAttribute("basic");
//fg.addAttribute("a.b");
//assertTrue(fg.getClass() == FetchGroup.class);
FetchGroup serFG = serialize(fg);
assertNotNull(serFG);
assertTrue(serFG.getClass() == EntityFetchGroup.class);
assertTrue(serFG.hasItems());
AttributeItem basicFI = serFG.getItem("basic");
assertNotNull(basicFI);
//assertTrue(basicFI instanceof DetachedFetchItem);
AttributeItem aFI = serFG.getItem("a");
assertNotNull(aFI);
//assertTrue(aFI instanceof DetachedFetchItem);
// serialized EntityFetchGroup is always flat - doesn't have nested groups.
assertNull(aFI.getGroup());
/*assertNotNull(aFI.getGroup());
assertTrue(aFI.getGroup() instanceof EntityFetchGroup);
EntityFetchGroup aEFG = (EntityFetchGroup) aFI.getGroup();
assertNull(aEFG.getParent());
assertTrue(aEFG.hasItems());
AttributeItem bFI = aEFG.getItem("b");
assertNotNull(bFI);
//assertTrue(bFI instanceof DetachedFetchItem);
assertNull(bFI.getGroup());*/
}
@Test
public void findMinimalFetchGroup() throws Exception {
EntityManager em = createEntityManager();
int minId = minimumEmployeeId(em);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
Map<String, Object> properties = new HashMap<String, Object>();
FetchGroup fg = new FetchGroup();
fg.addAttribute("id");
fg.addAttribute("version");
properties.put(QueryHints.FETCH_GROUP, fg);
Employee emp = em.find(Employee.class, minId, properties);
assertNotNull(emp);
assertFetched(emp, fg);
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertFetchedAttribute(emp, "id");
assertFetchedAttribute(emp, "version");
assertNotFetchedAttribute(emp, "firstName");
assertNotFetchedAttribute(emp, "lastName");
assertNotFetchedAttribute(emp, "gender");
assertNotFetchedAttribute(emp, "salary");
assertNotFetchedAttribute(emp, "startTime");
assertNotFetchedAttribute(emp, "endTime");
assertNotFetchedAttribute(emp, "period");
assertNotFetchedAttribute(emp, "address");
assertNotFetchedAttribute(emp, "manager");
assertNotFetchedAttribute(emp, "phoneNumbers");
assertNotFetchedAttribute(emp, "projects");
assertTrue(getFetchGroup(emp).getClass() == EntityFetchGroup.class);
Employee serEmp = serialize(emp);
assertNotNull(serEmp);
assertFetchedAttribute(serEmp, "id");
assertFetchedAttribute(serEmp, "version");
assertNotFetchedAttribute(serEmp, "firstName");
assertNotFetchedAttribute(serEmp, "lastName");
assertNotFetchedAttribute(serEmp, "gender");
assertNotFetchedAttribute(serEmp, "salary");
assertNotFetchedAttribute(serEmp, "startTime");
assertNotFetchedAttribute(serEmp, "endTime");
assertNotFetchedAttribute(serEmp, "period");
assertNotFetchedAttribute(serEmp, "address");
assertNotFetchedAttribute(serEmp, "manager");
assertNotFetchedAttribute(serEmp, "phoneNumbers");
assertNotFetchedAttribute(serEmp, "projects");
assertTrue(getFetchGroup(serEmp) instanceof EntityFetchGroup);
serEmp.setFirstName("Doug");
assertFetchedAttribute(serEmp, "firstName");
}
@Test
public void findEmptyFetchGroup_setUnfetchedSalary() throws Exception {
EntityManager em = createEntityManager();
int minId = minimumEmployeeId(em);
try {
beginTransaction(em);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
Map<String, Object> properties = new HashMap<String, Object>();
FetchGroup emptyFG = new FetchGroup();
properties.put(QueryHints.FETCH_GROUP, emptyFG);
Employee emp = em.find(Employee.class, minId, properties);
assertNotNull(emp);
assertFetched(emp, emptyFG);
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// Check Basics
assertFetchedAttribute(emp, "id");
assertFetchedAttribute(emp, "version");
assertNotFetchedAttribute(emp, "firstName");
assertNotFetchedAttribute(emp, "lastName");
assertNotFetchedAttribute(emp, "gender");
assertNotFetchedAttribute(emp, "salary");
assertNotFetchedAttribute(emp, "startTime");
assertNotFetchedAttribute(emp, "endTime");
if (emp.getPeriod() != null) {
assertFetchedAttribute(emp.getPeriod(), "startDate");
assertFetchedAttribute(emp.getPeriod(), "endDate");
}
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// Check Relationships
assertNotFetchedAttribute(emp, "address");
assertNotFetchedAttribute(emp, "manager");
assertNotFetchedAttribute(emp, "phoneNumbers");
assertNotFetchedAttribute(emp, "projects");
emp.setSalary(1);
assertFetchedAttribute(emp, "salary");
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp);
emp.getAddress();
assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp.getAddress());
emp.getPhoneNumbers().size();
assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
for (PhoneNumber phone : emp.getPhoneNumbers()) {
assertNoFetchGroup(phone);
}
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
/**
* Verify that attributes added to detached EntityFetchGroup are added using
* DetachedFetchItem
*/
@Test
public void verifyAddAttributeInDetachedEntityFetchGroup() {
EntityFetchGroup detFG = new EntityFetchGroup(new String[]{"basic", "a"});
//detFG.addAttribute("basic");
//detFG.addAttribute("a.b");
//assertNull(detFG.getParent());
assertEquals(2, detFG.getItems().size());
AttributeItem basicItem = detFG.getItem("basic");
assertNotNull(basicItem);
assertEquals("basic", basicItem.getAttributeName());
//assertTrue(basicItem instanceof DetachedFetchItem);
assertNull(basicItem.getGroup());
assertSame(detFG, basicItem.getParent());
//assertFalse(basicItem.useDefaultFetchGroup());
AttributeItem aItem = detFG.getItem("a");
assertNotNull(aItem);
assertEquals("a", aItem.getAttributeName());
//assertTrue(aItem instanceof DetachedFetchItem);
// serialized EntityFetchGroup is always flat - doesn't have nested groups.
//assertNull(aItem.getGroup());
assertNull(aItem.getGroup());
assertSame(detFG, aItem.getParent());
//assertFalse(aItem.useDefaultFetchGroup());
//assertTrue(aItem.getGroup() instanceof EntityFetchGroup);
//EntityFetchGroup aFG = (EntityFetchGroup) aItem.getGroup();
//assertEquals(1, aFG.getItems().size());
//AttributeItem bItem = aFG.getItem("b");
//assertNotNull(bItem);
//assertEquals("b", bItem.getAttributeName());
//assertTrue(bItem instanceof DetachedFetchItem);
//assertNull(bItem.getGroup());
//assertSame(aFG, bItem.getParent());
//assertFalse(bItem.useDefaultFetchGroup());
}
@Test
public void singleResultEmptyFetchGroup() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID");
query.setParameter("ID", minimumEmployeeId(em));
FetchGroup emptyFG = new FetchGroup();
query.setHint(QueryHints.FETCH_GROUP, emptyFG);
Employee emp = (Employee) query.getSingleResult();
assertNotNull(emp);
assertFetched(emp, emptyFG);
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// Check Basics
assertFetchedAttribute(emp, "id");
assertFetchedAttribute(emp, "version");
assertNotFetchedAttribute(emp, "firstName");
assertNotFetchedAttribute(emp, "lastName");
assertNotFetchedAttribute(emp, "gender");
assertNotFetchedAttribute(emp, "salary");
assertNotFetchedAttribute(emp, "startTime");
assertNotFetchedAttribute(emp, "endTime");
if (emp.getPeriod() != null) {
assertFetchedAttribute(emp.getPeriod(), "startDate");
assertFetchedAttribute(emp.getPeriod(), "endDate");
}
// Check Relationships
assertNotFetchedAttribute(emp, "address");
assertNotFetchedAttribute(emp, "manager");
assertNotFetchedAttribute(emp, "phoneNumbers");
assertNotFetchedAttribute(emp, "projects");
emp.getSalary();
assertFetchedAttribute(emp, "salary");
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp);
emp.getAddress();
assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp.getAddress());
emp.getPhoneNumbers().size();
assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
for (PhoneNumber phone : emp.getPhoneNumbers()) {
assertNoFetchGroup(phone);
}
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
/**
*
*/
@Test
public void resultListEmptyFetchGroup() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID");
query.setParameter("ID", minimumEmployeeId(em));
FetchGroup emptyFG = new FetchGroup();
query.setHint(QueryHints.FETCH_GROUP, emptyFG);
List<Employee> emps = query.getResultList();
assertNotNull(emps);
assertEquals(1, emps.size());
Employee emp = emps.get(0);
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertFetched(emp, emptyFG);
// Check Basics
assertFetchedAttribute(emp, "id");
assertFetchedAttribute(emp, "version");
assertNotFetchedAttribute(emp, "firstName");
assertNotFetchedAttribute(emp, "lastName");
assertNotFetchedAttribute(emp, "gender");
assertNotFetchedAttribute(emp, "salary");
assertNotFetchedAttribute(emp, "startTime");
assertNotFetchedAttribute(emp, "endTime");
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
if (emp.getPeriod() != null) {
assertFetchedAttribute(emp.getPeriod(), "startDate");
assertFetchedAttribute(emp.getPeriod(), "endDate");
}
// Check Relationships
assertNotFetchedAttribute(emp, "address");
assertNotFetchedAttribute(emp, "manager");
assertNotFetchedAttribute(emp, "phoneNumbers");
assertNotFetchedAttribute(emp, "projects");
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
emp.getSalary();
assertFetchedAttribute(emp, "salary");
assertNoFetchGroup(emp);
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp.getAddress());
assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
for (PhoneNumber phone : emp.getPhoneNumbers()) {
assertNoFetchGroup(phone);
}
assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
/**
*
*/
@Test
public void resultListPeriodFetchGroup() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID");
query.setParameter("ID", minimumEmployeeId(em));
FetchGroup fg = new FetchGroup();
fg.addAttribute("period");
query.setHint(QueryHints.FETCH_GROUP, fg);
List<Employee> emps = query.getResultList();
assertNotNull(emps);
assertEquals(1, emps.size());
Employee emp = emps.get(0);
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertFetched(emp, fg);
// Check Basics
assertFetchedAttribute(emp, "id");
assertFetchedAttribute(emp, "version");
assertNotFetchedAttribute(emp, "firstName");
assertNotFetchedAttribute(emp, "lastName");
assertNotFetchedAttribute(emp, "gender");
assertNotFetchedAttribute(emp, "salary");
assertNotFetchedAttribute(emp, "startTime");
assertNotFetchedAttribute(emp, "endTime");
if (emp.getPeriod() != null) {
assertFetchedAttribute(emp.getPeriod(), "startDate");
assertFetchedAttribute(emp.getPeriod(), "endDate");
}
// Check Relationships
assertNotFetchedAttribute(emp, "address");
assertNotFetchedAttribute(emp, "manager");
assertNotFetchedAttribute(emp, "phoneNumbers");
assertNotFetchedAttribute(emp, "projects");
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
emp.getSalary();
assertFetchedAttribute(emp, "salary");
assertNoFetchGroup(emp);
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp.getAddress());
assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
for (PhoneNumber phone : emp.getPhoneNumbers()) {
assertNoFetchGroup(phone);
}
assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
@Override
@Test
public void managerFetchGroup() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
// Use q query since find will only use default fetch group
//Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID");
//query.setParameter("ID", minimumEmployeeId(em));
// Complex where clause used to avoid triggering employees and their departments:
// Don't include employees who are managers themselves - otherwise if first selected as employee, then as e.manager the full read will be triggered;
// Don't include managers with departments - because there is no fetch group on e.manager its (non-null) department will trigger an extra sql
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.manager IS NOT NULL AND NOT EXISTS(SELECT e2 FROM Employee e2 WHERE e2.manager = e) AND e.manager.department IS NULL");
FetchGroup managerFG = new FetchGroup();
managerFG.addAttribute("manager");
query.setHint(QueryHints.FETCH_GROUP, managerFG);
assertNotNull(getFetchGroup(query));
assertSame(managerFG, getFetchGroup(query));
Employee emp = (Employee) query.getSingleResult();
assertFetched(emp, managerFG);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// manager (if not null) hasn't been instantiated yet.
int nSqlToAdd = 0;
if (emp.getManager() != null) {
assertFetchedAttribute(emp, "manager");
// additional sql to select the manager
nSqlToAdd++;
}
assertEquals(1 + nSqlToAdd, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
emp.getLastName();
assertEquals(2 + nSqlToAdd, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp);
for (PhoneNumber phone : emp.getPhoneNumbers()) {
assertNoFetchGroup(phone);
phone.getAreaCode();
}
assertEquals(3 + nSqlToAdd, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
@Test
public void managerFetchGroupWithJoinFetch() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
//int minId = minimumEmployeeId(em);
//assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// Use q query since find will only use default fetch group
//Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID");
//query.setParameter("ID", minId);
// Complex where clause used to avoid triggering employees and their departments:
// Don't include employees who are managers themselves - otherwise if first selected as employee, then as e.manager the full read will be triggered;
// Don't include managers with departments - because there is no fetch group on e.manager its (non-null) department will trigger an extra sql
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.manager IS NOT NULL AND NOT EXISTS(SELECT e2 FROM Employee e2 WHERE e2.manager = e) AND e.manager.department IS NULL");
FetchGroup managerFG = new FetchGroup();
managerFG.addAttribute("manager");
query.setHint(QueryHints.FETCH_GROUP, managerFG);
query.setHint(QueryHints.LEFT_FETCH, "e.manager");
assertNotNull(getFetchGroup(query));
assertSame(managerFG, getFetchGroup(query));
Employee emp = (Employee) query.getSingleResult();
assertFetched(emp, managerFG);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// manager has been already instantiated by the query.
emp.getManager();
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// instantiates the whiole object
emp.getLastName();
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp);
for (PhoneNumber phone : emp.getPhoneNumbers()) {
assertNoFetchGroup(phone);
phone.getAreaCode();
}
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
@Override
@Test
public void employeeNamesFetchGroup() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
int minId = minimumEmployeeId(em);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// Use q query since find will only use default fetch group
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID");
query.setParameter("ID", minId);
FetchGroup namesFG = new FetchGroup();
namesFG.addAttribute("firstName");
namesFG.addAttribute("lastName");
query.setHint(QueryHints.FETCH_GROUP, namesFG);
assertNotNull(getFetchGroup(query));
assertSame(namesFG, getFetchGroup(query));
Employee emp = (Employee) query.getSingleResult();
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertFetched(emp, namesFG);
emp.getId();
emp.getFirstName();
emp.getLastName();
emp.getVersion();
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertFetched(emp, namesFG);
emp.getGender();
emp.getSalary();
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp);
for (PhoneNumber phone : emp.getPhoneNumbers()) {
assertNoFetchGroup(phone);
phone.getAreaCode();
}
assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
if (emp.getManager() != null) {
assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertNoFetchGroup(emp.getManager());
} else {
// If manager_id field is null then getManager() does not trigger an sql.
assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
}
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
@Override
@Test
public void joinFetchEmployeeAddressWithDynamicFetchGroup() {
EntityManager em = createEntityManager();
Query query = em.createQuery("SELECT e FROM Employee e JOIN FETCH e.address");
FetchGroup fetchGroup = new FetchGroup("names");
fetchGroup.addAttribute("firstName");
fetchGroup.addAttribute("lastName");
query.setHint(QueryHints.FETCH_GROUP, fetchGroup);
List<Employee> emps = query.getResultList();
assertNotNull(emps);
}
@Override
@Test
public void joinFetchEmployeeAddressPhoneWithDynamicFetchGroup() {
EntityManager em = createEntityManager();
Query query = em.createQuery("SELECT e FROM Employee e JOIN FETCH e.address WHERE e.id IN (SELECT p.id FROM PhoneNumber p)");
FetchGroup fetchGroup = new FetchGroup("names");
fetchGroup.addAttribute("firstName");
fetchGroup.addAttribute("lastName");
query.setHint(QueryHints.FETCH_GROUP, fetchGroup);
List<Employee> emps = query.getResultList();
assertNotNull(emps);
}
@Test
public void verifyUnfetchedAttributes() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
TypedQuery<Employee> q = em.createQuery("SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(p.id) FROM PhoneNumber p)", Employee.class);
FetchGroup fg = new FetchGroup("Employee.empty");
q.setHint(QueryHints.FETCH_GROUP, fg);
Employee emp = q.getSingleResult();
assertNotNull(emp);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// This check using the mapping returns a default (empty) IndirectList
/*OneToManyMapping phoneMapping = (OneToManyMapping) employeeDescriptor.getMappingForAttributeName("phoneNumbers");
IndirectList phones = (IndirectList) phoneMapping.getAttributeValueFromObject(emp);
assertNotNull(phones);
assertTrue(phones.isInstantiated());
assertEquals(0, phones.size());
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());*/
IndirectList phonesIL = (IndirectList) emp.getPhoneNumbers();
assertFalse(phonesIL.isInstantiated());
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertTrue(emp.getPhoneNumbers().size() > 0);
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
@Test
public void verifyFetchedRelationshipAttributes() throws Exception {
EntityManager em = createEntityManager();
FetchGroup fg = new FetchGroup("Employee.relationships");
fg.addAttribute("address");
fg.addAttribute("phoneNumbers");
fg.addAttribute("manager");
fg.addAttribute("projects");
Map<String, Object> hints = new HashMap<String, Object>();
hints.put(QueryHints.FETCH_GROUP, fg);
Employee emp = minimumEmployee(em, hints);
assertNotNull(emp);
}
@Test
public void simpleSerializeAndMerge() throws Exception {
EntityManager em = createEntityManager();
int id = minEmployeeIdWithAddressAndPhones(em);
HashMap<String, PhoneNumber> phonesOriginal = new HashMap();
// save the original Employee for clean up
Employee empOriginal = em.find(Employee.class, id);
for(PhoneNumber phone : empOriginal.getPhoneNumbers()) {
phonesOriginal.put(phone.getType(), phone);
}
closeEntityManager(em);
clearCache();
int newSalary = empOriginal.getSalary() * 2;
if(newSalary == 0) {
newSalary = 100;
}
em = createEntityManager();
beginTransaction(em);
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = "+id);
FetchGroup fetchGroup = new FetchGroup("names");
fetchGroup.addAttribute("firstName");
fetchGroup.addAttribute("lastName");
fetchGroup.addAttribute("address.country");
fetchGroup.addAttribute("phoneNumbers.areaCode");
fetchGroup.addAttribute("phoneNumbers.owner.id");
fetchGroup.setShouldLoad(true);
query.setHint(QueryHints.FETCH_GROUP, fetchGroup);
Employee emp = (Employee)query.getSingleResult();
Employee empSerialized;
Employee empDeserialized;
Employee empMerged;
try {
empSerialized = serialize(emp);
assertFetched(empSerialized, fetchGroup);
empSerialized.setFirstName("newFirstName");
empSerialized.setLastName("newLastName");
// salary is not in the original fetchGroup
empSerialized.setSalary(newSalary);
FetchGroup extendedFetchGroup = fetchGroup.clone();
extendedFetchGroup.addAttribute("salary");
assertFetched(empSerialized, extendedFetchGroup);
empSerialized.getAddress().setCountry("newCountry");
assertFetched(empSerialized.getAddress(), fetchGroup.getGroup("address"));
// address.city is not in the original fetchGroup
empSerialized.getAddress().setCity("newCity");
extendedFetchGroup.addAttribute("address.city");
assertFetched(empSerialized.getAddress(), extendedFetchGroup.getGroup("address"));
// phoneNumbers.number is not in the original fetchGroup
extendedFetchGroup.addAttribute("phoneNumbers.number");
for(PhoneNumber phone : empSerialized.getPhoneNumbers()) {
phone.setAreaCode("000");
assertFetched(phone, fetchGroup.getGroup("phoneNumbers"));
// phoneNumbers.number is not in the original fetchGroup
phone.setNumber("0000000");
assertFetched(phone, extendedFetchGroup.getGroup("phoneNumbers"));
}
empDeserialized = serialize(empSerialized);
assertFetched(empDeserialized, extendedFetchGroup);
assertFetched(empDeserialized.getAddress(), extendedFetchGroup.getGroup("address"));
for(PhoneNumber phone : empDeserialized.getPhoneNumbers()) {
assertFetched(phone, extendedFetchGroup.getGroup("phoneNumbers"));
}
empMerged = em.merge(empDeserialized);
// verify merged in em
assertEquals("newFirstName", empMerged.getFirstName());
assertEquals("newLastName", empMerged.getLastName());
assertEquals(newSalary, empMerged.getSalary());
assertEquals("newCountry", empMerged.getAddress().getCountry());
assertEquals("newCity", empMerged.getAddress().getCity());
for(PhoneNumber phone : empSerialized.getPhoneNumbers()) {
assertEquals("000", phone.getAreaCode());
assertEquals("0000000", phone.getNumber());
}
// verify that the attributes outside of the fetch group not nullified.
assertEquals(empOriginal.getGender(), empMerged.getGender());
if(empOriginal.getDepartment() != null) {
assertEquals(empOriginal.getDepartment().getId(), empMerged.getDepartment().getId());
}
if(empOriginal.getPeriod() != null) {
assertEquals(empOriginal.getPeriod().getStartDate(), empMerged.getPeriod().getStartDate());
assertEquals(empOriginal.getPeriod().getEndDate(), empMerged.getPeriod().getEndDate());
}
assertEquals(empOriginal.getPayScale(), empMerged.getPayScale());
assertEquals(empOriginal.getStartTime(), empMerged.getStartTime());
assertEquals(empOriginal.getEndTime(), empMerged.getEndTime());
commitTransaction(em);
} finally {
if(isTransactionActive(em)) {
rollbackTransaction(em);
}
}
// verify merged in the shared cache - clear em cache, query using cache only.
em.clear();
Map<String, Object> hints = new HashMap<String, Object>(2);
hints.put(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheOnly);
//hints.put(QueryHints.FETCH_GROUP, fetchGroup);
Employee empShared = em.find(Employee.class, id, hints);
assertEquals("newFirstName", empShared.getFirstName());
assertEquals("newLastName", empShared.getLastName());
assertEquals(newSalary, empShared.getSalary());
assertEquals("newCountry", empShared.getAddress().getCountry());
assertEquals("newCity", empShared.getAddress().getCity());
for(PhoneNumber phone : empShared.getPhoneNumbers()) {
assertEquals("000", phone.getAreaCode());
assertEquals("0000000", phone.getNumber());
}
// verify merged in shared the db - clear both em and shared caches.
// Must read through the old EntityManager - the changes haven't been committed in the db.
clearCache();
em.clear();
Employee empDb = em.find(Employee.class, id);
assertEquals("newFirstName", empDb.getFirstName());
assertEquals("newLastName", empDb.getLastName());
assertEquals(newSalary, empDb.getSalary());
assertEquals("newCountry", empDb.getAddress().getCountry());
assertEquals("newCity", empDb.getAddress().getCity());
for(PhoneNumber phone : empDb.getPhoneNumbers()) {
assertEquals("000", phone.getAreaCode());
assertEquals("0000000", phone.getNumber());
}
// clean up
beginTransaction(em);
try {
empDb.setFirstName(empOriginal.getFirstName());
empDb.setLastName(empOriginal.getLastName());
empDb.setSalary(empOriginal.getSalary());
empDb.getAddress().setCountry(empOriginal.getAddress().getCountry());
empDb.getAddress().setCity(empOriginal.getAddress().getCity());
for(PhoneNumber phone : empDb.getPhoneNumbers()) {
PhoneNumber phoneOriginal = phonesOriginal.get(phone.getType());
phone.setAreaCode(phoneOriginal.getAreaCode());
phone.setNumber(phoneOriginal.getNumber());
}
commitTransaction(em);
} finally {
if(isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
/*public void simpleSerializeAndMerge() throws Exception {
EntityManager em = createEntityManager();
int id = minimumEmployeeId(em);
// save the original Employee for clean up
Employee empOriginal = em.find(Employee.class, id);
closeEntityManager(em);
clearCache();
int newSalary = empOriginal.getSalary() * 2;
if(newSalary == 0) {
newSalary = 100;
}
em = createEntityManager();
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = "+id);
FetchGroup fetchGroup = new FetchGroup("names");
fetchGroup.addAttribute("firstName");
fetchGroup.addAttribute("lastName");
query.setHint(QueryHints.FETCH_GROUP, fetchGroup);
Employee emp = (Employee)query.getSingleResult();
Employee empSerialized;
Employee empDeserialized;
Employee empMerged;
beginTransaction(em);
try {
empSerialized = serialize(emp);
empSerialized.setFirstName("newFirstName");
empSerialized.setLastName("newLastName");
empSerialized.setSalary(newSalary);
empDeserialized = serialize(empSerialized);
empMerged = em.merge(empDeserialized);
// verify merged in em
assertEquals("newFirstName", empMerged.getFirstName());
assertEquals("newLastName", empMerged.getLastName());
assertEquals(newSalary, empMerged.getSalary());
commitTransaction(em);
} finally {
if(isTransactionActive(em)) {
rollbackTransaction(em);
}
}
// verify merged in the shared cache - clear em cache, query using cache only.
em.clear();
HashMap hints = new HashMap(2);
hints.put(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheOnly);
hints.put(QueryHints.FETCH_GROUP, fetchGroup);
Employee empShared = em.find(Employee.class, id, hints);
assertEquals("newFirstName", empShared.getFirstName());
assertEquals("newLastName", empShared.getLastName());
assertEquals(newSalary, empShared.getSalary());
// verify merged in shared the db - clear both em and shared caches.
// Must read through the old EntityManager - the changes haven't been committed in the db.
clearCache();
em.clear();
Employee empDb = em.find(Employee.class, id);
assertEquals("newFirstName", empDb.getFirstName());
assertEquals("newLastName", empDb.getLastName());
assertEquals(newSalary, empDb.getSalary());
beginTransaction(em);
try {
empDb.setFirstName(empOriginal.getFirstName());
empDb.setLastName(empOriginal.getLastName());
empDb.setSalary(empOriginal.getSalary());
commitTransaction(em);
} finally {
if(isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}*/
public void partialMerge() throws Exception {
EntityManager em = createEntityManager();
// Search for an Employee with an Address and Phone Numbers
try {
beginTransaction(em);
TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e WHERE e.address IS NOT NULL AND e.id IN (SELECT MIN(p.id) FROM PhoneNumber p)", Employee.class);
// Load only its names and phone Numbers
FetchGroup fetchGroup = new FetchGroup();
fetchGroup.addAttribute("firstName");
fetchGroup.addAttribute("lastName");
FetchGroup phonesFG = phoneDescriptor.getFetchGroupManager().createFullFetchGroup();
// that ensures the owner is not instantiated
phonesFG.addAttribute("owner.id");
phonesFG.removeAttribute("status");
phonesFG.setShouldLoad(true);
fetchGroup.addAttribute("phoneNumbers", phonesFG);
// Make sure the FetchGroup also forces the relationships to be loaded
fetchGroup.setShouldLoad(true);
query.setHint(QueryHints.FETCH_GROUP, fetchGroup);
Employee emp = query.getSingleResult();
// Detach Employee through Serialization
Employee detachedEmp = serialize(emp);
// Modify the detached Employee inverting the names, adding a phone number, and setting the salary
detachedEmp.setFirstName(emp.getLastName());
detachedEmp.setLastName(emp.getFirstName());
detachedEmp.addPhoneNumber(new PhoneNumber("TEST", "999", "999999"));
// NOte that salary was not part of the original FetchGroupdetachedEmp.setSalary(1);
detachedEmp.setSalary(1);
// Merge the detached employee
em.merge(detachedEmp);
// Flush the changes to the database
em.flush();
} finally {
rollbackTransaction(em);
closeEntityManager(em);
}
}
public void copyGroupObjectGraph() {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
TypedQuery<Department> query = em.createQuery("SELECT d FROM ADV_DEPT d WHERE d.id IN (SELECT MIN(dd.id) FROM ADV_DEPT dd)", Department.class);
Department dept = query.getSingleResult();
CopyGroup group = new CopyGroup();
group.addAttribute("name");
group.addAttribute("employees", new CopyGroup());
group.getGroup("employees").addAttribute("name");
group.getGroup("employees").addAttribute("department", new CopyGroup());
group.getGroup("employees").getGroup("department").addAttribute("name");
Department deptCopy = (Department) em.unwrap(JpaEntityManager.class).copy(dept, group);
String reportCG = group.toString();
assertNotNull(reportCG);
} finally {
rollbackTransaction(em);
closeEntityManager(em);
}
}
public void copyGroupMerge() {
// Search for an Employee with an Address and Phone Numbers
EntityManager em = createEntityManager();
try {
beginTransaction(em);
TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(ee.id) FROM Employee ee)", Employee.class);
Employee emp = query.getSingleResult();
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
System.out.println(">>> Employee retrieved");
// Copy only its names and phone Numbers
AttributeGroup group = new CopyGroup();
group.addAttribute("firstName");
group.addAttribute("lastName");
group.addAttribute("address");
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
System.out.println(">>> Employee copied");
// Modify the detached Employee inverting the names, adding a phone number, and setting the salary
empCopy.setFirstName(emp.getLastName());
empCopy.setLastName(emp.getFirstName());
// Note that salary was not part of the original FetchGroup
//empCopy.setSalary(1);
// Merge the detached employee
em.merge(empCopy);
System.out.println(">>> Sparse merge complete");
// Flush the changes to the database
em.flush();
System.out.println(">>> Flush complete");
} finally {
rollbackTransaction(em);
closeEntityManager(em);
}
}
public void copyGroupMerge2() {
// Search for an Employee with an Address and Phone Numbers
EntityManager em = createEntityManager();
try {
beginTransaction(em);
TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e", Employee.class);
query.setHint(QueryHints.BATCH, "e.address");
List<Employee> employees = query.getResultList();
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
System.out.println(">>> Employees retrieved");
// Copy only its names and phone Numbers
AttributeGroup group = new CopyGroup();
group.addAttribute("firstName");
group.addAttribute("lastName");
group.addAttribute("address");
List<Employee> employeesCopy = (List<Employee>) em.unwrap(JpaEntityManager.class).copy(employees, group);
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
System.out.println(">>> Employees copied");
for(Employee empCopy : employeesCopy) {
// Modify the detached Employee inverting the names, adding a phone number, and setting the salary
String firstName = empCopy.getFirstName();
String lastName = empCopy.getLastName();
empCopy.setFirstName(lastName);
empCopy.setLastName(firstName);
// Note that salary was not part of the original FetchGroup
//empCopy.setSalary(1);
// Merge the detached employee
em.merge(empCopy);
}
System.out.println(">>> Sparse merge complete");
// Flush the changes to the database
em.flush();
System.out.println(">>> Flush complete");
} finally {
rollbackTransaction(em);
closeEntityManager(em);
}
}
@Test
public void copyWithPk() {
copyWithOrWithoutPk(false, false);
}
@Test
public void copyWithPkUseFullGroup() {
copyWithOrWithoutPk(false, true);
}
@Test
public void copyWithoutPk() {
copyWithOrWithoutPk(true, false);
}
@Test
public void copyWithoutPkUseFullGroup() {
copyWithOrWithoutPk(true, true);
}
void copyWithOrWithoutPk(boolean noPk, boolean useFullGroup) {
CopyGroup group = new CopyGroup();
if(noPk) {
// setShouldResetPrimaryKey set to true means that:
// pk would not be copied - unless explicitly specified in the group;
// copy would not have a fetch group
group.setShouldResetPrimaryKey(true);
// setShouldResetVersion set to true means that
// version would not be copied - unless explicitly specified in the group;
group.setShouldResetVersion(true);
}
group.cascadeTree(); // Copy only the attributes specified
// note that
// default value shouldResetPrimaryKey==false causes pk to copied, too;
// default value shouldResetVersion==false causes version to be copied, too.
group.addAttribute("firstName");
group.addAttribute("lastName");
group.addAttribute("gender");
group.addAttribute("period");
group.addAttribute("salary");
if(useFullGroup) {
// copy group contains all the attributes defined in Address class
CopyGroup address = addressDescriptor.getFetchGroupManager().createFullFetchGroup().toCopyGroup();
// attribute "employees" removed from the group
address.removeAttribute("employees");
if(noPk) {
// pk attribute "ID" removed from the group - not that it's necessary to explicitly remove it:
// setShouldResetPrimaryKey(true) would not remove explicitly specified in the group pk.
address.removeAttribute("ID");
// note that default value shouldResetPrimaryKey==false would have resulted in copying the pk (that would be equivalent of adding the removed "ID" attribute back to the group).
address.setShouldResetPrimaryKey(true);
address.setShouldResetVersion(true);
}
group.addAttribute("address", address);
// copy group contains all the attributes defined in PhoneNumber class
CopyGroup phones = phoneDescriptor.getFetchGroupManager().createFullFetchGroup().toCopyGroup();
if(noPk) {
// the only goal of setting shouldResetPrimaryKey to true here is to avoid a FetchGroup being assigned to phone's copy.
// Note that because both pk components ("owner" and "type") are part of the copy group they still will be copied:
// the phone's copy will have the same type as original and it's owner will be original's owner's copy.
phones.setShouldResetPrimaryKey(true);
}
if(!noPk) {
// to avoid instantiating the whole owner
phones.addAttribute("owner.id");
}
group.addAttribute("phoneNumbers", phones);
} else {
// implicitly created sub CopyGroups address and phoneNumbers will have the same shouldReset flags values as their master CopyGroup.
group.addAttribute("address.country");
group.addAttribute("address.province");
group.addAttribute("address.street");
group.addAttribute("address.postalCode");
group.addAttribute("address.city");
if(noPk) {
group.addAttribute("phoneNumbers.owner");
} else {
// to avoid instantiating the whole owner
group.addAttribute("phoneNumbers.owner.id");
}
group.addAttribute("phoneNumbers.type");
group.addAttribute("phoneNumbers.id");
group.addAttribute("phoneNumbers.areaCode");
group.addAttribute("phoneNumbers.number");
}
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Employee emp = minimumEmployee(em);
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);
if(noPk) {
assertNoFetchGroup(empCopy);
assertNoFetchGroup(empCopy.getAddress());
for(PhoneNumber phoneCopy : empCopy.getPhoneNumbers()) {
assertNoFetchGroup(phoneCopy);
}
// Persist the employee copy
em.persist(empCopy);
} else {
FetchGroup fetchGroup = group.toFetchGroup();
// the following call adds pk and version, verifies that all attribute names correct.
employeeDescriptor.getFetchGroupManager().prepareAndVerify(fetchGroup);
// copyEmp, its address and phones each should have an EntityFetchGroup corresponding to the respective copyGroup.
assertFetched(empCopy, fetchGroup);
EntityFetchGroup addressEntityFetchGroup = addressDescriptor.getFetchGroupManager().getEntityFetchGroup(fetchGroup.getGroup("address"));
if(addressEntityFetchGroup == null) {
assertNoFetchGroup(empCopy.getAddress());
} else {
assertFetched(empCopy.getAddress(), addressEntityFetchGroup);
}
EntityFetchGroup phonesEntityFetchGroup = phoneDescriptor.getFetchGroupManager().getEntityFetchGroup(fetchGroup.getGroup("phoneNumbers"));
for(PhoneNumber phoneCopy : empCopy.getPhoneNumbers()) {
if(phonesEntityFetchGroup == null) {
assertNoFetchGroup(phoneCopy);
} else {
assertFetched(phoneCopy, phonesEntityFetchGroup);
}
}
// to cause updates let's change something:
// in Employee table
empCopy.setFirstName((empCopy.getFirstName() != null ? empCopy.getFirstName() : "") + "_NEW");
// in Salary table
empCopy.setSalary(empCopy.getSalary() * 2 + 1);
// in Address
empCopy.getAddress().setCountry((empCopy.getAddress().getCountry() != null ? empCopy.getAddress().getCountry() : "") + "_NEW");
// in each Phone
for(PhoneNumber phoneCopy : empCopy.getPhoneNumbers()) {
if(phoneCopy.getAreaCode() != null && phoneCopy.getAreaCode().equals("000")) {
phoneCopy.setAreaCode("111");
} else {
phoneCopy.setAreaCode("000");
}
}
em.merge(empCopy);
}
// Insert a new row into Employee, Salary, Address, and a row for each Phone
int nExpectedInsertsOrUpdates = 3 + empCopy.getPhoneNumbers().size();
int nExpectedInserts, nExpectedUpdates;
if(noPk) {
nExpectedInserts = nExpectedInsertsOrUpdates;
// table sequence might have been updated
nExpectedUpdates = getQuerySQLTracker(em).getTotalSQLUPDATECalls();
} else {
nExpectedInserts = 0;
nExpectedUpdates = nExpectedInsertsOrUpdates;
}
// Flush the changes to the database
em.flush();
assertEquals(nExpectedInserts, getQuerySQLTracker(em).getTotalSQLINSERTCalls());
assertEquals(nExpectedUpdates, getQuerySQLTracker(em).getTotalSQLUPDATECalls());
} finally {
if(isTransactionActive(em)) {
rollbackTransaction(em);
}
}
}
@Test
public void copyNoCascade() {
copyCascade(CopyGroup.NO_CASCADE);
}
@Test
public void copyCascadePrivateParts() {
copyCascade(CopyGroup.CASCADE_PRIVATE_PARTS);
}
@Test
public void copyCascadeAllParts() {
copyCascade(CopyGroup.CASCADE_ALL_PARTS);
}
void copyCascade(int cascadeDepth) {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Query query = em.createQuery("SELECT e FROM Employee e");
List<Employee> employees = query.getResultList();
CopyGroup group = new CopyGroup();
if(cascadeDepth == CopyGroup.NO_CASCADE) {
group.dontCascade();
} else if(cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS) {
// default cascade depth setting
group.cascadePrivateParts();
} else if(cascadeDepth == CopyGroup.CASCADE_ALL_PARTS) {
group.cascadeAllParts();
} else {
fail("Invalid cascadeDepth = " + cascadeDepth);
}
group.setShouldResetPrimaryKey(true);
List<Employee> employeesCopy;
if(cascadeDepth == CopyGroup.NO_CASCADE || cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS) {
// In this case the objects should be copied one by one - each one using a new CopyGroup.
// That ensures most referenced object are original ones (not copies) -
// the only exception is privately owned objects in CASCADE_PRIVATE_PARTS case.
employeesCopy = new ArrayList(employees.size());
for(Employee emp : employees) {
CopyGroup groupClone = group.clone();
employeesCopy.add((Employee)em.unwrap(JpaEntityManager.class).copy(emp, groupClone));
}
} else {
// cascadeDepth == CopyGroup.CASCADE_ALL_PARTS
// In this case all objects should be copied using a single CopyGroup.
// That ensures identities of the copies:
// for instance if several employees referenced the same project,
// then all copies of these employees will reference the single copy of the project.
employeesCopy = (List)em.unwrap(JpaEntityManager.class).copy(employees, group);
}
// IdentityHashSets will be used to verify copy identities
IdentityHashSet originalEmployees = new IdentityHashSet();
IdentityHashSet copyEmployees = new IdentityHashSet();
IdentityHashSet originalAddresses = new IdentityHashSet();
IdentityHashSet copyAddresses = new IdentityHashSet();
IdentityHashSet originalProjects = new IdentityHashSet();
IdentityHashSet copyProjects = new IdentityHashSet();
IdentityHashSet originalPhones = new IdentityHashSet();
IdentityHashSet copyPhones = new IdentityHashSet();
int size = employees.size();
for(int i=0; i < size; i++) {
Employee emp = employees.get(i);
Employee empCopy = employeesCopy.get(i);
if(cascadeDepth == CopyGroup.CASCADE_ALL_PARTS) {
originalEmployees.add(emp);
copyEmployees.add(empCopy);
} else {
// cascadeDepth == CopyGroup.NO_CASCADE || cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS
// In this case all Employees referenced by empCopyes are originals (manager and managed employees).
// Therefore if we add here each emp and empCopy to originalEmployees and copyEmployees respectively
// then copyEmployees will always contain all original managers and managed + plus all copies.
// Therefore in this case originalEmployees and copyEmployees will contain only references (managers and managed employees).
}
if(emp.getAddress() == null) {
assertTrue("emp.getAddress() == null, but empCopy.getAddress() != null", empCopy.getAddress() == null);
} else {
originalAddresses.add(emp.getAddress());
copyAddresses.add(empCopy.getAddress());
if(cascadeDepth == CopyGroup.NO_CASCADE || cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS) {
assertTrue("address has been copied", emp.getAddress() == empCopy.getAddress());
} else {
// cascadeDepth == CopyGroup.CASCADE_ALL_PARTS
assertFalse("address has not been copied", emp.getAddress() == empCopy.getAddress());
}
}
boolean same;
for(Project project : emp.getProjects()) {
originalProjects.add(project);
same = false;
for(Project projectCopy : empCopy.getProjects()) {
copyProjects.add(projectCopy);
if(!same && project == projectCopy) {
same = true;
}
}
if(cascadeDepth == CopyGroup.NO_CASCADE || cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS) {
assertTrue("project has been copied", same);
} else {
// cascadeDepth == CopyGroup.CASCADE_ALL_PARTS
assertFalse("project has not been copied", same);
}
}
for(Employee managedEmp : emp.getManagedEmployees()) {
originalEmployees.add(managedEmp);
same = false;
for(Employee managedEmpCopy : empCopy.getManagedEmployees()) {
copyEmployees.add(managedEmpCopy);
if(!same && managedEmp == managedEmpCopy) {
same = true;
}
}
if(cascadeDepth == CopyGroup.NO_CASCADE || cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS) {
assertTrue("managedEmployee has been copied", same);
} else {
// cascadeDepth == CopyGroup.CASCADE_ALL_PARTS
assertFalse("managedEmployee has not been copied", same);
}
}
if(emp.getManager() == null) {
assertTrue("emp.getManager() == null, but empCopy.getManager() != null", empCopy.getManager() == null);
} else {
originalEmployees.add(emp.getManager());
copyEmployees.add(empCopy.getManager());
if(cascadeDepth == CopyGroup.NO_CASCADE || cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS) {
assertTrue("manager has been copied", emp.getManager() == empCopy.getManager());
} else {
// cascadeDepth == CopyGroup.CASCADE_ALL_PARTS
assertFalse("manager has not been copied", emp.getManager() == empCopy.getManager());
}
}
// phoneNumbers is privately owned
for(PhoneNumber phone : emp.getPhoneNumbers()) {
originalPhones.add(phone);
same = false;
for(PhoneNumber phoneCopy : empCopy.getPhoneNumbers()) {
copyPhones.add(phoneCopy);
if(!same && phone == phoneCopy) {
same = true;
}
}
if(cascadeDepth == CopyGroup.NO_CASCADE) {
assertTrue("phone has been copied", same);
} else {
// cascadeDepth == CopyGroup.CASCADE_ALL_PARTS || cascadeDepth == CopyGroup.CASCADE_PRIVATE_PARTS
assertFalse("phone has not been copied", same);
}
}
}
assertTrue("copyEmployees.size() == " + copyEmployees.size() + "; was expected " + originalEmployees.size(), originalEmployees.size() == copyEmployees.size());
assertTrue("copyAddresses.size() == " + copyAddresses.size() + "; was expected " + originalAddresses.size(), originalAddresses.size() == copyAddresses.size());
assertTrue("copyProjects.size() == " + copyProjects.size() + "; was expected " + originalProjects.size(), originalProjects.size() == copyProjects.size());
assertTrue("copyPhones.size() == " + copyPhones.size() + "; was expected " + originalPhones.size(), originalPhones.size() == copyPhones.size());
} finally {
if(isTransactionActive(em)) {
rollbackTransaction(em);
}
}
}
@Test
public void attrAndVHContainSameObjectAfterGetRealAttributeValue() throws Exception {
String errorMsg = "";
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.address IS NOT NULL and e.manager IS NOT NULL");
List<Employee> employees = query.getResultList();
OneToOneMapping addressMapping = (OneToOneMapping)employeeDescriptor.getMappingForAttributeName("address");
OneToOneMapping managerMapping = (OneToOneMapping)employeeDescriptor.getMappingForAttributeName("manager");
InstanceVariableAttributeAccessor addressAccessor = new InstanceVariableAttributeAccessor();
addressAccessor.setAttributeName("m_address");
addressAccessor.initializeAttributes(Employee.class);
InstanceVariableAttributeAccessor managerAccessor = new InstanceVariableAttributeAccessor();
managerAccessor.setAttributeName("manager");
managerAccessor.initializeAttributes(Employee.class);
AbstractSession session = (AbstractSession)((EntityManagerImpl)em.getDelegate()).getActiveSession();
for(Employee emp : employees) {
String localErrorMsg = "";
Address addressVH = (Address)addressMapping.getRealAttributeValueFromObject(emp, session);
Address addressAttr = (Address)addressAccessor.getAttributeValueFromObject(emp);
if(addressVH != addressAttr) {
localErrorMsg += "\n\taddressVH = " + addressVH + " != addressAttr = " + addressAttr;
}
Employee managerVH = (Employee)managerMapping.getRealAttributeValueFromObject(emp, session);
Employee managerAttr = (Employee)managerAccessor.getAttributeValueFromObject(emp);
if(managerVH != managerAttr) {
localErrorMsg += "\n\tmanagerVH = " + managerVH + " != managerAttr = " + managerAttr;
}
if(localErrorMsg.length() > 0) {
localErrorMsg = emp.toString() + localErrorMsg + "\n";
errorMsg += localErrorMsg;
}
}
} finally {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
closeEntityManager(em);
}
if(errorMsg.length() > 0) {
errorMsg = '\n' + errorMsg;
fail(errorMsg);
}
}
@Test
public void copyEmbedded() {
Employee emp = new Employee();
emp.setPeriod(new EmploymentPeriod(Helper.dateFromYearMonthDate(1993, 0, 1), Helper.dateFromYearMonthDate(1996, 11, 31)));
CopyGroup group = new CopyGroup();
group.addAttribute("period");
EntityManager em = createEntityManager();
Employee empCopy = (Employee) em.unwrap(ServerSession.class).copy(emp, group);
assertTrue("!emp.getPeriod().equals(empCopy.getPeriod())", emp.getPeriod().equals(empCopy.getPeriod()));
assertHasFetchGroup(empCopy);
}
private <T> T serialize(Serializable entity) throws IOException, ClassNotFoundException {
byte[] bytes = SerializationHelper.serialize(entity);
ObjectInputStream inStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
return (T) inStream.readObject();
}
}