Skip to content

Commit 2e9b0cd

Browse files
committed
[WIP][OPENJPA-2940] Implements version JPQL function
1 parent bef97d8 commit 2e9b0cd

9 files changed

Lines changed: 292 additions & 1 deletion

File tree

openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,11 @@ public Value getNativeObjectId(Value val) {
660660
}
661661
return new GetNativeObjectId((PCPath) val);
662662
}
663+
664+
@Override
665+
public Value version(Value val) {
666+
return new VersionVal((PCPath) val);
667+
}
663668

664669
@Override
665670
public Value getMapValue(Value map, Value arg) {
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.openjpa.jdbc.kernel.exps;
20+
21+
import java.sql.SQLException;
22+
23+
import org.apache.openjpa.jdbc.kernel.exps.PCPath.PathExpState;
24+
import org.apache.openjpa.jdbc.meta.ClassMapping;
25+
import org.apache.openjpa.jdbc.schema.Column;
26+
import org.apache.openjpa.jdbc.sql.Result;
27+
import org.apache.openjpa.jdbc.sql.SQLBuffer;
28+
import org.apache.openjpa.jdbc.sql.Select;
29+
import org.apache.openjpa.kernel.Filters;
30+
import org.apache.openjpa.kernel.exps.ExpressionVisitor;
31+
import org.apache.openjpa.lib.util.Localizer;
32+
import org.apache.openjpa.meta.ClassMetaData;
33+
import org.apache.openjpa.meta.FieldMetaData;
34+
import org.apache.openjpa.util.UserException;
35+
36+
/**
37+
* Select the version value of an object; typically used in projections.
38+
*
39+
* @author Abe White
40+
* @author Paulo Cristovão Filho
41+
*/
42+
class VersionVal
43+
extends AbstractVal {
44+
45+
46+
private static final long serialVersionUID = 1L;
47+
48+
private static final Localizer _loc = Localizer.forPackage(VersionVal.class);
49+
50+
protected final PCPath _path;
51+
private ClassMetaData _meta = null;
52+
53+
/**
54+
* Constructor. Provide the value whose version to extract.
55+
*/
56+
public VersionVal(PCPath path) {
57+
_path = path;
58+
}
59+
60+
/**
61+
* Return the version column.
62+
*/
63+
public Column[] getColumns(ExpState state) {
64+
return _path.getClassMapping(state).getVersionFieldMapping().getColumns();
65+
}
66+
67+
@Override
68+
public ClassMetaData getMetaData() {
69+
return _meta;
70+
}
71+
72+
@Override
73+
public void setMetaData(ClassMetaData meta) {
74+
_meta = meta;
75+
}
76+
77+
@Override
78+
public Class getType() {
79+
FieldMetaData versionField = _path.getMetaData().getVersionField();
80+
if (versionField != null) {
81+
return versionField.getType();
82+
}
83+
return null;
84+
}
85+
86+
@Override
87+
public void setImplicitType(Class type) {
88+
}
89+
90+
@Override
91+
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
92+
ExpState state = _path.initialize(sel, ctx, JOIN_REL);
93+
94+
// it's difficult to get calls on non-pc fields to always return null
95+
// without screwing up the SQL, to just don't let users call it on
96+
// non-pc fields at all
97+
ClassMapping cls = _path.getClassMapping(state);
98+
if (cls == null || cls.getEmbeddingMapping() != null)
99+
throw new UserException(_loc.get("bad-getobjectid", _path.getFieldMapping(state)));
100+
return state;
101+
}
102+
103+
@Override
104+
public Object toDataStoreValue(Select sel, ExpContext ctx, ExpState state, Object val) {
105+
ClassMapping mapping = _path.getClassMapping(state);
106+
if (mapping.getVersion() != null) {
107+
return Filters.convert(val, getType());
108+
}
109+
return null;
110+
}
111+
112+
@Override
113+
public void select(Select sel, ExpContext ctx, ExpState state,
114+
boolean pks) {
115+
selectColumns(sel, ctx, state, true);
116+
}
117+
118+
@Override
119+
public void selectColumns(Select sel, ExpContext ctx, ExpState state,
120+
boolean pks) {
121+
sel.setSchemaAlias(_path.getSchemaAlias());
122+
sel.select(getColumns(state), ((PathExpState) state).joins);
123+
}
124+
125+
@Override
126+
public void groupBy(Select sel, ExpContext ctx, ExpState state) {
127+
_path.groupBy(sel, ctx, state);
128+
}
129+
130+
@Override
131+
public void orderBy(Select sel, ExpContext ctx, ExpState state,
132+
boolean asc) {
133+
_path.orderBy(sel, ctx, state, asc);
134+
}
135+
136+
@Override
137+
public Object load(ExpContext ctx, ExpState state, Result res)
138+
throws SQLException {
139+
return res.getObject(getColumns(state)[0], null, ((PathExpState) state).joins);
140+
}
141+
142+
@Override
143+
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
144+
Val other, ExpState otherState) {
145+
_path.calculateValue(sel, ctx, state, null, null);
146+
}
147+
148+
@Override
149+
public int length(Select sel, ExpContext ctx, ExpState state) {
150+
return _path.length(sel, ctx, state);
151+
}
152+
153+
@Override
154+
public void appendTo(Select sel, ExpContext ctx, ExpState state,
155+
SQLBuffer sql, int index) {
156+
_path.appendTo(sel, ctx, state, sql, index);
157+
}
158+
159+
@Override
160+
public void acceptVisit(ExpressionVisitor visitor) {
161+
visitor.enter(this);
162+
_path.acceptVisit(visitor);
163+
visitor.exit(this);
164+
}
165+
}
166+

openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,11 @@ Value newExtension(FilterListener listener, Value target,
538538
* Returns the id of the given value
539539
*/
540540
Value getNativeObjectId(Value val);
541+
542+
/**
543+
* Returns the version of the given value
544+
*/
545+
Value version(Value val);
541546

542547
/**
543548
* Return a simple case expression

openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,11 @@ public Value getObjectId(Value val) {
820820
public Value getNativeObjectId(Value val) {
821821
return new GetObjectId((Val) val);
822822
}
823+
824+
@Override
825+
public Value version(Value val) {
826+
return new VersionVal((Val) val);
827+
}
823828

824829
/**
825830
* Key that implements hashCode and equals methods for object arrays.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.openjpa.kernel.exps;
20+
21+
import org.apache.openjpa.kernel.StoreContext;
22+
23+
/**
24+
* Get the version of an object.
25+
*
26+
* @author Abe White
27+
* @author Paulo Cristovão Filho
28+
*/
29+
class VersionVal
30+
extends Val {
31+
32+
33+
private static final long serialVersionUID = 1L;
34+
private final Val _val;
35+
36+
/**
37+
* Constructor. Provide value whose version to extract.
38+
*/
39+
public VersionVal(Val val) {
40+
_val = val;
41+
}
42+
43+
@Override
44+
public Class getType() {
45+
return Object.class;
46+
}
47+
48+
@Override
49+
public void setImplicitType(Class type) {
50+
}
51+
52+
@Override
53+
protected Object eval(Object candidate, Object orig,
54+
StoreContext ctx, Object[] params) {
55+
return ctx.getVersion(_val.eval(candidate, orig, ctx, params));
56+
}
57+
58+
@Override
59+
public void acceptVisit(ExpressionVisitor visitor) {
60+
visitor.enter(this);
61+
_val.acceptVisit(visitor);
62+
visitor.exit(this);
63+
}
64+
}

openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,9 @@ else if (node.getChildCount() == 2
15701570
case JJTIDFUNCTION:
15711571
return factory.getNativeObjectId(getValue(firstChild(node)));
15721572

1573+
case JJTVERSIONFUNCTION:
1574+
return factory.version(getValue(firstChild(node)));
1575+
15731576
default:
15741577
throw parseException(EX_FATAL, "bad-tree",
15751578
new Object[]{ node }, null);

openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,20 @@ public void testIdFunctionOnSelect() {
258258
}
259259
fail();
260260
}
261-
261+
262+
@Test
263+
public void testVersionFunctionEquals() {
264+
try {
265+
String query = "SELECT u FROM User AS u WHERE VERSION(u) = :version";
266+
JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
267+
assertNotNull(node);
268+
return;
269+
} catch (ParseException ex) {
270+
ex.printStackTrace();
271+
}
272+
fail();
273+
}
274+
262275
@Test
263276
public void testVersionFunctionSimple() {
264277
try {

openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import jakarta.persistence.JoinColumn;
3232
import jakarta.persistence.Lob;
3333
import jakarta.persistence.OneToOne;
34+
import jakarta.persistence.Version;
3435

3536
import org.apache.openjpa.persistence.PersistentCollection;
3637

@@ -66,6 +67,9 @@ public class CompUser {
6667
@Enumerated
6768
@Basic
6869
private CreditRating creditRating;
70+
71+
@Version
72+
private int version;
6973

7074
public CompUser() {
7175
}
@@ -149,4 +153,12 @@ public CreditRating getCreditRating() {
149153
public void setCreditRating(CreditRating creditRating) {
150154
this.creditRating = creditRating;
151155
}
156+
157+
public int getVersion() {
158+
return version;
159+
}
160+
161+
public void setVersion(int version) {
162+
this.version = version;
163+
}
152164
}

openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,24 @@ public void testIdFunctionOnWhere() {
10181018

10191019
endEm(em);
10201020
}
1021+
1022+
public void testVersionFunction() {
1023+
EntityManager em = currentEntityManager();
1024+
1025+
String query = "SELECT VERSION(u) FROM CompUser AS u WHERE u.name = :name";
1026+
List result = em.createQuery(query).setParameter("name", "Seetha").getResultList();
1027+
1028+
assertEquals(1, result.size());
1029+
int currentVersion = (int) result.get(0);
1030+
1031+
query = "SELECT u FROM CompUser AS u WHERE u.name = :name AND version(u) = :version";
1032+
result = em.createQuery(query).setParameter("name", "Seetha").setParameter("version", currentVersion).getResultList();
1033+
1034+
assertEquals(1, result.size());
1035+
assertEquals("Seetha", ((CompUser) result.get(0)).getName());
1036+
1037+
endEm(em);
1038+
}
10211039

10221040
public CompUser createUser(String name, String cName, Address add, int age,
10231041
boolean isMale) {

0 commit comments

Comments
 (0)