Author: ppoddar
Date: Mon Aug 25 07:25:36 2008
New Revision: 688734

URL: http://svn.apache.org/viewvc?rev=688734&view=rev
Log:
OPENJPA-697: Upgrade version strategy for multiple numeric-valued columns to 
support @VersionColumns.

Added:
    
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MultiColumnVersionStrategy.java
    
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/MultiColumnVersionPC.java
    
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestMultiColumnVersion.java
Modified:
    
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
    
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.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/TimestampVersionStrategy.java
    
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
    
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingDefaults.java

Modified: 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java?rev=688734&r1=688733&r2=688734&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
 (original)
+++ 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
 Mon Aug 25 07:25:36 2008
@@ -50,6 +50,7 @@
 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedByteArrayFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedCharArrayFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedClobFieldStrategy;
+import org.apache.openjpa.jdbc.meta.strats.MultiColumnVersionStrategy;
 import 
org.apache.openjpa.jdbc.meta.strats.NanoPrecisionTimestampVersionStrategy;
 import org.apache.openjpa.jdbc.meta.strats.NoneClassStrategy;
 import org.apache.openjpa.jdbc.meta.strats.NoneDiscriminatorStrategy;
@@ -626,6 +627,8 @@
 
         if (NumberVersionStrategy.ALIAS.equals(name))
             strat = NumberVersionStrategy.class;
+        else if (MultiColumnVersionStrategy.ALIAS.equals(name))
+               strat = MultiColumnVersionStrategy.class;
         else if (TimestampVersionStrategy.ALIAS.equals(name))
             strat = TimestampVersionStrategy.class;
         else if (NanoPrecisionTimestampVersionStrategy.ALIAS.equals(name))

Modified: 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java?rev=688734&r1=688733&r2=688734&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java
 (original)
+++ 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java
 Mon Aug 25 07:25:36 2008
@@ -18,9 +18,12 @@
  */
 package org.apache.openjpa.jdbc.meta.strats;
 
+import java.lang.reflect.Array;
 import java.math.BigDecimal;
 import java.sql.SQLException;
 import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
@@ -36,8 +39,11 @@
 import org.apache.openjpa.kernel.StoreManager;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.util.MetaDataException;
 
+import serp.util.Numbers;
+
 /**
  * Uses a single column and corresponding version object.
  *
@@ -54,6 +60,13 @@
      * strategy uses. This method is only used during mapping installation.
      */
     protected abstract int getJavaType();
+    
+    /**
+     * Return the code from [EMAIL PROTECTED] JavaTypes} for the version value 
this given
+     * column index uses. Only used if the version strategy employs more than
+     * one column. 
+     */
+    protected abstract int getJavaType(int i);
 
     /**
      * Return the next version given the current one, which may be null.
@@ -73,7 +86,12 @@
             return -1;
         if (v2 == null)
             return 1;
-
+        
+        if (v1.getClass().isArray()) {
+               if (!v2.getClass().isArray())
+                       throw new InternalException();
+               return compare((Object[])v1, (Object[])v2);
+        }
         if (v1.getClass() != v2.getClass()) {
             if (v1 instanceof Number && !(v1 instanceof BigDecimal))
                 v1 = new BigDecimal(((Number) v1).doubleValue());
@@ -85,6 +103,29 @@
         return ((Comparable) v1).compareTo(v2);
     }
 
+
+       /**
+        * Compare each element of the given arrays that must be of equal size. 
+        * 
+        * @return If each element comparison results into same sign then 
returns 
+        * that sign. If some elements compare equal and all the rest has the 
same
+        * sign then return that sign. Otherwise, return 1.
+        */
+       protected int compare(Object[] a1, Object[] a2) {
+               if (a1.length != a2.length)
+               throw new InternalException();
+               Set<Integer> comps = new HashSet<Integer>();
+               for (int i = 0; i < a1.length; i++)
+                       comps.add(sign(compare(a1[i], a2[i])));
+               if (comps.size() == 1 || (comps.size() == 2 && comps.remove(0)))
+                       return comps.iterator().next();
+               return 1;
+       }
+       
+       int sign(int i) {
+               return (i > 0) ? 1 : (i == 0) ? 0 : -1;
+       }
+
     public void map(boolean adapt) {
         ClassMapping cls = vers.getClassMapping();
         if (cls.getJoinablePCSuperclassMapping() != null
@@ -95,18 +136,35 @@
         info.assertNoJoin(vers, true);
         info.assertNoForeignKey(vers, !adapt);
         info.assertNoUnique(vers, false);
+        if (info.getColumns().size() > 1) {
+               Column[] templates = new Column[info.getColumns().size()];
+               for (int i = 0; i < info.getColumns().size(); i++) {
+                templates[i] = new Column();
+                       Column infoColumn = (Column)info.getColumns().get(i);
+                       templates[i].setType(infoColumn.getType());
+                       templates[i].setSize(infoColumn.getSize());
+                       
templates[i].setDecimalDigits(infoColumn.getDecimalDigits());
+                       templates[i].setJavaType(getJavaType(i));
+                       templates[i].setName("versn" +i);
+               }
+               Column[] cols = info.getColumns(vers, templates, adapt);
+               for (int i = 0; i < cols.length; i++)
+                       cols[i].setVersionStrategy(this);
+               vers.setColumns(cols);
+               vers.setColumnIO(info.getColumnIO());
+        } else {
+           Column tmplate = new Column();
+           tmplate.setJavaType(getJavaType());
+           tmplate.setName("versn");
+
+           Column[] cols = info.getColumns(vers, new Column[]{ tmplate }, 
adapt);
+           cols[0].setVersionStrategy(this);
+           vers.setColumns(cols);
+           vers.setColumnIO(info.getColumnIO());
 
-        Column tmplate = new Column();
-        tmplate.setJavaType(getJavaType());
-        tmplate.setName("versn");
-
-        Column[] cols = info.getColumns(vers, new Column[]{ tmplate }, adapt);
-        cols[0].setVersionStrategy(this);
-        vers.setColumns(cols);
-        vers.setColumnIO(info.getColumnIO());
-
-        Index idx = info.getIndex(vers, cols, adapt);
-        vers.setIndex(idx);
+           Index idx = info.getIndex(vers, cols, adapt);
+           vers.setIndex(idx);
+        }
     }
 
     public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
@@ -118,7 +176,7 @@
             Row.ACTION_INSERT, sm, true);
         for (int i = 0; i < cols.length; i++)
             if (io.isInsertable(i, initial == null))
