This is an automated email from the ASF dual-hosted git repository.
pcristof pushed a commit to branch OPENJPA-2940
in repository https://gitbox.apache.org/repos/asf/openjpa.git
The following commit(s) were added to refs/heads/OPENJPA-2940 by this push:
new 2e9b0cdd1 [WIP][OPENJPA-2940] Implements version JPQL function
2e9b0cdd1 is described below
commit 2e9b0cdd1132c241b114a634fa791a1c95bf3163
Author: Paulo Cristovão de Araújo Silva Filho <[email protected]>
AuthorDate: Mon Nov 17 20:34:16 2025 -0300
[WIP][OPENJPA-2940] Implements version JPQL function
---
.../jdbc/kernel/exps/JDBCExpressionFactory.java | 5 +
.../openjpa/jdbc/kernel/exps/VersionVal.java | 166 +++++++++++++++++++++
.../openjpa/kernel/exps/ExpressionFactory.java | 5 +
.../kernel/exps/InMemoryExpressionFactory.java | 5 +
.../org/apache/openjpa/kernel/exps/VersionVal.java | 64 ++++++++
.../openjpa/kernel/jpql/JPQLExpressionBuilder.java | 3 +
.../apache/openjpa/kernel/jpql/TestJPQLParser.java | 15 +-
.../openjpa/persistence/common/apps/CompUser.java | 12 ++
.../jpql/functions/TestEJBQLFunction.java | 18 +++
9 files changed, 292 insertions(+), 1 deletion(-)
diff --git
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
index ded7d807b..2a2f7b816 100644
---
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
+++
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
@@ -660,6 +660,11 @@ public class JDBCExpressionFactory
}
return new GetNativeObjectId((PCPath) val);
}
+
+ @Override
+ public Value version(Value val) {
+ return new VersionVal((PCPath) val);
+ }
@Override
public Value getMapValue(Value map, Value arg) {
diff --git
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/VersionVal.java
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/VersionVal.java
new file mode 100644
index 000000000..4c0b516dc
--- /dev/null
+++
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/VersionVal.java
@@ -0,0 +1,166 @@
+/*
+ * 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.kernel.exps;
+
+import java.sql.SQLException;
+
+import org.apache.openjpa.jdbc.kernel.exps.PCPath.PathExpState;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.SQLBuffer;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.exps.ExpressionVisitor;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.util.UserException;
+
+/**
+ * Select the version value of an object; typically used in projections.
+ *
+ * @author Abe White
+ * @author Paulo Cristovão Filho
+ */
+class VersionVal
+ extends AbstractVal {
+
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Localizer _loc =
Localizer.forPackage(VersionVal.class);
+
+ protected final PCPath _path;
+ private ClassMetaData _meta = null;
+
+ /**
+ * Constructor. Provide the value whose version to extract.
+ */
+ public VersionVal(PCPath path) {
+ _path = path;
+ }
+
+ /**
+ * Return the version column.
+ */
+ public Column[] getColumns(ExpState state) {
+ return
_path.getClassMapping(state).getVersionFieldMapping().getColumns();
+ }
+
+ @Override
+ public ClassMetaData getMetaData() {
+ return _meta;
+ }
+
+ @Override
+ public void setMetaData(ClassMetaData meta) {
+ _meta = meta;
+ }
+
+ @Override
+ public Class getType() {
+ FieldMetaData versionField = _path.getMetaData().getVersionField();
+ if (versionField != null) {
+ return versionField.getType();
+ }
+ return null;
+ }
+
+ @Override
+ public void setImplicitType(Class type) {
+ }
+
+ @Override
+ public ExpState initialize(Select sel, ExpContext ctx, int flags) {
+ ExpState state = _path.initialize(sel, ctx, JOIN_REL);
+
+ // it's difficult to get calls on non-pc fields to always return null
+ // without screwing up the SQL, to just don't let users call it on
+ // non-pc fields at all
+ ClassMapping cls = _path.getClassMapping(state);
+ if (cls == null || cls.getEmbeddingMapping() != null)
+ throw new UserException(_loc.get("bad-getobjectid",
_path.getFieldMapping(state)));
+ return state;
+ }
+
+ @Override
+ public Object toDataStoreValue(Select sel, ExpContext ctx, ExpState state,
Object val) {
+ ClassMapping mapping = _path.getClassMapping(state);
+ if (mapping.getVersion() != null) {
+ return Filters.convert(val, getType());
+ }
+ return null;
+ }
+
+ @Override
+ public void select(Select sel, ExpContext ctx, ExpState state,
+ boolean pks) {
+ selectColumns(sel, ctx, state, true);
+ }
+
+ @Override
+ public void selectColumns(Select sel, ExpContext ctx, ExpState state,
+ boolean pks) {
+ sel.setSchemaAlias(_path.getSchemaAlias());
+ sel.select(getColumns(state), ((PathExpState) state).joins);
+ }
+
+ @Override
+ public void groupBy(Select sel, ExpContext ctx, ExpState state) {
+ _path.groupBy(sel, ctx, state);
+ }
+
+ @Override
+ public void orderBy(Select sel, ExpContext ctx, ExpState state,
+ boolean asc) {
+ _path.orderBy(sel, ctx, state, asc);
+ }
+
+ @Override
+ public Object load(ExpContext ctx, ExpState state, Result res)
+ throws SQLException {
+ return res.getObject(getColumns(state)[0], null, ((PathExpState)
state).joins);
+ }
+
+ @Override
+ public void calculateValue(Select sel, ExpContext ctx, ExpState state,
+ Val other, ExpState otherState) {
+ _path.calculateValue(sel, ctx, state, null, null);
+ }
+
+ @Override
+ public int length(Select sel, ExpContext ctx, ExpState state) {
+ return _path.length(sel, ctx, state);
+ }
+
+ @Override
+ public void appendTo(Select sel, ExpContext ctx, ExpState state,
+ SQLBuffer sql, int index) {
+ _path.appendTo(sel, ctx, state, sql, index);
+ }
+
+ @Override
+ public void acceptVisit(ExpressionVisitor visitor) {
+ visitor.enter(this);
+ _path.acceptVisit(visitor);
+ visitor.exit(this);
+ }
+}
+
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
index a4b19defd..e18c8a7f3 100644
---
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
@@ -538,6 +538,11 @@ public interface ExpressionFactory {
* Returns the id of the given value
*/
Value getNativeObjectId(Value val);
+
+ /**
+ * Returns the version of the given value
+ */
+ Value version(Value val);
/**
* Return a simple case expression
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
index 654839d46..479c308a0 100644
---
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
@@ -820,6 +820,11 @@ public class InMemoryExpressionFactory
public Value getNativeObjectId(Value val) {
return new GetObjectId((Val) val);
}
+
+ @Override
+ public Value version(Value val) {
+ return new VersionVal((Val) val);
+ }
/**
* Key that implements hashCode and equals methods for object arrays.
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/VersionVal.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/VersionVal.java
new file mode 100644
index 000000000..3231b473b
--- /dev/null
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/VersionVal.java
@@ -0,0 +1,64 @@
+/*
+ * 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.kernel.exps;
+
+import org.apache.openjpa.kernel.StoreContext;
+
+/**
+ * Get the version of an object.
+ *
+ * @author Abe White
+ * @author Paulo Cristovão Filho
+ */
+class VersionVal
+ extends Val {
+
+
+ private static final long serialVersionUID = 1L;
+ private final Val _val;
+
+ /**
+ * Constructor. Provide value whose version to extract.
+ */
+ public VersionVal(Val val) {
+ _val = val;
+ }
+
+ @Override
+ public Class getType() {
+ return Object.class;
+ }
+
+ @Override
+ public void setImplicitType(Class type) {
+ }
+
+ @Override
+ protected Object eval(Object candidate, Object orig,
+ StoreContext ctx, Object[] params) {
+ return ctx.getVersion(_val.eval(candidate, orig, ctx, params));
+ }
+
+ @Override
+ public void acceptVisit(ExpressionVisitor visitor) {
+ visitor.enter(this);
+ _val.acceptVisit(visitor);
+ visitor.exit(this);
+ }
+}
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
index 91726cd44..056442343 100644
---
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
@@ -1570,6 +1570,9 @@ public class JPQLExpressionBuilder
case JJTIDFUNCTION:
return factory.getNativeObjectId(getValue(firstChild(node)));
+ case JJTVERSIONFUNCTION:
+ return factory.version(getValue(firstChild(node)));
+
default:
throw parseException(EX_FATAL, "bad-tree",
new Object[]{ node }, null);
diff --git
a/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
b/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
index 9bacdf083..39ff4e8b4 100644
---
a/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
+++
b/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
@@ -258,7 +258,20 @@ public class TestJPQLParser {
}
fail();
}
-
+
+ @Test
+ public void testVersionFunctionEquals() {
+ try {
+ String query = "SELECT u FROM User AS u WHERE VERSION(u) =
:version";
+ JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+ assertNotNull(node);
+ return;
+ } catch (ParseException ex) {
+ ex.printStackTrace();
+ }
+ fail();
+ }
+
@Test
public void testVersionFunctionSimple() {
try {
diff --git
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
index 754fb2cfd..3bd1180cf 100644
---
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
+++
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
@@ -31,6 +31,7 @@ import jakarta.persistence.InheritanceType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.OneToOne;
+import jakarta.persistence.Version;
import org.apache.openjpa.persistence.PersistentCollection;
@@ -66,6 +67,9 @@ public class CompUser {
@Enumerated
@Basic
private CreditRating creditRating;
+
+ @Version
+ private int version;
public CompUser() {
}
@@ -149,4 +153,12 @@ public class CompUser {
public void setCreditRating(CreditRating creditRating) {
this.creditRating = creditRating;
}
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
}
diff --git
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
index 29ef52078..d4488eba0 100644
---
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
+++
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
@@ -1018,6 +1018,24 @@ public class TestEJBQLFunction extends AbstractTestCase {
endEm(em);
}
+
+ public void testVersionFunction() {
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT VERSION(u) FROM CompUser AS u WHERE u.name =
:name";
+ List result = em.createQuery(query).setParameter("name",
"Seetha").getResultList();
+
+ assertEquals(1, result.size());
+ int currentVersion = (int) result.get(0);
+
+ query = "SELECT u FROM CompUser AS u WHERE u.name = :name AND
version(u) = :version";
+ result = em.createQuery(query).setParameter("name",
"Seetha").setParameter("version", currentVersion).getResultList();
+
+ assertEquals(1, result.size());
+ assertEquals("Seetha", ((CompUser) result.get(0)).getName());
+
+ endEm(em);
+ }
public CompUser createUser(String name, String cName, Address add, int age,
boolean isMale) {