/******************************************************************************* | |
* Copyright (c) 2005, 2009 SAP. All rights reserved. | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 | |
* which accompanies this distribution. | |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | |
* and the Eclipse Distribution License is available at | |
* http://www.eclipse.org/org/documents/edl-v10.php. | |
* | |
* Contributors: | |
* SAP - initial API and implementation | |
******************************************************************************/ | |
package org.eclipse.persistence.testing.tests.wdf.jpa1.relation; | |
import java.io.IOException; | |
import java.sql.Connection; | |
import java.sql.PreparedStatement; | |
import java.sql.ResultSet; | |
import java.sql.SQLException; | |
import java.util.Collections; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.Set; | |
import javax.persistence.EntityManager; | |
import javax.sql.DataSource; | |
import org.eclipse.persistence.testing.framework.wdf.AbstractBaseTest; | |
import org.eclipse.persistence.testing.framework.wdf.Bugzilla; | |
import org.eclipse.persistence.testing.framework.wdf.JPAEnvironment; | |
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Bicycle; | |
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Department; | |
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Employee; | |
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Item_Byte; | |
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Project; | |
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.TravelProfile; | |
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Vehicle; | |
import org.eclipse.persistence.testing.tests.wdf.jpa1.JPA1Base; | |
import org.junit.Before; | |
import org.junit.Test; | |
@SuppressWarnings("unchecked") | |
public class TestBidirectionalManyToMany extends JPA1Base { | |
private static final int HANS_ID_VALUE = 1; | |
private static final Integer HANS_ID = new Integer(HANS_ID_VALUE); | |
private static final Set<Pair> HANS_SET = new HashSet<Pair>(); | |
private static final int FRED_ID_VALUE = 2; | |
private static final Integer FRED_ID = new Integer(FRED_ID_VALUE); | |
private static final Set<Pair> FRED_SET = new HashSet<Pair>(); | |
private static final Set<Pair> SEED_SET = new HashSet<Pair>(); | |
private static final Project PUHLEN = new Project("G\u00fcrteltiere puhlen"); | |
private static final Project PINSELN = new Project("B\u00e4uche pinseln"); | |
private static final Project FALTEN = new Project("Zitronen falten"); | |
private static final Project ZAEHLEN = new Project("Erbsen z\u00e4hlen"); | |
private static final Project LINKEN = new Project("Eclipse Linken"); | |
@Before | |
public void initData() throws SQLException { | |
/* | |
* 5 Projects: | |
*/ | |
clearAllTables(); | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
env.beginTransaction(em); | |
Department dep = new Department(17, "diverses"); | |
em.persist(dep); | |
em.persist(PUHLEN); | |
em.persist(PINSELN); | |
em.persist(FALTEN); | |
em.persist(ZAEHLEN); | |
em.persist(LINKEN); | |
Employee hans = new Employee(HANS_ID_VALUE, "Hans", "Wurst", dep); | |
Set<Project> hansProjects = new HashSet<Project>(); | |
hansProjects.add(PUHLEN); | |
hansProjects.add(PINSELN); | |
hansProjects.add(FALTEN); | |
hans.setProjects(hansProjects); | |
Employee fred = new Employee(FRED_ID_VALUE, "Fred von", "Jupiter", dep); | |
Set<Project> fredProjects = new HashSet<Project>(); | |
fredProjects.add(FALTEN); | |
fredProjects.add(ZAEHLEN); | |
fred.setProjects(fredProjects); | |
em.persist(hans); | |
em.persist(fred); | |
env.commitTransactionAndClear(em); | |
HANS_SET.clear(); | |
for (final Project element : hansProjects) { | |
HANS_SET.add(new Pair(HANS_ID_VALUE, element.getId().intValue())); | |
} | |
FRED_SET.clear(); | |
for (final Project element : fredProjects) { | |
FRED_SET.add(new Pair(FRED_ID_VALUE, element.getId().intValue())); | |
} | |
SEED_SET.clear(); | |
SEED_SET.addAll(HANS_SET); | |
SEED_SET.addAll(FRED_SET); | |
} finally { | |
env.evictAll(em); | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testUnchanged() throws SQLException { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
// 1. do nothing | |
env.beginTransaction(em); | |
Employee emp = em.find(Employee.class, HANS_ID); | |
// do nothing | |
emp.clearPostUpdate(); | |
env.commitTransactionAndClear(em); | |
verify(!emp.postUpdateWasCalled(), "postUpdate was called"); | |
checkJoinTable(SEED_SET); | |
// 2. touch the projects | |
env.beginTransaction(em); | |
emp = em.find(Employee.class, HANS_ID); | |
emp.getProjects().size(); | |
emp.clearPostUpdate(); | |
env.commitTransactionAndClear(em); | |
verify(!emp.postUpdateWasCalled(), "postUpdate was called"); | |
checkJoinTable(SEED_SET); | |
// 3. trivial update | |
env.beginTransaction(em); | |
emp = em.find(Employee.class, HANS_ID); | |
Set<Project> projects = emp.getProjects(); | |
emp.setProjects(new HashSet<Project>(projects)); | |
emp.clearPostUpdate(); | |
env.commitTransactionAndClear(em); | |
verify(!emp.postUpdateWasCalled(), "postUpdate was called"); | |
checkJoinTable(SEED_SET); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
private void checkJoinTable(final Set<Pair> expected) throws SQLException { | |
DataSource ds = getEnvironment().getDataSource(); | |
Connection conn = ds.getConnection(); | |
Set<Pair> actual = new HashSet<Pair>(); | |
try { | |
PreparedStatement pstmt = conn.prepareStatement("SELECT EMP_ID, PROJECT_ID FROM TMP_EMP_PROJECT"); | |
try { | |
ResultSet rs = pstmt.executeQuery(); | |
try { | |
while (rs.next()) { | |
actual.add(new Pair(rs.getInt("EMP_ID"), rs.getInt("PROJECT_ID"))); | |
} | |
} finally { | |
rs.close(); | |
} | |
} finally { | |
pstmt.close(); | |
} | |
} finally { | |
conn.close(); | |
} | |
if (expected.size() != actual.size()) { | |
flop("actual set has wrong size " + actual.size() + " expected: " + expected.size()); | |
} else { | |
verify(true, ""); | |
} | |
verify(expected.containsAll(actual), "actual set contains some elements missing in the expected set"); | |
verify(actual.containsAll(expected), "expected set contains some elements missing in the actual set"); | |
} | |
@Test | |
public void testDeleteEmployee() throws SQLException { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
env.beginTransaction(em); | |
Employee emp = em.find(Employee.class, HANS_ID); | |
verify(emp != null, "employee not found"); | |
em.remove(emp); | |
env.commitTransactionAndClear(em); | |
checkJoinTable(FRED_SET); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testDeleteNonSharedProject() throws SQLException { | |
// remove a project that is not shared between employees: | |
// 1) remove project from set of projects of employee | |
// 2) remove employee from set of employees of project | |
// 3) remove the project from the database | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
final int removeId = PUHLEN.getId().intValue(); | |
env.beginTransaction(em); | |
Employee emp = em.find(Employee.class, HANS_ID); | |
verify(emp != null, "employee not found"); | |
Set<Project> projects = emp.getProjects(); | |
verify(projects != null, "projects are null"); | |
verify(projects.size() == 3, "not exactly 3 projects but " + projects.size()); | |
Iterator<Project> iter = projects.iterator(); | |
while (iter.hasNext()) { | |
Project project = iter.next(); | |
if (project.getId().intValue() == removeId) { | |
Set<Employee> employeesOfProject = project.getEmployees(); | |
employeesOfProject.remove(emp); | |
em.remove(project); | |
iter.remove(); | |
break; | |
} | |
} | |
verify(projects.size() == 2, "no project removed"); | |
emp.clearPostUpdate(); | |
env.commitTransactionAndClear(em); | |
verify(emp.postUpdateWasCalled(), "post update was not called"); | |
Set<Pair> expected = new HashSet<Pair>(SEED_SET); | |
expected.remove(new Pair(HANS_ID_VALUE, removeId)); | |
checkJoinTable(expected); | |
env.beginTransaction(em); | |
emp = em.find(Employee.class, HANS_ID); | |
projects = emp.getProjects(); | |
verify(projects.size() == 2, "not exactly 2 projects but " + projects.size()); | |
Object object = em.find(Project.class, removeId); | |
verify(object == null, "project found"); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testDeleteSharedProject() throws SQLException { | |
// remove a project that is shared between employees: | |
// 1) remove project from set of projects of employee | |
// 2) remove employee from set of employees of project | |
// 3) don't remove the project from the database | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
final int REMOVE_ID = FALTEN.getId().intValue(); | |
env.beginTransaction(em); | |
Employee emp = em.find(Employee.class, HANS_ID); | |
verify(emp != null, "employee not found"); | |
Set<Project> projects = emp.getProjects(); | |
verify(projects != null, "projects are null"); | |
verify(projects.size() == 3, "not exactly 3 projects but " + projects.size()); | |
Iterator<Project> iter = projects.iterator(); | |
while (iter.hasNext()) { | |
Project project = iter.next(); | |
if (project.getId().intValue() == REMOVE_ID) { | |
Set<Employee> employeesOfProject = project.getEmployees(); | |
employeesOfProject.remove(emp); | |
iter.remove(); | |
} | |
} | |
verify(projects.size() == 2, "no project removed"); | |
emp.clearPostUpdate(); | |
env.commitTransactionAndClear(em); | |
verify(emp.postUpdateWasCalled(), "post update was not called"); | |
Set<Pair> expected = new HashSet<Pair>(SEED_SET); | |
expected.remove(new Pair(HANS_ID_VALUE, REMOVE_ID)); | |
checkJoinTable(expected); | |
env.beginTransaction(em); | |
emp = em.find(Employee.class, HANS_ID); | |
projects = emp.getProjects(); | |
verify(projects.size() == 2, "not exactly 2 projects but " + projects.size()); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testAdd() throws SQLException { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
env.beginTransaction(em); | |
final int newId; | |
Employee emp = em.find(Employee.class, HANS_ID); | |
verify(emp != null, "employee not found"); | |
Set<Project> projects = emp.getProjects(); | |
Project p6 = new Project("Nasen bohren"); | |
em.persist(p6); | |
newId = p6.getId().intValue(); | |
projects.add(p6); | |
emp.clearPostUpdate(); | |
env.commitTransactionAndClear(em); | |
verify(emp.postUpdateWasCalled(), "post update was not called"); | |
Set<Pair> expected = new HashSet<Pair>(SEED_SET); | |
expected.add(new Pair(HANS_ID_VALUE, newId)); | |
checkJoinTable(expected); | |
env.beginTransaction(em); | |
emp = em.find(Employee.class, HANS_ID); | |
projects = emp.getProjects(); | |
verify(projects.size() == 4, "not exactly 4 projects but " + projects.size()); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testExchange() throws SQLException { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
final int newId; | |
env.beginTransaction(em); | |
Employee emp = em.find(Employee.class, HANS_ID); | |
verify(emp != null, "employee not found"); | |
Set<Project> projects = emp.getProjects(); | |
Iterator<Project> iter = projects.iterator(); | |
Project project = (Project) iter.next(); | |
int removedId = project.getId().intValue(); | |
// there are no managed relationships -> we have to remove the projects on both sides | |
em.remove(project); | |
iter.remove(); | |
Project p7 = new Project("Ohren wacklen"); | |
em.persist(p7); | |
newId = p7.getId().intValue(); | |
projects.add(p7); | |
emp.clearPostUpdate(); | |
env.commitTransactionAndClear(em); | |
verify(emp.postUpdateWasCalled(), "post update was not called"); | |
Set<Pair> expected = new HashSet<Pair>(SEED_SET); | |
expected.remove(new Pair(HANS_ID_VALUE, removedId)); | |
expected.add(new Pair(HANS_ID_VALUE, newId)); | |
checkJoinTable(expected); | |
env.beginTransaction(em); | |
emp = em.find(Employee.class, HANS_ID); | |
projects = emp.getProjects(); | |
verify(projects.size() == 3, "not exactly 3 projects but " + projects.size()); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
env.evictAll(em); | |
closeEntityManager(em); | |
} | |
} | |
private void verifyEmployees(EntityManager em, int id, int size) { | |
Project project = em.find(Project.class, new Integer(id)); | |
verify(project != null, "project not found"); | |
Set<Employee> employees = project.getEmployees(); | |
verify(employees.size() == size, "wrong number of employees: " + employees.size() + " expected: " + size); | |
} | |
@Test | |
public void testGetEmployees() { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
env.beginTransaction(em); | |
verifyEmployees(em, LINKEN.getId().intValue(), 0); | |
verifyEmployees(em, PUHLEN.getId().intValue(), 1); | |
verifyEmployees(em, FALTEN.getId().intValue(), 2); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testCopyProjectsToNew() throws SQLException { | |
// copy all projects from hans to a new employee with out actually touching them | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
final int newId = 19; | |
env.beginTransaction(em); | |
Employee hans = em.find(Employee.class, HANS_ID); | |
Employee paul = new Employee(19, "Paul", "Knack", null); | |
paul.setProjects(hans.getProjects()); | |
em.persist(paul); | |
env.commitTransactionAndClear(em); | |
Set<Pair> expected = new HashSet<Pair>(SEED_SET); | |
for (Pair pair : HANS_SET) { | |
expected.add(new Pair(newId, pair.getProjectId())); | |
} | |
checkJoinTable(expected); | |
env.beginTransaction(em); | |
paul = em.find(Employee.class, new Integer(newId)); | |
verify(paul.getProjects().size() == HANS_SET.size(), "Paul has wrong number of projects"); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Bugzilla(bugid=300503) | |
@Test | |
public void testCopyProjectsToExisting() throws SQLException { | |
// copy all projects from hans to a new employee with out actually touching them | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
env.beginTransaction(em); | |
Employee hans = em.find(Employee.class, HANS_ID); | |
Employee fred = em.find(Employee.class, FRED_ID); | |
fred.setProjects(hans.getProjects()); | |
env.commitTransactionAndClear(em); | |
Set<Pair> expected = new HashSet<Pair>(SEED_SET); | |
expected.removeAll(FRED_SET); | |
for (Pair pair : HANS_SET) { | |
expected.add(new Pair(FRED_ID_VALUE, pair.getProjectId())); | |
} | |
checkJoinTable(expected); | |
env.beginTransaction(em); | |
fred = em.find(Employee.class, FRED_ID); | |
verify(fred.getProjects().size() == HANS_SET.size(), "Paul has wrong number of projects"); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testCopyProjectsToExistingTouching() throws SQLException { | |
// copy all projects from hans to a new employee with out actually touching them | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
env.beginTransaction(em); | |
Employee hans = em.find(Employee.class, HANS_ID); | |
Employee fred = em.find(Employee.class, FRED_ID); | |
hans.getProjects().size(); | |
fred.setProjects(hans.getProjects()); | |
env.commitTransactionAndClear(em); | |
Set<Pair> expected = new HashSet<Pair>(SEED_SET); | |
expected.removeAll(FRED_SET); | |
for (Pair pair : HANS_SET) { | |
expected.add(new Pair(FRED_ID_VALUE, pair.getProjectId())); | |
} | |
checkJoinTable(expected); | |
env.beginTransaction(em); | |
fred = em.find(Employee.class, FRED_ID); | |
verify(fred.getProjects().size() == HANS_SET.size(), "Paul has wrong number of projects"); | |
env.rollbackTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testGuidCollection() throws SQLException { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
byte[] id1 = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; | |
byte[] id2 = new byte[] { 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; | |
try { | |
TravelProfile profile1 = new TravelProfile(id1, true, "neverComeBack"); | |
TravelProfile profile2 = new TravelProfile(id2, true, "flyAway"); | |
Set<TravelProfile> profileSet = new HashSet<TravelProfile>(); | |
profileSet.add(profile1); | |
profileSet.add(profile2); | |
Vehicle vehicle = new Vehicle(); | |
Set<Vehicle> vehicleSet = new HashSet<Vehicle>(); | |
vehicleSet.add(vehicle); | |
profile1.setVehicles(vehicleSet); | |
profile2.setVehicles(vehicleSet); | |
vehicle.setProfiles(profileSet); | |
env.beginTransaction(em); | |
em.persist(profile1); | |
em.persist(profile2); | |
em.persist(vehicle); | |
env.commitTransactionAndClear(em); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
public void testByteItem() { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
byte[] id1 = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; | |
try { | |
env.beginTransaction(em); | |
Item_Byte item = new Item_Byte(id1, "a", "b", "c"); | |
item.addAttr("key1", "value1"); | |
em.persist(item); | |
env.commitTransactionAndClear(em); | |
item = em.find(Item_Byte.class, id1); | |
item.getAttributes(); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
@Test | |
@Bugzilla(bugid=338783, databaseNames={"org.eclipse.persistence.platform.database.oracle.Oracle10Platform", | |
"org.eclipse.persistence.platform.database.oracle.Oracle11Platform"}) | |
public void testCascadeMerge() throws IOException, ClassNotFoundException { | |
JPAEnvironment env = getEnvironment(); | |
EntityManager em = env.getEntityManager(); | |
try { | |
env.beginTransaction(em); | |
em.createQuery("delete from Employee").executeUpdate(); | |
em.createQuery("delete from Vehicle").executeUpdate(); | |
env.commitTransactionAndClear(em); | |
env.beginTransaction(em); | |
Bicycle bicycle = new Bicycle(); | |
em.persist(bicycle); | |
Short bikeId = bicycle.getId(); | |
env.commitTransaction(em); | |
bicycle = em.find(Bicycle.class, bikeId); | |
em.clear(); | |
bicycle = AbstractBaseTest.serializeDeserialize(bicycle); | |
// bicycle should be detached | |
Employee emp = new Employee(9999, "Robbi", "Tobbi", null); | |
emp.clearPostPersist(); | |
bicycle.setRiders(Collections.singleton(emp)); | |
env.beginTransaction(em); | |
Bicycle mergedBike = em.merge(bicycle); | |
env.commitTransactionAndClear(em); | |
Employee mergedEmp = mergedBike.getRiders().iterator().next(); | |
verify(mergedEmp.postPersistWasCalled(), "merge did not cascade to employee"); | |
verify(em.find(Employee.class, 9999) != null, "employee not found"); | |
} finally { | |
closeEntityManager(em); | |
} | |
} | |
private static class Pair { | |
@Override | |
public boolean equals(Object obj) { | |
if (obj instanceof Pair) { | |
Pair other = (Pair) obj; | |
return other.empId == empId && other.projectId == projectId; | |
} | |
return false; | |
} | |
@Override | |
public int hashCode() { | |
return empId + 17 * projectId; | |
} | |
/** | |
* @return Returns the empId. | |
*/ | |
@SuppressWarnings("unused") | |
public int getEmpId() { | |
return empId; | |
} | |
/** | |
* @return Returns the projectId. | |
*/ | |
public int getProjectId() { | |
return projectId; | |
} | |
final int empId; | |
final int projectId; | |
Pair(int e, int r) { | |
empId = e; | |
projectId = r; | |
} | |
} | |
} |