-                row.setObject(cols[i], initial);
+                row.setObject(cols[i], getColumnValue(initial, i));
 
         // set initial version into state manager
         Object nextVersion;
@@ -143,9 +201,9 @@
         // set where and update conditions on row
         for (int i = 0; i < cols.length; i++) {
             if (curVersion != null && sm.isVersionCheckRequired())
-                row.whereObject(cols[i], curVersion);
+                row.whereObject(cols[i], getColumnValue(curVersion, i));
             if (vers.getColumnIO().isUpdatable(i, nextVersion == null))
-                row.setObject(cols[i], nextVersion);
+                row.setObject(cols[i], getColumnValue(nextVersion, i));
         }
 
         if (nextVersion != null)
@@ -162,11 +220,7 @@
         Object curVersion = sm.getVersion();
         Object cur;
         for (int i = 0; i < cols.length; i++) {
-            if (cols.length == 1 || curVersion == null)
-                cur = curVersion;
-            else
-                cur = ((Object[]) curVersion)[i];
-
+            cur = getColumnValue(curVersion, i);
             // set where and update conditions on row
             if (cur != null)
                 row.whereObject(cols[i], cur);
@@ -186,19 +240,8 @@
         if (!res.contains(cols[0]))
             return null;
 
-        Object version = null;
-        if (cols.length > 0)
-            version = new Object[cols.length];
-        Object cur;
-        for (int i = 0; i < cols.length; i++) {
-            if (i > 0 && !res.contains(cols[i]))
-                return null;
-            cur = res.getObject(cols[i], -1, null);
-            if (cols.length == 1)
-                version = cur;
-            else
-                ((Object[]) version)[i] = cur;
-        }
+        Object version = populateFromResult(res);
+
         // OPENJPA-662 Allow a null StateManager because this method may just 
be
         // invoked to get the result of projection query
         if (sm != null)
@@ -220,31 +263,8 @@
                 return false;
 
             Object memVersion = sm.getVersion();
-            Object dbVersion = null;
-            if (cols.length > 1)
-                dbVersion = new Object[cols.length];
-
-            boolean refresh = false;
-            Object mem, db;
-            for (int i = 0; i < cols.length; i++) {
-                db = res.getObject(cols[i], -1, null);
-                if (cols.length == 1)
-                    dbVersion = db;
-                else
-                    ((Object[]) dbVersion)[i] = db;
-
-                // if we haven't already determined that we need a refresh,
-                // check if the mem version is earlier than the db one
-                if (!refresh) {
-                    if (cols.length == 1 || memVersion == null)
-                        mem = memVersion;
-                    else
-                        mem = ((Object[]) memVersion)[i];
-
-                    if (mem == null || (db != null && compare(mem, db) < 0))
-                        refresh = true;
-                }
-            }
+            Object dbVersion  = populateFromResult(res);
+            boolean refresh   = compare(memVersion, dbVersion) < 0;
 
             if (updateVersion)
                 sm.setVersion(dbVersion);
@@ -267,4 +287,30 @@
             return StoreManager.VERSION_LATER;
         return StoreManager.VERSION_SAME;
     }
