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);
+ }
+ }
+}