You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2007/07/06 16:49:53 UTC

svn commit: r553912 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ openjpa-jdbc/src/main/java/org/...

Author: pcl
Date: Fri Jul  6 07:49:52 2007
New Revision: 553912

URL: http://svn.apache.org/viewvc?view=rev&rev=553912
Log:
OPENJPA-274, OPENJPA-275. Improved our bulk update support to automatically increment version counters as necessary if an UPDATE query does not maintain the version fields itself. Also fixed a bug with all queries involving version fields by changing FieldMappings representing version fields to return their owning ClassMapping's Version's columns from a getColumns() call.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesAndVersionColumn.java
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/Version.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/VersionStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/AbstractVersionStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/NumberVersionStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/SuperclassVersionStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/TimestampVersionStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/OptimisticLockInstance.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java Fri Jul  6 07:49:52 2007
@@ -28,6 +28,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Iterator;
 
 import org.apache.openjpa.event.LifecycleEventManager;
 import org.apache.openjpa.jdbc.kernel.exps.ExpContext;
@@ -62,6 +63,7 @@
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.ClassMetaData;
 import org.apache.openjpa.meta.ValueMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.util.UserException;
 import serp.util.Numbers;
 
@@ -437,7 +439,11 @@
         // we cannot execute a bulk delete statement when have mappings in
         // multiple tables, so indicate we want to use in-memory with null
         ClassMapping[] mappings = (ClassMapping[]) metas;
+
+        // specification of the "updates" map indicates that this is
+        // an update query; otherwise, this is a delete statement
         boolean isUpdate = updates != null && updates.size() > 0;
+
         for (int i = 0; i < mappings.length; i++) {
             if (!isSingleTableMapping(mappings[i], subclasses) && !isUpdate)
                 return null;
@@ -471,13 +477,11 @@
                 subclasses, exps[i], state[i], 
                 JDBCFetchConfiguration.EAGER_NONE);
 
-            // specification of the "udpates" map indicates that this is
-            // an update query; otherwise, this is a delete statement
             // The bulk operation will return null to indicate that the database
             // does not support the request bulk delete operation; in
             // this case, we need to perform the query in-memory and
             // manually delete the instances
