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
commit 003f5e5269e4d8150628fa1ef7faa203fb34702e Author: Paulo Cristovão de Araújo Silva Filho <[email protected]> AuthorDate: Mon Sep 22 10:03:30 2025 -0300 [WIP][OPENJPA-2940] Implementing Criteria typecast support --- .../persistence/criteria/TestStringCriteria.java | 2 +- .../criteria/TestTypecastAsCriteria.java | 111 +++++++++++++++++++++ .../persistence/criteria/ExpressionImpl.java | 14 ++- .../openjpa/persistence/criteria/Expressions.java | 18 ++++ .../openjpa/persistence/criteria/PathImpl.java | 14 ++- 5 files changed, 154 insertions(+), 5 deletions(-) diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestStringCriteria.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestStringCriteria.java index 1c7af68b5..3b4a9d5cb 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestStringCriteria.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestStringCriteria.java @@ -402,5 +402,5 @@ public class TestStringCriteria extends CriteriaTest { assertEquivalence(q, jpql); } - + } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestTypecastAsCriteria.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestTypecastAsCriteria.java new file mode 100644 index 000000000..716107259 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestTypecastAsCriteria.java @@ -0,0 +1,111 @@ +/* + * 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.criteria; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Collection; + +import jakarta.persistence.Query; +import jakarta.persistence.Tuple; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.ListJoin; +import jakarta.persistence.criteria.MapJoin; +import jakarta.persistence.criteria.ParameterExpression; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.SetJoin; +import jakarta.persistence.criteria.Subquery; + +import org.apache.openjpa.persistence.test.AllowFailure; + +public class TestTypecastAsCriteria extends CriteriaTest { + + public void testTypecastAsString() { + String jpql = "SELECT c FROM Customer c JOIN c.orders o WHERE CAST(o.quantity AS String) = '0'"; + CriteriaQuery<Customer> q = cb.createQuery(Customer.class); + Root<Customer> c = q.from(Customer.class); + SetJoin<Customer, Order> o = c.joinSet("orders"); + q.where(cb.equal(o.get("quantity").cast(String.class), "0")); + q.select(c); + + assertEquivalence(q, jpql); + } + + public void testTypecastAsInteger() { + String jpql = "SELECT c FROM Customer c JOIN c.orders o WHERE CAST(o.quantity AS INTEGER) = 0"; + CriteriaQuery<Customer> q = cb.createQuery(Customer.class); + Root<Customer> c = q.from(Customer.class); + SetJoin<Customer, Order> o = c.joinSet("orders"); + q.where(cb.equal(o.get("quantity").cast(Integer.class), 0)); + q.select(c); + + assertEquivalence(q, jpql); + } + + public void testTypecastAsLong() { + String jpql = "SELECT c FROM Customer c JOIN c.orders o WHERE CAST(o.quantity AS LONG) = 0"; + CriteriaQuery<Customer> q = cb.createQuery(Customer.class); + Root<Customer> c = q.from(Customer.class); + SetJoin<Customer, Order> o = c.joinSet("orders"); + q.where(cb.equal(o.get("quantity").cast(Long.class), 0l)); + q.select(c); + + assertEquivalence(q, jpql); + } + + public void testTypecastAsFloat() { + String jpql = "SELECT c FROM Customer c JOIN c.orders o WHERE CAST(o.quantity AS float) = 0"; + CriteriaQuery<Customer> q = cb.createQuery(Customer.class); + Root<Customer> c = q.from(Customer.class); + SetJoin<Customer, Order> o = c.joinSet("orders"); + q.where(cb.equal(o.get("quantity").cast(Float.class), 0f)); + q.select(c); + + assertEquivalence(q, jpql); + } + + public void testTypecastAsDouble() { + String jpql = "SELECT c FROM Customer c JOIN c.orders o WHERE CAST(o.quantity AS double) = 0"; + CriteriaQuery<Customer> q = cb.createQuery(Customer.class); + Root<Customer> c = q.from(Customer.class); + SetJoin<Customer, Order> o = c.joinSet("orders"); + q.where(cb.equal(o.get("quantity").cast(Float.class), 0d)); + q.select(c); + + assertEquivalence(q, jpql); + } + + public void testTypecastAsInvalid() { + String jpql = "SELECT c FROM Customer c JOIN c.orders o WHERE CAST(o.quantity AS double) = 0"; + CriteriaQuery<Customer> q = cb.createQuery(Customer.class); + Root<Customer> c = q.from(Customer.class); + SetJoin<Customer, Order> o = c.joinSet("orders"); + try { + q.where(cb.equal(o.get("quantity").cast(LocalDateTime.class), LocalDateTime.now())); + q.select(c); + fail("Should have thrown IllegalTargetException"); + } catch (Throwable t) { + assertTrue(t.getClass() == IllegalArgumentException.class); + } + } +} diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java index 8aa9f8040..32dcc2917 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/ExpressionImpl.java @@ -127,8 +127,18 @@ public Predicate in(Object... values) { @Override public <X> Expression<X> cast(Class<X> type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Not yet implemented (JPA 3.2)"); + if (type == String.class) { + return new Expressions.TypecastAs(this, "STRING"); + } else if (type == Integer.class) { + return new Expressions.TypecastAs(this, "INTEGER"); + } else if (type == Long.class) { + return new Expressions.TypecastAs(this, "LONG"); + } else if (type == Float.class) { + return new Expressions.TypecastAs(this, "FLOAT"); + } else if (type == Double.class) { + return new Expressions.TypecastAs(this, "DOUBLE"); + } + throw new IllegalArgumentException("Target cast not supported"); } @Override diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java index 7cb4f23ba..7d30dc146 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java @@ -613,6 +613,24 @@ class Expressions { return Expressions.asValue(q, OPEN_BRACE, getJavaType().getSimpleName(), CLOSE_BRACE, e); } } + + public static class TypecastAs<B> extends UnaryFunctionalExpression<B> { + private String target; + public TypecastAs(Expression<B> x, String target) { + super(x); + this.target = target; + } + + @Override + public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) { + return factory.newTypecastAsString(Expressions.toValue(e, factory, q)); + } + + @Override + public StringBuilder asValue(AliasContext q) { + return Expressions.asValue(q, "CAST", OPEN_BRACE, e, "AS", target, CLOSE_BRACE); + } + } public static class Concat extends BinarayFunctionalExpression<String> { public Concat(Expression<String> x, Expression<String> y) { diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/PathImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/PathImpl.java index fa8d88a58..6f5ee0ed8 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/PathImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/PathImpl.java @@ -354,7 +354,17 @@ class PathImpl<Z,X> extends ExpressionImpl<X> implements Path<X> { @Override public <X> Expression<X> cast(Class<X> type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Not yet implemented (JPA 3.2)"); + if (type == String.class) { + return new Expressions.TypecastAs(this, "STRING"); + } else if (type == Integer.class) { + return new Expressions.TypecastAs(this, "INTEGER"); + } else if (type == Long.class) { + return new Expressions.TypecastAs(this, "LONG"); + } else if (type == Float.class) { + return new Expressions.TypecastAs(this, "FLOAT"); + } else if (type == Double.class) { + return new Expressions.TypecastAs(this, "DOUBLE"); + } + throw new IllegalArgumentException("Target cast not supported"); } }
