| /* |
| * Copyright (c) 1998, 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: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.testing.tests.queries; |
| |
| import org.eclipse.persistence.descriptors.*; |
| import org.eclipse.persistence.expressions.*; |
| import org.eclipse.persistence.mappings.*; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.sessions.*; |
| import org.eclipse.persistence.exceptions.*; |
| |
| import org.eclipse.persistence.testing.models.employee.domain.*; |
| import org.eclipse.persistence.testing.framework.*; |
| |
| /** |
| * Tests fine-grained / descriptor level pessimistic locking with joined |
| * attributes. |
| * <p> |
| * Specifically tests the unusual cases of bug 3422202, and makes sure the |
| * joined attributes are properly recorded with the correct session. |
| * <p> |
| * This test must be run using the ServerSessionTestAdaptor, so that each |
| * UnitOfWork is on a separate UnitOfWork, each having their own transaction |
| * but still sharing the global cache. |
| * <p> |
| * Test cases: |
| * <ul> |
| * <li>Query on a project, where team leader and its address are joined. Query |
| * for both the address and the employee: and check the SQL to see if had |
| * a cache hit (or checkCacheOnly). |
| * <li>Query on a project, where team leader is joined. Then in a separate UnitOfWork |
| * query on the same project where team leader is not joined, and then attempt |
| * project.getTeamLeader(). This should fail. |
| * <li>Query on a project, where team leader is not joined. Then in a separate |
| * UnitOfWork query on the project where team leader is joined. Then try to |
| * get the teamleader on the first UnitOfWork. This should fail. |
| * <li>Query on a project, where team leader is joined, and rollback. Change |
| * the cache copy and then have another read the project where team leader is |
| * joined. The query and the refresh should succeed. |
| * </ul> |
| */ |
| public class PessimisticLockJoinedAttributeTest extends TestCase { |
| public UnitOfWork uow; |
| public short lockMode; |
| CMPPolicy oldCMPPolicy; |
| |
| /** |
| * PessimisticLockInheritanceTest constructor comment. |
| */ |
| public PessimisticLockJoinedAttributeTest() { |
| this.lockMode = ObjectLevelReadQuery.LOCK_NOWAIT; |
| setDescription("For bug 3422202 verifies the pessimistic locking feature works properly when set on the descriptor and joined attributes are involved."); |
| } |
| |
| @Override |
| protected void setup() { |
| getSession().getIdentityMapAccessor().initializeIdentityMaps(); |
| |
| PessimisticLockingPolicy policy = new PessimisticLockingPolicy(); |
| policy.setLockingMode(this.lockMode); |
| CMPPolicy cmpPolicy = new CMPPolicy(); |
| cmpPolicy.setPessimisticLockingPolicy(policy); |
| |
| ClassDescriptor projectDescriptor = getSession().getDescriptor(org.eclipse.persistence.testing.models.employee.domain.Project.class); |
| ((ObjectLevelReadQuery)((ForeignReferenceMapping)projectDescriptor.getMappingForAttributeName("teamLeader")).getSelectionQuery()).setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| |
| ClassDescriptor employeeDescriptor = getSession().getDescriptor(Employee.class); |
| oldCMPPolicy = employeeDescriptor.getCMPPolicy(); |
| employeeDescriptor.setCMPPolicy(cmpPolicy); |
| employeeDescriptor.getQueryManager().getReadObjectQuery().setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| ((ObjectLevelReadQuery)((ForeignReferenceMapping)employeeDescriptor.getMappingForAttributeName("address")).getSelectionQuery()).setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| |
| ClassDescriptor addressDescriptor = getSession().getDescriptor(Address.class); |
| addressDescriptor.setCMPPolicy(cmpPolicy); |
| addressDescriptor.getQueryManager().getReadObjectQuery().setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| } |
| |
| @Override |
| public void reset() { |
| getSession().getIdentityMapAccessor().initializeAllIdentityMaps(); |
| if (uow != null) { |
| uow.release(); |
| } |
| ClassDescriptor projectDescriptor = getSession().getDescriptor(org.eclipse.persistence.testing.models.employee.domain.Project.class); |
| ((ObjectLevelReadQuery)((ForeignReferenceMapping)projectDescriptor.getMappingForAttributeName("teamLeader")).getSelectionQuery()).setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| ((ObjectLevelReadQuery)((ForeignReferenceMapping)projectDescriptor.getMappingForAttributeName("teamLeader")).getSelectionQuery()).dontRefreshIdentityMapResult(); |
| ClassDescriptor employeeDescriptor = getSession().getDescriptor(Employee.class); |
| employeeDescriptor.setCMPPolicy(oldCMPPolicy); |
| employeeDescriptor.getQueryManager().getReadObjectQuery().setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| |
| ((ObjectLevelReadQuery)((ForeignReferenceMapping)employeeDescriptor.getMappingForAttributeName("address")).getSelectionQuery()).setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| ((ObjectLevelReadQuery)((ForeignReferenceMapping)employeeDescriptor.getMappingForAttributeName("address")).getSelectionQuery()).dontRefreshIdentityMapResult(); |
| |
| ClassDescriptor addressDescriptor = getSession().getDescriptor(Address.class); |
| addressDescriptor.setCMPPolicy(oldCMPPolicy); |
| addressDescriptor.getQueryManager().getReadObjectQuery().setLockMode(ObjectLevelReadQuery.DEFAULT_LOCK_MODE); |
| } |
| |
| @Override |
| public void test() throws Exception { |
| if (!getSession().getPlatform().isOracle() && !getSession().getPlatform().isSQLServer()) { |
| throw new TestWarningException("This test only runs on Oracle wears writes do not block reads."); |
| } |
| |
| uow = getSession().acquireUnitOfWork(); |
| |
| ReadObjectQuery query = new ReadObjectQuery(LargeProject.class); |
| |
| // Only Charles Chanley and John Way are team leaders of projects they are also working on. |
| Expression expression = query.getExpressionBuilder().get("teamLeader").get("firstName").equal("Charles"); |
| query.setSelectionCriteria(expression); |
| query.addJoinedAttribute(query.getExpressionBuilder().get("teamLeader")); |
| query.addJoinedAttribute(query.getExpressionBuilder().get("teamLeader").get("address")); |
| |
| Object result = uow.executeQuery(query); |
| |
| // Now trigger the valueholders... These clones should be registered in |
| // the uow and marked as already locked. |
| // Check the SQL so that none gets issued. |
| Employee charles = (Employee)((LargeProject)result).getTeamLeader(); |
| Address address = charles.getAddress(); |
| |
| ReadObjectQuery cacheQuery = null; |
| Object cachedObject = null; |
| |
| // There is only one address in the cache, and it will only be returned |
| // if it was tracked as being locked. |
| cacheQuery = new ReadObjectQuery(Address.class); |
| cacheQuery.checkCacheThenDatabase(); |
| cachedObject = uow.executeQuery(cacheQuery); |
| if (address != cachedObject) { |
| throw new TestErrorException("Did not get a cache hit after pessimistically locking a nested joined attribute."); |
| } |
| cacheQuery = new ReadObjectQuery(org.eclipse.persistence.testing.models.employee.domain.Employee.class); |
| cacheQuery.checkCacheThenDatabase(); |
| cachedObject = uow.executeQuery(cacheQuery); |
| if (charles != cachedObject) { |
| throw new TestErrorException("Did not get a cache hit after pessimisticly locking a joined attribute."); |
| } |
| |
| // Test the lock. |
| // Because this is on a ServerSession the second UOW will have its own |
| // ClientSession/exclusive connection. |
| UnitOfWork uow2 = getSession().acquireUnitOfWork(); |
| try { |
| boolean isLocked = false; |
| query = new ReadObjectQuery(LargeProject.class); |
| expression = query.getExpressionBuilder().get("teamLeader").get("firstName").equal("Charles"); |
| query.setSelectionCriteria(expression); |
| LargeProject result2 = (LargeProject)uow2.executeQuery(query); |
| |
| // assert(result2 != null, "It was never meant to be locked."); |
| try { |
| result2.getTeamLeader(); |
| } catch (EclipseLinkException exception) { |
| isLocked = true; |
| } |
| if (!isLocked) { |
| throw new TestErrorException("Triggering an attribute that was locked by another in a joined read should trigger a no_wait exception."); |
| } |
| |
| // Now release the first UnitOfWork and try again. |
| Employee originalCharles = (Employee)uow.getOriginalVersionOfObject(charles); |
| uow.release(); |
| uow = null; |
| |
| // Now change the session copy, this is what would happen if the |
| // object was actually committed to the database. Want to test that |
| // the object is refreshed when locked. |
| originalCharles.setSalary(0); |
| try { |
| charles = (Employee)result2.getTeamLeader(); |
| address = charles.getAddress(); |
| } catch (EclipseLinkException exception) { |
| throw new TestErrorException("Now that a joined attribute locked by UOW1 has been released it should be readable now."); |
| } |
| if (charles.getSalary() == 0) { |
| throw new TestErrorException("When a joined attribute is locked by another after it is released, should get refreshed."); |
| } |
| } catch (RuntimeException e) { |
| throw e; |
| } finally { |
| if (uow2 != null) { |
| uow2.release(); |
| } |
| } |
| } |
| } |