+        
+    /**
+     * Populate values of a version object from the given result.
+     * 
+     * @return a single Object or an array depending on whether using a single
+     * or multiple columns being used for representation.
+    */
+    Object populateFromResult(Result res) throws SQLException {
+        if (res == null)
+               return null;
+       
+        Column[] cols = vers.getColumns();
+        Object[] values = new Object[cols.length];
+        for (int i = 0; i < cols.length; i++) {
+            values[i] = res.getObject(cols[i], -1, null);
+        }
+        return (cols.length == 1) ? values[0] : values;
+    }
+    
+    Object getColumnValue(Object o, int idx) {
+       if (o == null) 
+               return null;
+       if (o.getClass().isArray())
+               return Array.get(o, idx);
+       return o;
+    }
 }

Added: 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MultiColumnVersionStrategy.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MultiColumnVersionStrategy.java?rev=688734&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MultiColumnVersionStrategy.java
 (added)
+++ 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MultiColumnVersionStrategy.java
 Mon Aug 25 07:25:36 2008
@@ -0,0 +1,113 @@
+/*
+ * 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.jdbc.meta.strats;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.Schemas;
+import org.apache.openjpa.meta.JavaTypes;
+
+import serp.util.Numbers;
+
+/**
+ * Uses multiple version numbers spanning multiple columns for optimistic 
+ * versioning.
+ * 
+ * @since 1.3.0
+ *
+ * @author Pinaki Poddar
+ */
+public class MultiColumnVersionStrategy extends NumberVersionStrategy {
+
+    public static final String ALIAS = "version-numbers";
+    
+    private Number[] _initials = null;
+    private Integer[] _javaTypes;
+    
+    @Override
+    public void initialize() {
+       if (_initials == null) {
+               Column[] cols = vers.getColumns();
+               _initials = new Number[cols.length];
+               for (int i = 0; i < cols.length; i++) {
+                       _initials[i] = nextValue(Numbers.valueOf(0), 
getJavaType(i));
+               }
+       }
+       super.initialize();
+    }
+       
+    /**
+     * Set the initial value for version columns. Defaults to 1 for each 
column.
+     */
+    public void setInitialValues(Number[] initial) {
+       _initials = new Number[initial.length];
+       System.arraycopy(initial, 0, _initials, 0, initial.length);
+    }
+    
+    /**
+     * Return the initial values for version columns. Defaults to 1 for each 
+     * column.
+     */
+    public Number[] getInitialValues() {
+       return _initials;
+    }
+
+    public String getAlias() {
+        return ALIAS;
+    }
+    
+    protected int getJavaType() {
+        return JavaTypes.ARRAY;
+    }
+    
+    protected int getJavaType(int i) {
+       if (_javaTypes == null) {
+               _javaTypes = new 
Integer[vers.getMappingInfo().getColumns().size()];
+       }
+       if (_javaTypes[i] == null) {
+               Column col = (Column)vers.getMappingInfo().getColumns().get(i);
+               if (!StringUtils.isEmpty(col.getTypeName())) {
+                       Class javaType = Schemas.getJavaType(col.getType(), 
+                                       col.getSize(), col.getDecimalDigits());
+                       _javaTypes[i] = JavaTypes.getTypeCode(javaType);
+               } else {
+                       _javaTypes[i] = JavaTypes.INT;
+               }
+       }
+       return _javaTypes[i];
+    }
+
+    protected Object nextVersion(Object version) {
+        if (version == null)
+            return _initials;
+        Object[] values = (Object[])version;
+        Number[] result = new Number[values.length];
+        for (int i = 0; i < values.length; i++)
+               result[i] = nextValue(values[i], getJavaType(i));
+        return result;
+    }
+    
+    Number nextValue(Object number, int javaTypeCode) {
+       Number result = (number == null) ? 1 : ((Number)number).intValue() + 1;
+       return  (Number)JavaTypes.convert(""+result, javaTypeCode);
+    }
+}

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?rev=688734&r1=688733&r2=688734&view=diff
==============================================================================
--- 
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
 Mon Aug 25 07:25:36 2008
@@ -22,6 +22,7 @@
 import java.util.HashMap;
 
 import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.jdbc.schema.Column;
 import serp.util.Numbers;
 
