/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.openjpa.persistence.lockmgr; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.LockModeType; import javax.persistence.PessimisticLockScope; import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI; import org.apache.openjpa.persistence.test.SQLListenerTestCase; public class TestLocking extends SQLListenerTestCase { String _phone = "5075555555"; @Override protected String getPersistenceUnitName() { return "locking-test"; } public void setUp() { super.setUp(CLEAR_TABLES, Person.class, PhoneNumber.class // ,"openjpa.Log", "SQL=trace" ); populate(); } public void testExtendedLockScope() throws Exception { Map<String, Object> props = new HashMap<String, Object>(); props.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); OpenJPAEntityManagerSPI em1 = emf.createEntityManager(); OpenJPAEntityManagerSPI em2 = emf.createEntityManager(); CommitterThread committer = new CommitterThread(em2); em1.getTransaction().begin(); Person e1 = em1.find(Person.class, 1); assertEquals(1, e1.getPhoneNumbers().size()); // This SHOULD lock Employee with id=1 AND the join table. // // pg 86 // Element collections and relationships owned by the entity that are contained in join tables will be // locked if the javax.persistence.lock.scope property is specified with a value of // PessimisticLockScope.EXTENDED. The state of entities referenced by such relationships will // not be locked (unless those entities are explicitly locked). This property may be passed as an argument // to the methods of the EntityManager, Query, and TypedQuery interfaces that allow lock modes // to be specified or used with the NamedQuery annotation. em1.refresh(e1, LockModeType.PESSIMISTIC_FORCE_INCREMENT, props); // Kick off the committer thread committer.start(); // Make sure to sleep at least for 5 seconds AFTER the committer calls commit while (System.currentTimeMillis() - committer.sleepStartTime < 5000) { Thread.sleep(5000); } // The committer should still be waiting because the em1.refresh(...) call should have locked the join table and // the remove can't complete assertFalse(committer.commitComplete); em1.getTransaction().commit(); em1.close(); // wait for child thread to finish committer.join(); } private class CommitterThread extends Thread { OpenJPAEntityManagerSPI _em2; boolean inCommit = false; boolean commitComplete = false; long sleepStartTime = Long.MAX_VALUE; public CommitterThread(OpenJPAEntityManagerSPI e) { _em2 = e; } @Override public void run() { _em2.getTransaction().begin(); PhoneNumber phoneNumber = _em2.find(PhoneNumber.class, _phone); _em2.remove(phoneNumber); inCommit = true; sleepStartTime = System.currentTimeMillis(); _em2.getTransaction().commit(); commitComplete = true; _em2.close(); } } private void populate() { OpenJPAEntityManagerSPI em = emf.createEntityManager(); em.getTransaction().begin(); PhoneNumber p = new PhoneNumber(_phone); List<PhoneNumber> numbers = Arrays.asList(new PhoneNumber[] { p }); Person e1 = new Person(); e1.setId(1); e1.setPhoneNumbers(numbers); Person e2 = new Person(); e2.setId(2); e2.setPhoneNumbers(numbers); p.setOwners(Arrays.asList(new Person[] { e1, e2 })); em.persist(e1); em.persist(e2); em.persist(p); em.getTransaction().commit(); em.close(); } }