-            if (updates == null)
+            if (!isUpdate)
                 sql[i] = dict.toDelete(mappings[i], sel, params);
             else
                 sql[i] = dict.toUpdate(mappings[i], sel, _store, params,

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java Fri Jul  6 07:49:52 2007
@@ -918,7 +918,14 @@
     }
 
     public Column[] getColumns() {
-        return _val.getColumns();
+        // pcl: 6 July 2007: this seems a bit hacky, but if the mapping is a
+        // version, it will have a NoneFieldMapping (since the version strategy
+        // for the class takes care of it's mapping), and NoneFieldStrategies
+        // do not have columns.
+        if (isVersion())
+            return getDeclaringMapping().getVersion().getColumns();
+        else
+            return _val.getColumns();
     }
 
     public void setColumns(Column[] cols) {

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java Fri Jul  6 07:49:52 2007
@@ -413,8 +413,6 @@
                     throw new MetaDataException(_loc.get("untraversable-path",
                         QueryResultMapping.this, _candidate, path));
                 Column[] cols = last.getColumns();
-                if (last.isVersion())
-                    cols = candidate.getVersion().getColumns();
                 assertSingleColumn(cols, path);
                 Column col = cols[0];
                 

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/Version.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/Version.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/Version.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/Version.java Fri Jul  6 07:49:52 2007
@@ -19,6 +19,7 @@
 package org.apache.openjpa.jdbc.meta;
 
 import java.sql.SQLException;
+import java.util.Map;
 
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.jdbc.schema.Column;
@@ -364,5 +365,15 @@
 
     public String toString() {
         return _mapping + "<version>";
+    }
+
+    /**
+     * @return a Map<Column,String> specifying how to update each version
+     * column in this instance during a bulk update.
+     *
+     * @since 1.0.0
+     */
+    public Map getBulkUpdateValues() {
+        return _strategy.getBulkUpdateValues();
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/VersionStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/VersionStrategy.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/VersionStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/VersionStrategy.java Fri Jul  6 07:49:52 2007
@@ -19,10 +19,12 @@
 package org.apache.openjpa.jdbc.meta;
 
 import java.sql.SQLException;
+import java.util.Map;
 
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.jdbc.sql.Result;
 import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.jdbc.schema.Column;
 import org.apache.openjpa.kernel.OpenJPAStateManager;
 import org.apache.openjpa.kernel.StoreManager;
 
@@ -74,4 +76,12 @@
      * @see StoreManager#compareVersion
      */
     public int compareVersion(Object v1, Object v2);
+
+    /**
+     * @return a Map<Column,String> specifying how to update each version
+     * column during a bulk update.
+     *
+     * @since 1.0.0
+     */
+    public Map getBulkUpdateValues();
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/AbstractVersionStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/AbstractVersionStrategy.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/AbstractVersionStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/AbstractVersionStrategy.java Fri Jul  6 07:49:52 2007
@@ -19,6 +19,8 @@
 package org.apache.openjpa.jdbc.meta.strats;
 
 import java.sql.SQLException;
+import java.util.Map;
+import java.util.Collections;
 
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
@@ -66,5 +68,9 @@
 
     public int compareVersion(Object v1, Object v2) {
         return StoreManager.VERSION_SAME;
+    }
+
+    public Map getBulkUpdateValues() {
+        return Collections.EMPTY_MAP;
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/NumberVersionStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/NumberVersionStrategy.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/NumberVersionStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/NumberVersionStrategy.java Fri Jul  6 07:49:52 2007
@@ -18,7 +18,11 @@
  */
 package org.apache.openjpa.jdbc.meta.strats;
 
+import java.util.Map;
+import java.util.HashMap;
+
 import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.jdbc.schema.Column;
 import serp.util.Numbers;
 
 /**
@@ -59,5 +63,13 @@
         if (version == null)
             return _initial;
         return Numbers.valueOf(((Number) version).intValue() + 1);
+    }
+
+    public Map getBulkUpdateValues() {
+        Column[] cols = vers.getColumns();
+        Map map = new HashMap(cols.length);
+        for (int i = 0; i < cols.length; i++)
+            map.put(cols[i], cols[i].getName() + " + 1");
+        return map;
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/SuperclassVersionStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/SuperclassVersionStrategy.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/SuperclassVersionStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/SuperclassVersionStrategy.java Fri Jul  6 07:49:52 2007
@@ -19,6 +19,7 @@
 package org.apache.openjpa.jdbc.meta.strats;
 
 import java.sql.SQLException;
+import java.util.Map;
 
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.kernel.OpenJPAStateManager;
@@ -47,5 +48,10 @@
     public int compareVersion(Object v1, Object v2) {
         return vers.getClassMapping().getPCSuperclassMapping().getVersion().
             compareVersion(v1, v2);
+    }
+
+    public Map getBulkUpdateValues() {
+        return vers.getClassMapping().getPCSuperclassMapping().getVersion()
+            .getBulkUpdateValues();
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/TimestampVersionStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/TimestampVersionStrategy.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/TimestampVersionStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/TimestampVersionStrategy.java Fri Jul  6 07:49:52 2007
@@ -19,8 +19,12 @@
 package org.apache.openjpa.jdbc.meta.strats;
 
 import java.sql.Timestamp;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Date;
 
 import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
+import org.apache.openjpa.jdbc.schema.Column;
 
 /**
  * Uses a timestamp for optimistic versioning.
@@ -42,5 +46,14 @@
 
     protected Object nextVersion(Object version) {
         return new Timestamp(System.currentTimeMillis());
+    }
+
+    public Map getBulkUpdateValues() {
+        Column[] cols = vers.getColumns();
+        Map map = new HashMap(cols.length);
+        Date d = new Date();
+        for (int i = 0; i < cols.length; i++)
+            map.put(cols[i], d);
+        return map;
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Fri Jul  6 07:49:52 2007
@@ -58,6 +58,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.HashMap;
 import javax.sql.DataSource;
 
 import org.apache.commons.lang.StringUtils;
@@ -1883,12 +1884,23 @@
         sql.append(" SET ");
         ExpContext ctx = new ExpContext(store, params, 
             store.getFetchConfiguration());
+
+        // If the updates map contains any version fields, assume that the
+        // optimistic lock version data is being handled properly by the
+        // caller. Otherwise, give the version indicator an opportunity to
+        // add more update clauses as needed.
+        boolean augmentUpdates = true;
+
         for (Iterator i = updateParams.entrySet().iterator(); i.hasNext();) {
             Map.Entry next = (Map.Entry) i.next();
-            FieldMetaData fmd = (FieldMetaData) next.getKey();
+            FieldMapping fmd = (FieldMapping) next.getKey();
+
+            if (fmd.isVersion())
+                augmentUpdates = false;
+
             Val val = (Val) next.getValue();
 
-            Column col = ((FieldMapping) fmd).getColumns()[0];
+            Column col = fmd.getColumns()[0];
             sql.append(col.getName());
             sql.append(" = ");
 
@@ -1903,6 +1915,21 @@
 
             if (i.hasNext())
                 sql.append(", ");
+        }
+
+        if (augmentUpdates) {
+            ClassMapping meta =
+                ((FieldMapping) updateParams.keySet().iterator().next())
+                    .getDeclaringMapping();
+            Map updates = meta.getVersion().getBulkUpdateValues();
+            for (Iterator iter = updates.entrySet().iterator();
+                iter.hasNext(); ) {
+                Map.Entry e = (Map.Entry) iter.next();
+                Column col = (Column) e.getKey();
+                String val = (String) e.getValue();
+                sql.append(", ").append(col.getName())
+                    .append(" = ").append(val);
+            }
         }
     }
     

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/OptimisticLockInstance.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/OptimisticLockInstance.java?view=diff&rev=553912&r1=553911&r2=553912
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/OptimisticLockInstance.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/OptimisticLockInstance.java Fri Jul  6 07:49:52 2007
@@ -35,6 +35,7 @@
     private int oplock;
 
     private String str;
+    private int intField;
 
     protected OptimisticLockInstance() { }
 

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesAndVersionColumn.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesAndVersionColumn.java?view=auto&rev=553912
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesAndVersionColumn.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesAndVersionColumn.java Fri Jul  6 07:49:52 2007
@@ -0,0 +1,101 @@
+/*
+ * 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.datacache;
+
+import javax.persistence.EntityManager;
+import javax.persistence.LockModeType;
+import javax.persistence.OptimisticLockException;
+import javax.persistence.RollbackException;
+
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+
+public class TestBulkUpdatesAndVersionColumn
+    extends SingleEMFTestCase {
+
+    public void setUp() throws Exception {
+        setUp("openjpa.DataCache", "true",
+            "openjpa.RemoteCommitProvider", "sjvm",
+            "openjpa.Log", "SQL=TRACE",
+            OptimisticLockInstance.class, CLEAR_TABLES);
+
+        OpenJPAEntityManager em = emf.createEntityManager();
+        em.getTransaction().begin();
+        OptimisticLockInstance pc = new OptimisticLockInstance("foo");
+        em.persist(pc);
+        em.getTransaction().commit();
+        em.close();
+    }
+
+    public void testSelectOnOplockField() {
+        EntityManager em = emf.createEntityManager();
+        em.createQuery("select o from OptimisticLockInstance o "
+            + "where o.oplock = 0").getResultList();
+        em.close();
+    }
+
+    public void testOplockFieldMapping() {
+        ClassMapping cm = (ClassMapping) OpenJPAPersistence.getMetaData(
+            emf, OptimisticLockInstance.class);
+        FieldMapping fm = cm.getFieldMapping("oplock");
+        assertEquals(1, fm.getColumns().length);
+    }
+
+    public void testBulkUpdateWithManualVersionIncrement() {
+        bulkUpdateHelper(true);
+    }
+
+    public void testBulkUpdateWithoutManualVersionIncrement() {
+        bulkUpdateHelper(false);
+    }
+
+    private void bulkUpdateHelper(boolean incrementVersionField) {
+        EntityManager em = emf.createEntityManager();
+
+        em.getTransaction().begin();
+        OptimisticLockInstance oli = (OptimisticLockInstance) em.createQuery(
+            "SELECT o FROM OptimisticLockInstance o WHERE o.str = 'foo'")
+            .getSingleResult();
+        assertNotNull(oli);
+        em.lock(oli, LockModeType.READ);
+
+        EntityManager em2 = emf.createEntityManager();
+        em2.getTransaction().begin();
+        em2.createQuery("UPDATE OptimisticLockInstance o SET o.str = 'foo', "
+            + "o.intField = o.intField + 1"
+            + (incrementVersionField ? ", o.oplock = o.oplock + 1 " : "")
+            + "WHERE o.str = 'foo'")
+            .executeUpdate();
+        em2.getTransaction().commit();
+        em2.close();
+
+        try {
+            em.getTransaction().commit();
+            fail("transaction should have failed");
+        } catch (RollbackException re) {
+            assertTrue("nested exception must be an oplock exception",
+                re.getCause() instanceof OptimisticLockException);
+        } finally {
+            em.close();
+        }
+    }
+}