You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by al...@apache.org on 2009/11/20 00:28:46 UTC

svn commit: r882358 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/

Author: allee8285
Date: Thu Nov 19 23:28:45 2009
New Revision: 882358

URL: http://svn.apache.org/viewvc?rev=882358&view=rev
Log:
OPENJPA-1394 - DB2 supports Order By clause with recording locking using "WITH R*" construct. By enabling this feature in the DB2 dictionary, row locking can be perform with the fetch and eliminate the time window other thread snike in to fetch the same row.

Added:
    openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Department.java   (with props)
    openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Employee.java   (with props)
    openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java   (with props)
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java?rev=882358&r1=882357&r2=882358&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java Thu Nov 19 23:28:45 2009
@@ -122,6 +122,7 @@
         supportsDeferredConstraints = false;
         supportsDefaultDeleteAction = false;
         supportsAlterTableWithDropColumn = false;
+        supportsLockingWithOrderClause = true;
 
         supportsNullTableForGetColumns = false;
         requiresCastForMathFunctions = true;

Added: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Department.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Department.java?rev=882358&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Department.java (added)
+++ openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Department.java Thu Nov 19 23:28:45 2009
@@ -0,0 +1,79 @@
+/*
+ * 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.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Collection;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+@Entity
+public class Department implements Externalizable {
+
+    private int id;
+
+    private String name;
+
+    private Collection<Employee> employees;
+
+    @OneToMany(cascade=CascadeType.ALL, mappedBy="department")
+    public Collection<Employee> getEmployees() {
+        return employees;
+    }
+
+    @Id
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String toString() {
+        return this.getClass().getName() + '@'
+            + Integer.toHexString(System.identityHashCode(this)) + "[id="
+            + getId() + "] first=" + getName();
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+        ClassNotFoundException {
+        id = in.readInt();
+        name = (String) in.readObject();
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeInt(id);
+        out.writeObject(name);
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Department.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Employee.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Employee.java?rev=882358&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Employee.java (added)
+++ openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Employee.java Thu Nov 19 23:28:45 2009
@@ -0,0 +1,94 @@
+/*
+ * 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.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class Employee implements Externalizable {
+
+    private int id;
+
+    private String firstName;
+    private String lastName;
+    private Department department;
+
+    @Id
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    @ManyToOne
+    @JoinColumn(name="FK_DEPT")
+    public Department getDepartment() {
+        return department;
+    }
+
+    public void setDepartment(Department department) {
+        this.department = department;
+    }
+
+    public String toString() {
+        return this.getClass().getName() + '@'
+            + Integer.toHexString(System.identityHashCode(this)) + "[id="
+            + getId() + "] first=" + getFirstName()
+            + ", last=" + getLastName();
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+        ClassNotFoundException {
+        id = in.readInt();
+        firstName = (String) in.readObject();
+        lastName = (String) in.readObject();
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeInt(id);
+        out.writeObject(firstName);
+        out.writeObject(lastName);
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Employee.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java?rev=882358&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java (added)
+++ openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java Thu Nov 19 23:28:45 2009
@@ -0,0 +1,242 @@
+/*
+ * 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.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.LockModeType;
+import javax.persistence.PessimisticLockException;
+import javax.persistence.Query;
+import javax.persistence.QueryTimeoutException;
+
+import org.apache.openjpa.persistence.test.SQLListenerTestCase;
+
+/**
+ * Test Pessimistic Lock and exception behavior against EntityManager and Query interface methods.
+ */
+public class TestPessimisticLocks extends SQLListenerTestCase {
+
+    public void setUp() {
+        setSupportedDatabases(
+//                org.apache.openjpa.jdbc.sql.DerbyDictionary.class,
+//                org.apache.openjpa.jdbc.sql.OracleDictionary.class,
+                org.apache.openjpa.jdbc.sql.DB2Dictionary.class);
+        if (isTestsDisabled()) {
+            return;
+        }
+
+        setUp(Employee.class, Department.class, "openjpa.LockManager", "mixed");
+        String empTable = getMapping(Employee.class).getTable().getFullName();
+        String deptTable = getMapping(Department.class).getTable().getFullName();
+
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            em.getTransaction().begin();
+
+            em.createQuery("delete from " + empTable).executeUpdate();
+            em.createQuery("delete from " + deptTable).executeUpdate();
+
+            em.getTransaction().commit();
+
+            Employee e1, e2;
+            Department d1, d2;
+            d1 = new Department();
+            d1.setId(10);
+            d1.setName("D10");
+
+            e1 = new Employee();
+            e1.setId(1);
+            e1.setDepartment(d1);
+            e1.setFirstName("first.1");
+            e1.setLastName("last.1");
+
+            d2 = new Department();
+            d2.setId(20);
+            d2.setName("D20");
+
+            e2 = new Employee();
+            e2.setId(2);
+            e2.setDepartment(d2);
+            e2.setFirstName("first.2");
+            e2.setLastName("last.2");
+
+            em.getTransaction().begin();
+            em.persist(d1);
+            em.persist(d2);
+            em.persist(e1);
+            em.persist(e2);
+            em.getTransaction().commit();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (em != null && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /*
+     * Test a find with pessimistic lock after a query with pessimistic lock and expect PessimisticLockException.
+     */
+    public void testFindWithLockTimeoutAfterQueryWithPessimisticLocks() {
+        EntityManager em1 = emf.createEntityManager();
+        EntityManager em2 = emf.createEntityManager();
+        try {
+            em1.getTransaction().begin();
+
+            Query query = em1.createQuery(
+                    "select e from Employee e where e.id < 10 order by e.id").setFirstResult(1);
+            query.setLockMode(LockModeType.PESSIMISTIC_READ);
+            List<Employee> q = query.getResultList();
+            assertEquals("Expected 1 element with emplyee id=2", q.size(), 1);
+            assertEquals("Test Employee first name = 'first.2'", q.get(0).getFirstName(), "first.2");
+
+            em2.getTransaction().begin();
+
+            Map<String,Object> map = new HashMap<String,Object>();
+            map.put("javax.persistence.lock.timeout", 2000);
+            em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
+            fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
+        } catch (QueryTimeoutException e) {            
+            // TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
+            // Remove this when the problem is fixed
+//            System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
+        } catch (PessimisticLockException e) {
+            // TODO: This is the expected exception but will be fixed under OPENJPA-991
+//            System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
+        } catch (Exception ex) {
+            fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
+        } finally {
+            if( em1.getTransaction().isActive())
+                em1.getTransaction().rollback();
+            if( em2.getTransaction().isActive())
+                em2.getTransaction().rollback();
+        }
+
+        try {
+            em1.getTransaction().begin();
+
+            Query query = em1.createQuery(
+                    "select e.department from Employee e where e.id < 10 order by e.department.id").setFirstResult(1);
+            query.setLockMode(LockModeType.PESSIMISTIC_READ);
+            List<Department> q = query.getResultList();
+            assertEquals("Expected 1 element with department id=20", q.size(), 1);
+            assertEquals("Test department name = 'D20'", q.get(0).getName(), "D20");
+
+            em2.getTransaction().begin();
+
+            Map<String,Object> map = new HashMap<String,Object>();
+            map.put("javax.persistence.lock.timeout", 2000);
+            Employee emp = em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
+            assertNotNull("Query locks department but find locks Employee.", emp);
+            fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
+        } catch (QueryTimeoutException e) {            
+            // TODO: This is the current unexpected exception due to OPENJPA-991. Remove this when the problem is fixed 
+//            System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
+        } catch (PessimisticLockException e) {
+            // TODO: This is the expected exception but will be fixed under OPENJPA-991
+//            System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
+        } catch (Exception ex) {
+            fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
+        } finally {
+            if( em1.getTransaction().isActive())
+                em1.getTransaction().rollback();
+            if( em2.getTransaction().isActive())
+                em2.getTransaction().rollback();
+        }
+        em1.close();
+        em2.close();
+    }
+
+    /*
+     * Test a query with pessimistic lock after a find with pessimistic lock and expect PessimisticLockException.
+     */
+    public void testQueryAfterFindWithPessimisticLocks() {
+        EntityManager em1 = emf.createEntityManager();
+        EntityManager em2 = emf.createEntityManager();
+        try {
+            em2.getTransaction().begin();
+
+            Map<String,Object> map = new HashMap<String,Object>();
+            map.put("javax.persistence.lock.timeout", 2000);
+            em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
+
+            em1.getTransaction().begin();
+
+            Query query = em1.createQuery(
+                    "select e.department from Employee e where e.id < 10 order by e.department.id").setFirstResult(1);
+            query.setLockMode(LockModeType.PESSIMISTIC_READ);
+            List<Department> q = query.getResultList();
+
+            fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
+        } catch (PessimisticLockException e) {
+            // This is the expected exception.
+        } catch (Exception ex) {
+            fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
+        } finally {
+            if( em1.getTransaction().isActive())
+                em1.getTransaction().rollback();
+            if (em2.getTransaction().isActive())
+                em2.getTransaction().rollback();
+        }
+        em1.close();
+        em2.close();
+    }
+
+    /*
+     * Test a query with pessimistic lock with query timeout set after a find
+     * with pessimistic lock and expect QueryTimeoutException.
+     */
+    public void testQueryWithQueryTimeoutAfterFindWithPessimisticLocks() {
+        EntityManager em1 = emf.createEntityManager();
+        EntityManager em2 = emf.createEntityManager();
+        try {
+            em2.getTransaction().begin();
+
+            Map<String,Object> map = new HashMap<String,Object>();
+            map.put("javax.persistence.lock.timeout", 2000);
+            em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
+
+            em1.getTransaction().begin();
+
+            Query query = em1.createQuery(
+                    "select e.department from Employee e where e.id < 10 order by e.department.id").setFirstResult(1);
+            query.setLockMode(LockModeType.PESSIMISTIC_READ);
+            query.setHint("javax.persistence.query.timeout", 2000);
+            List<Department> q = query.getResultList();
+
+            fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
+        } catch (QueryTimeoutException e) {            
+            // This is the expected exception.
+        } catch (Exception ex) {
+            fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
+        } finally {
+            if( em1.getTransaction().isActive())
+                em1.getTransaction().rollback();
+            if( em2.getTransaction().isActive())
+                em2.getTransaction().rollback();
+        }
+        em1.close();
+        em2.close();
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java
------------------------------------------------------------------------------
    svn:eol-style = native