| /* |
| * Copyright (c) 2005, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2005, 2015 SAP. 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: |
| // 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 jakarta.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 = 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 = 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())); |
| } |
| FRED_SET.clear(); |
| for (final Project element : fredProjects) { |
| FRED_SET.add(new Pair(FRED_ID_VALUE, element.getId())); |
| } |
| 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(); |
| 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() == 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(); |
| 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() == 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(); |
| 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 = iter.next(); |
| int removedId = project.getId(); |
| // 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(); |
| 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)); |
| //we need to remove also record with Fred (if applicable), because if Fred had associated project, which is now removed, |
| //he is not associated with the project anymore |
| expected.remove(new Pair(FRED_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, 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(), 0); |
| verifyEmployees(em, PUHLEN.getId(), 1); |
| verifyEmployees(em, FALTEN.getId(), 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, 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 |
| 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; |
| } |
| } |
| } |