@@ -58,6 +59,10 @@
     protected int getJavaType() {
         return JavaTypes.INT;
     }
+    
+    protected int getJavaType(int i) {
+       throw new InternalException("multi-column-version-not-supported");
+    }
 
     protected Object nextVersion(Object version) {
         if (version == null)

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?rev=688734&r1=688733&r2=688734&view=diff
==============================================================================
--- 
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
 Mon Aug 25 07:25:36 2008
@@ -25,6 +25,7 @@
 
 import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
 import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.util.InternalException;
 
 /**
  * Uses a timestamp for optimistic versioning.
@@ -43,6 +44,10 @@
     protected int getJavaType() {
         return JavaSQLTypes.TIMESTAMP;
     }
+    
+    protected int getJavaType(int i) {
+        throw new InternalException("multi-column-version-not-supported");
+    }
 
     protected Object nextVersion(Object version) {
         return new Timestamp(System.currentTimeMillis());

Modified: 
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java?rev=688734&r1=688733&r2=688734&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
 Mon Aug 25 07:25:36 2008
@@ -73,6 +73,7 @@
 import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy;
 import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
 import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.Schemas;
 import org.apache.openjpa.jdbc.schema.Unique;
 import org.apache.openjpa.jdbc.sql.DBDictionary;
 import org.apache.openjpa.lib.log.Log;
@@ -808,14 +809,18 @@
         Column col = new Column();
         if (!StringUtils.isEmpty(anno.name()))
             col.setName(anno.name());
-        if (!StringUtils.isEmpty(anno.columnDefinition()))
-            col.setTypeName(anno.columnDefinition());
         if (anno.precision() != 0)
             col.setSize(anno.precision());
         else if (anno.length() != 255)
             col.setSize(anno.length());
         col.setNotNull(!anno.nullable());
         col.setDecimalDigits(anno.scale());
+        if (!StringUtils.isEmpty(anno.columnDefinition())) {
+            col.setTypeName(anno.columnDefinition());
+            col.setType(Schemas.getJDBCType(col.getTypeName()));
+            col.setJavaType(JavaTypes.getTypeCode(Schemas.getJavaType
+               (col.getType(), col.getSize(), col.getDecimalDigits())));
+        }
         col.setFlag(Column.FLAG_UNINSERTABLE, !anno.insertable());
         col.setFlag(Column.FLAG_UNUPDATABLE, !anno.updatable());
         return col;

Modified: 
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingDefaults.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingDefaults.java?rev=688734&r1=688733&r2=688734&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingDefaults.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingDefaults.java
 Mon Aug 25 07:25:36 2008
@@ -25,6 +25,7 @@
 import org.apache.openjpa.jdbc.meta.ValueMapping;
 import org.apache.openjpa.jdbc.meta.Version;
 import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.MultiColumnVersionStrategy;
 import org.apache.openjpa.jdbc.meta.strats.NoneDiscriminatorStrategy;
 import org.apache.openjpa.jdbc.meta.strats.NoneVersionStrategy;
 import org.apache.openjpa.jdbc.meta.strats.NumberVersionStrategy;
@@ -84,9 +85,12 @@
             || cls.getVersionField() != null)
             return strat;
 
-        if (vers.getMappingInfo().getColumns().isEmpty())
-            return NoneVersionStrategy.getInstance();
-        return new NumberVersionStrategy();
+        int nColumn = vers.getMappingInfo().getColumns().size();
+        switch (nColumn) {
+               case 0 : return NoneVersionStrategy.getInstance();
+               case 1 : return new NumberVersionStrategy();
+               default: return new MultiColumnVersionStrategy();
+        }
     }
 
     @Override

Added: 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/MultiColumnVersionPC.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/MultiColumnVersionPC.java?rev=688734&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/MultiColumnVersionPC.java
 (added)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/MultiColumnVersionPC.java
 Mon Aug 25 07:25:36 2008
@@ -0,0 +1,63 @@
+/*
+ * 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.jdbc.annotations;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import org.apache.openjpa.persistence.jdbc.VersionColumn;
+import org.apache.openjpa.persistence.jdbc.VersionColumns;
+import org.apache.openjpa.persistence.jdbc.VersionStrategy;
+
+/**
+ * Persistent entity for testing multiple column numeric version strategy as 
+ * set by @VersionColumns annotations.
+ * 
+ * The version columns can have numeric values of different types.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
[EMAIL PROTECTED]
[EMAIL PROTECTED]("version-numbers")
[EMAIL PROTECTED]({
+       @VersionColumn(name="v1"), 
+       @VersionColumn(name="v2"),
+       @VersionColumn(name="v3", columnDefinition="FLOAT", scale=3, 
precision=10)
+})
+public class MultiColumnVersionPC {
+       @Id
+       @GeneratedValue
+       private long id;
+       
+       private String name;
+
+       public long getId() {
+               return id;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public void setName(String name) {
+               this.name = name;
+       }
+}

Added: 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestMultiColumnVersion.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestMultiColumnVersion.java?rev=688734&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestMultiColumnVersion.java
 (added)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestMultiColumnVersion.java
 Mon Aug 25 07:25:36 2008
@@ -0,0 +1,126 @@
+/*
+ * 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.jdbc.annotations;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.strats.MultiColumnVersionStrategy;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Tests numeric version spanning multiple columns. 
+ *
+ * @author Pinaki Poddar
+ */
+public class TestMultiColumnVersion extends SingleEMFTestCase {
+    public void setUp() {
+        setUp(MultiColumnVersionPC.class, CLEAR_TABLES);
+    }
+    
+    public void testVersionStrategyIsSet() {
+       ClassMapping mapping = getMapping(MultiColumnVersionPC.class);
+       assertNotNull(mapping.getVersion());
+       assertTrue(mapping.getVersion().getStrategy() 
+               instanceof MultiColumnVersionStrategy);
+    }
+    
+    public void testVersionOnPersistAndUpdate() {
+       OpenJPAEntityManager em = emf.createEntityManager();
+       em.getTransaction().begin();
+        MultiColumnVersionPC pc = new MultiColumnVersionPC();
+        assertEquals(null, em.getVersion(pc));
+        em.persist(pc);
+        em.getTransaction().commit();
+        assertVersionEquals(new Number[]{1,1, 1.0f}, em.getVersion(pc));
+       
+       em.getTransaction().begin();
+       pc.setName("updated");
+       em.merge(pc);
+        em.getTransaction().commit();
+        assertVersionEquals(new Number[]{2,2, 2.0f}, em.getVersion(pc));
+    }
+
+    public void testConcurrentOptimisticUpdateFails() {
+       OpenJPAEntityManager em1 = emf.createEntityManager();
+        em1.getTransaction().begin();
+        OpenJPAEntityManager em2 = emf.createEntityManager();
+        em2.getTransaction().begin();
+
+        MultiColumnVersionPC pc1 = new MultiColumnVersionPC();
+        em1.persist(pc1);
+        em1.getTransaction().commit();
+        em1.getTransaction().begin();
+        Object oid = em1.getObjectId(pc1);
+        
+        
+        MultiColumnVersionPC pc2 = em2.find(MultiColumnVersionPC.class, oid);
+        assertVersionEquals(em1.getVersion(pc1), em2.getVersion(pc2));
+        
+        pc1.setName("Updated in em1");
+        pc2.setName("Updated in em2");
+        em1.getTransaction().commit();
+        
+        try {
+            em2.getTransaction().commit();
+            fail("Optimistic fail");
+        } catch (Exception e) {
+        } finally {
+            em2.close();
+        }
+    }
+
+    public void testConcurrentOptimisticReadSucceeds() {
+       OpenJPAEntityManager em1 = emf.createEntityManager();
+        em1.getTransaction().begin();
+        OpenJPAEntityManager em2 = emf.createEntityManager();
+        em2.getTransaction().begin();
+
+        MultiColumnVersionPC pc1 = new MultiColumnVersionPC();
+        em1.persist(pc1);
+        em1.getTransaction().commit();
+        em1.getTransaction().begin();
+        Object oid = em1.getObjectId(pc1);
+        
+        
+        MultiColumnVersionPC pc2 = em2.find(MultiColumnVersionPC.class, oid);
+        assertVersionEquals(em1.getVersion(pc1), em2.getVersion(pc2));
+        
+        em1.getTransaction().commit();
+        em2.getTransaction().commit();
+    }
+    
+    static void assertVersionEquals(Object expected, Object actual) {
+       assertTrue(expected.getClass().isArray());
+       assertTrue(actual.getClass().isArray());
+       assertEquals(Array.getLength(expected), Array.getLength(actual));
+       int n = Array.getLength(expected);
+       for (int i = 0; i < n; i++) {
+               Object v1 = Array.get(expected, i);
+               Object v2 = Array.get(actual, i);
+               // exact equality may fail on non-integral values
+               assertTrue("element " + i + " mismatch. Expeceted: " + 
+                           v1 + " actual: " + v2,
+                           Math.abs(((Number)v1).doubleValue() - 
((Number)v2).doubleValue()) 
+                           < 0.01);
+       }
+    }
+}


Reply via email to