Implement New JPA API 3.1.0 Features
Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java
index f671f45..6497c7b 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -15,6 +15,8 @@
// Oracle - initial API and implementation from Oracle TopLink
// 05/24/2011-2.3 Guy Pelletier
// - 345962: Join fetch query when using tenant discriminator column fails.
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.expressions;
import java.io.BufferedWriter;
@@ -1198,6 +1200,34 @@
/**
* PUBLIC:
+ * This gives access to the local date and time on the database through expression.
+ */
+ public Expression localDateTime() {
+ return getFunction(ExpressionOperator.LocalDateTime);
+ }
+
+ /**
+ * PUBLIC:
+ * This gives access to the local date only on the database through expression.
+ * Note the difference between localDate() and this method. This method does
+ * not return the time portion of local datetime where as localDate() does.
+ */
+ public Expression localDate() {
+ return getFunction(ExpressionOperator.LocalDate);
+ }
+
+ /**
+ * PUBLIC:
+ * This gives access to the local time only on the database through expression.
+ * Note the difference between localDate() and this method. This method does
+ * not return the date portion of local datetime where as localDate() does.
+ */
+ public Expression localTime() {
+ return getFunction(ExpressionOperator.LocalTime);
+ }
+
+ /**
+ * PUBLIC:
* Function, Return the difference between the queried part of a date(i.e. years, days etc.)
* and same part of the given date. The equivalent of the Sybase function DateDiff
* <p>Example:
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
index be93a0c..3f0cf97 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -18,6 +18,8 @@
// - 263904: [PATCH] ExpressionOperator doesn't compare arrays correctly
// 01/23/2018-2.7 Will Dazey
// - 530214: trim operation should not bind parameters
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.expressions;
import java.io.IOException;
@@ -215,6 +217,10 @@
*/
public static final int CurrentTime = 128;
+ public static final int LocalDate = 149;
+ public static final int LocalTime = 150;
+ public static final int LocalDateTime = 151;
+
// Math
public static final int Ceil = 55;
public static final int Cos = 56;
@@ -2982,6 +2988,30 @@
/**
* INTERNAL:
+ * Build operator.
+ */
+ public static ExpressionOperator localDate() {
+ return simpleFunctionNoParentheses(LocalDate, "CURRENT_DATE");
+ }
+
+ /**
+ * INTERNAL:
+ * Build operator.
+ */
+ public static ExpressionOperator localTime() {
+ return simpleFunctionNoParentheses(LocalTime, "CURRENT_TIME");
+ }
+
+ /**
+ * INTERNAL:
+ * Build operator.
+ */
+ public static ExpressionOperator localDateTime() {
+ return simpleFunctionNoParentheses(LocalDateTime, "CURRENT_TIMESTAMP");
+ }
+
+ /**
+ * INTERNAL:
* Create the toLowerCase operator.
*/
public static ExpressionOperator toLowerCase() {
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java
index 955eb12..0a16a5c 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java
@@ -12,6 +12,8 @@
// Contributors:
// Blaise Doughan - 2.5 - initial implementation
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.internal.core.helper;
import java.io.File;
@@ -21,6 +23,9 @@
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
@@ -78,6 +83,9 @@
public static final Class<String> STRING = String.class;
public static final Class<Time> TIME = Time.class;
public static final Class<Timestamp> TIMESTAMP = Timestamp.class;
+ public static final Class<LocalDate> LOCAL_DATE = LocalDate.class;
+ public static final Class<LocalTime> LOCAL_TIME = LocalTime.class;
+ public static final Class<LocalDateTime> LOCAL_DATETIME = LocalDateTime.class;
public static final Class<UUID> UUID = UUID.class;
public static final Class<URL> URL_Class = URL.class;
public static final Class<java.util.Date> UTILDATE = java.util.Date.class;
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
index 4ddb7c1..35f2811 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
@@ -19,6 +19,8 @@
// - 522312: Add the eclipselink.sequencing.start-sequence-at-nextval property
// 02/20/2018-2.7 Will Dazey
// - 529602: Added support for CLOBs in DELETE statements for Oracle
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.internal.databaseaccess;
import java.io.IOException;
@@ -470,6 +472,9 @@
addOperator(ExpressionOperator.today());
addOperator(ExpressionOperator.currentDate());
addOperator(ExpressionOperator.currentTime());
+ addOperator(ExpressionOperator.localDateTime());
+ addOperator(ExpressionOperator.localTime());
+ addOperator(ExpressionOperator.localDate());
addOperator(ExpressionOperator.extract());
// Math
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java
index 59b5757..62cba10 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2021 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -18,32 +18,36 @@
// - 357533: Allow DDL queries to execute even when Multitenant entities are part of the PU
// 03/18/2015-2.6.0 Joe Grassel
// - 462498: Missing isolation level expression in SQL for Derby platform
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.platform.database;
-import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
-import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
-import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
-import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
-import org.eclipse.persistence.internal.sessions.AbstractSession;
-import org.eclipse.persistence.internal.helper.ClassConstants;
-import org.eclipse.persistence.internal.helper.DatabaseTable;
-import org.eclipse.persistence.internal.helper.Helper;
-import org.eclipse.persistence.exceptions.ValidationException;
-import org.eclipse.persistence.expressions.ExpressionOperator;
-import org.eclipse.persistence.queries.ValueReadQuery;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Vector;
-import java.io.Writer;
import java.io.IOException;
+import java.io.Writer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
-import java.util.Hashtable;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Hashtable;
import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.persistence.exceptions.ValidationException;
+import org.eclipse.persistence.expressions.Expression;
+import org.eclipse.persistence.expressions.ExpressionOperator;
+import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
+import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
+import org.eclipse.persistence.internal.expressions.ExpressionJavaPrinter;
+import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
+import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
+import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.internal.helper.DatabaseTable;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.queries.ValueReadQuery;
/**
* <p><b>Purpose</b>: Provides Derby DBMS specific behavior.
@@ -313,7 +317,7 @@
fieldTypeMapping.put(java.time.LocalDate.class, new FieldTypeDefinition("DATE"));
fieldTypeMapping.put(java.time.LocalDateTime.class, new FieldTypeDefinition("TIMESTAMP"));
- fieldTypeMapping.put(java.time.LocalTime.class, new FieldTypeDefinition("TIMESTAMP"));
+ fieldTypeMapping.put(java.time.LocalTime.class, new FieldTypeDefinition("TIME"));
fieldTypeMapping.put(java.time.OffsetDateTime.class, new FieldTypeDefinition("TIMESTAMP"));
fieldTypeMapping.put(java.time.OffsetTime.class, new FieldTypeDefinition("TIMESTAMP"));
@@ -340,6 +344,149 @@
// Derby does not support DECIMAL, but does have a DOUBLE function.
addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.ToNumber, "DOUBLE"));
addOperator(extractOperator());
+ addOperator(derbyPowerOperator());
+ addOperator(derbyRoundOperator());
+ }
+
+ // Emulate POWER(:a,:b) as EXP((:b)*LN(:a))
+ private static ExpressionOperator derbyPowerOperator() {
+ ExpressionOperator exOperator = new ExpressionOperator() {
+ @Override
+ public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
+ printer.printString(getDatabaseStrings()[0]);
+ if (second != null) {
+ second.printSQL(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[1]);
+ first.printSQL(printer);
+ printer.printString(getDatabaseStrings()[2]);
+ }
+ @Override
+ public void printCollection(List items, ExpressionSQLPrinter printer) {
+ if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
+ printer.getCall().setUsesBinding(false);
+ }
+ if (items.size() > 0) {
+ Expression firstItem = (Expression)items.get(0);
+ Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+ printDuo(firstItem, secondItem, printer);
+ } else {
+ throw new IllegalArgumentException("List of items shall contain at least one item");
+ }
+ }
+ @Override
+ public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
+ printer.printString(getDatabaseStrings()[0]);
+ if (second != null) {
+ second.printJava(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[1]);
+ first.printJava(printer);
+ printer.printString(getDatabaseStrings()[2]);
+ }
+ @Override
+ public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
+ if (items.size() > 0) {
+ Expression firstItem = (Expression)items.get(0);
+ Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+ printJavaDuo(firstItem, secondItem, printer);
+ } else {
+ throw new IllegalArgumentException("List of items shall contain at least one item");
+ }
+ }
+ };
+ exOperator.setType(ExpressionOperator.FunctionOperator);
+ exOperator.setSelector(ExpressionOperator.Power);
+ exOperator.setName("POWER");
+ List<String> v = new ArrayList<>(4);
+ v.add("EXP((");
+ v.add(")*LN(");
+ v.add("))");
+ exOperator.printsAs(v);
+ exOperator.bePrefix();
+ exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
+ return exOperator;
+ }
+
+ // Emulate ROUND as FLOOR((:x)*1e:n+0.5)/1e:n
+ private static ExpressionOperator derbyRoundOperator() {
+ ExpressionOperator exOperator = new ExpressionOperator() {
+ @Override
+ public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
+ printer.printString(getDatabaseStrings()[0]);
+ first.printSQL(printer);
+ printer.printString(getDatabaseStrings()[1]);
+ if (second != null) {
+ second.printSQL(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[2]);
+ if (second != null) {
+ second.printSQL(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[3]);
+ }
+ @Override
+ public void printCollection(List items, ExpressionSQLPrinter printer) {
+ if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
+ printer.getCall().setUsesBinding(false);
+ }
+ if (items.size() > 0) {
+ Expression firstItem = (Expression)items.get(0);
+ Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+ printDuo(firstItem, secondItem, printer);
+ } else {
+ throw new IllegalArgumentException("List of items shall contain at least one item");
+ }
+ }
+ @Override
+ public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
+ printer.printString(getDatabaseStrings()[0]);
+ first.printJava(printer);
+ printer.printString(getDatabaseStrings()[1]);
+ if (second != null) {
+ second.printJava(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[2]);
+ if (second != null) {
+ second.printJava(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[3]);
+ }
+ @Override
+ public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
+ if (items.size() > 0) {
+ Expression firstItem = (Expression)items.get(0);
+ Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+ printJavaDuo(firstItem, secondItem, printer);
+ } else {
+ throw new IllegalArgumentException("List of items shall contain at least one item");
+ }
+ }
+ };
+ exOperator.setType(ExpressionOperator.FunctionOperator);
+ exOperator.setSelector(ExpressionOperator.Round);
+ exOperator.setName("ROUND");
+ List<String> v = new ArrayList<>(4);
+ v.add("FLOOR((");
+ v.add(")*1e");
+ v.add("+0.5)/1e");
+ v.add("");
+ exOperator.printsAs(v);
+ exOperator.bePrefix();
+ exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
+ return exOperator;
}
/**
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java
index 80ea527..fb267bc 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -16,28 +16,43 @@
// Phillip Ross - LIMIT/OFFSET syntax support
// 09/14/2011-2.3.1 Guy Pelletier
// - 357533: Allow DDL queries to execute even when Multitenant entities are part of the PU
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.platform.database;
-import java.io.*;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
-import org.eclipse.persistence.internal.sessions.AbstractRecord;
-import org.eclipse.persistence.internal.sessions.AbstractSession;
-import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
-import org.eclipse.persistence.queries.StoredProcedureCall;
import org.eclipse.persistence.exceptions.ValidationException;
+import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionOperator;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
+import org.eclipse.persistence.internal.expressions.ExpressionJavaPrinter;
import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
import org.eclipse.persistence.internal.expressions.RelationExpression;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
-import org.eclipse.persistence.internal.helper.*;
+import org.eclipse.persistence.internal.helper.ClassConstants;
+import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.internal.helper.DatabaseTable;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.sessions.AbstractRecord;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
import org.eclipse.persistence.queries.SQLCall;
+import org.eclipse.persistence.queries.StoredProcedureCall;
import org.eclipse.persistence.queries.ValueReadQuery;
import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
@@ -130,6 +145,84 @@
addOperator(operatorLocate2());
addOperator(toNumberOperator());
addOperator(regexpOperator());
+ addOperator(pgsqlRoundOperator());
+ }
+
+ // Emulate ROUND(:x,:n) as FLOOR((:x)*10^(:n)+0.5)/10^(:n)
+ private static ExpressionOperator pgsqlRoundOperator() {
+ ExpressionOperator exOperator = new ExpressionOperator() {
+ @Override
+ public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
+ printer.printString(getDatabaseStrings()[0]);
+ first.printSQL(printer);
+ printer.printString(getDatabaseStrings()[1]);
+ if (second != null) {
+ second.printSQL(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[2]);
+ if (second != null) {
+ second.printSQL(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[3]);
+ }
+ @Override
+ public void printCollection(List items, ExpressionSQLPrinter printer) {
+ if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
+ printer.getCall().setUsesBinding(false);
+ }
+ if (items.size() > 0) {
+ Expression firstItem = (Expression)items.get(0);
+ Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+ printDuo(firstItem, secondItem, printer);
+ } else {
+ throw new IllegalArgumentException("List of items shall contain at least one item");
+ }
+ }
+ @Override
+ public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
+ printer.printString(getDatabaseStrings()[0]);
+ first.printJava(printer);
+ printer.printString(getDatabaseStrings()[1]);
+ if (second != null) {
+ second.printJava(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[2]);
+ if (second != null) {
+ second.printJava(printer);
+ } else {
+ printer.printString("0");
+ }
+ printer.printString(getDatabaseStrings()[3]);
+ }
+ @Override
+ public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
+ if (items.size() > 0) {
+ Expression firstItem = (Expression)items.get(0);
+ Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+ printJavaDuo(firstItem, secondItem, printer);
+ } else {
+ throw new IllegalArgumentException("List of items shall contain at least one item");
+ }
+ }
+ };
+ exOperator.setType(ExpressionOperator.FunctionOperator);
+ exOperator.setSelector(ExpressionOperator.Round);
+ exOperator.setName("ROUND");
+ List<String> v = new ArrayList<>(4);
+ v.add("FLOOR((");
+ v.add(")*10^(");
+ v.add(")+0.5)/10^(");
+ v.add(")");
+ exOperator.printsAs(v);
+ exOperator.bePrefix();
+ exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
+ return exOperator;
}
/**
diff --git a/jpa/eclipselink.jpa.test.jse/pom.xml b/jpa/eclipselink.jpa.test.jse/pom.xml
index 141223e..d09f403 100644
--- a/jpa/eclipselink.jpa.test.jse/pom.xml
+++ b/jpa/eclipselink.jpa.test.jse/pom.xml
@@ -42,6 +42,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <version>1.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<scope>test</scope>
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
index 965cce7..2070143 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -12,7 +12,7 @@
// Contributors:
// 02/01/2022: Tomas Kraus
-// - #N/A: Test extract() in CriteriaBuilder
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.jpa.test.criteria;
import java.math.RoundingMode;
@@ -31,6 +31,8 @@
import org.eclipse.persistence.jpa.test.framework.EmfRunner;
import org.eclipse.persistence.jpa.test.framework.Property;
import org.eclipse.persistence.sessions.Session;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
@@ -39,7 +41,7 @@
import org.junit.runner.RunWith;
/**
- * Test sign() method in CriteriaBuilder.
+ * Test math functions in CriteriaBuilder.
* Added to JPA-API as PR #351
*/
@RunWith(EmfRunner.class)
@@ -358,9 +360,10 @@
return em.createQuery(cq).getSingleResult();
}
- // Call POWER(n.longValue, 2) on long n>0.
+ // Call POWER(n.longValue, 2) on long n=0.
@Test
public void testPower2MethodWithZeroBase() {
+ // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callPower(em, 2, 0, "longValue");
@@ -372,28 +375,32 @@
// Call POWER(n.longValue, 2) on long n>0.
@Test
public void testPower2MethodWithPositiveLongBase() {
- Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callPower(em, 2, 3, "longValue");
Assert.assertEquals(
- Double.valueOf(Math.pow(NUMBER[3].getLongValue(), 2)), result);
+ Double.valueOf(Math.pow(NUMBER[3].getLongValue(), 2)), Double.valueOf(Math.round(result)));
}
}
// Call POWER(n.doubleValue, 2) on double n>0.
@Test
public void testPower2MethodWithPositiveDoubleBase() {
- Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callPower(em, 2, 3, "doubleValue");
- Assert.assertEquals(
- Double.valueOf(Math.pow(NUMBER[3].getDoubleValue(), 2)), result);
+ if (emf.unwrap(Session.class).getPlatform().isDerby()) {
+ MatcherAssert.assertThat(
+ Math.abs(Math.pow(NUMBER[3].getDoubleValue(), 2) - result), Matchers.lessThan(0.0000001d));
+ } else {
+ Assert.assertEquals(
+ Double.valueOf(Math.pow(NUMBER[3].getDoubleValue(), 2)), result);
+ }
}
}
// Call POWER(n.longValue, 2) on long n<0.
@Test
public void testPower2MethodWithNegativeLongBase() {
+ // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callPower(em, 2, 4, "longValue");
@@ -405,6 +412,7 @@
// Call POWER(n.doubleValue, 2) on double n<0.
@Test
public void testPower2MethodWithNegativeDoubleBase() {
+ // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callPower(em, 2, 4, "doubleValue");
@@ -416,6 +424,7 @@
// Call POWER(n.longValue, 3) on long n<0.
@Test
public void testPower3MethodWithNegativeLongBase() {
+ // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callPower(em, 3, 4, "longValue");
@@ -427,6 +436,7 @@
// Call POWER(n.doubleValue, 3) on double n<0.
@Test
public void testPower3MethodWithNegativeDoubleBase() {
+ // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callPower(em, 3, 4, "doubleValue");
@@ -450,11 +460,16 @@
// Call POWER(n.doubleValue, n.longValue) on id=7: [14.23D,4L] (result fits in double with no exponent).
@Test
public void testPowerMethodWithPositiveArgs() {
- Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callExprPower(em, 7);
- Assert.assertEquals(
- Double.valueOf(Math.pow(NUMBER[7].getDoubleValue(), NUMBER[7].getLongValue())), result);
+ if (emf.unwrap(Session.class).getPlatform().isDerby()) {
+ MatcherAssert.assertThat(
+ Math.abs(Math.pow(NUMBER[7].getDoubleValue(), NUMBER[7].getLongValue()) - result),
+ Matchers.lessThan(0.0000001d));
+ } else {
+ Assert.assertEquals(
+ Double.valueOf(Math.pow(NUMBER[7].getDoubleValue(), NUMBER[7].getLongValue())), result);
+ }
}
}
@@ -462,19 +477,22 @@
// using CriteriaQuery
// Matches Expression<Double> power(Expression<? extends Number> x, Number y) prototype
private static Double callRound(final EntityManager em, final int d, final int id) {
- CriteriaBuilder cb = em.getCriteriaBuilder();
- CriteriaQuery<Double> cq = cb.createQuery(Double.class);
- Root<NumberEntity> number = cq.from(NumberEntity.class);
- cq.select(cb.round(number.get("doubleValue"), d));
- cq.where(cb.equal(number.get("id"), id));
- return em.createQuery(cq).getSingleResult();
+ try {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+ Root<NumberEntity> number = cq.from(NumberEntity.class);
+ cq.select(cb.round(number.get("doubleValue"), d));
+ cq.where(cb.equal(number.get("id"), id));
+ return em.createQuery(cq).getSingleResult();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ }
}
// Call ROUND(n) on n>0.
@Test
public void testRoundMethodWithPositive() {
- Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
- Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isPostgreSQL());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callRound(em, 6,8);
Assert.assertEquals(Double.valueOf(44.754238D), result);
@@ -484,8 +502,6 @@
// Call ROUND(n) on n<0.
@Test
public void testRoundMethodWithNegative() {
- Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
- Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isPostgreSQL());
try (final EntityManager em = emf.createEntityManager()) {
Double result = callRound(em, 6, 9);
Assert.assertEquals(Double.valueOf(-214.245732D), result);
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestSimpleCase.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestSimpleCase.java
new file mode 100644
index 0000000..412d1e4
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestSimpleCase.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.criteria;
+
+import java.util.List;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.Root;
+
+import org.eclipse.persistence.jpa.test.criteria.model.NumberEntity;
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.framework.Property;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test new API 3.1.0 when methods of SimpleCase subclass of CriteriaBuilder.
+ * Added to JPA-API as PR #362
+ */
+@RunWith(EmfRunner.class)
+public class TestSimpleCase {
+
+ @Emf(createTables = DDLGen.DROP_CREATE,
+ classes = {
+ NumberEntity.class
+ },
+ properties = {
+ @Property(name = "eclipselink.cache.shared.default", value = "false"),
+ // This property remaps String from VARCHAR->NVARCHAR(or db equivalent)
+ @Property(name = "eclipselink.target-database-properties",
+ value = "UseNationalCharacterVaryingTypeForString=true"),
+ })
+ private EntityManagerFactory emf;
+
+ private final NumberEntity[] NUMBER = {
+ new NumberEntity(1, 0L, 0D),
+ new NumberEntity(2, 3L, 10D),
+ new NumberEntity(3, 3L, 100D),
+ new NumberEntity(4, 5L, 10D),
+ new NumberEntity(5, 5L, 100D),
+ new NumberEntity(6, 5L, 1000D)
+ };
+
+ @Before
+ public void setup() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ for (NumberEntity e : NUMBER) {
+ em.persist(e);
+ }
+ em.flush();
+ em.getTransaction().commit();
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ @After
+ public void cleanup() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ em.createQuery("DELETE FROM NumberEntity e").executeUpdate();
+ em.flush();
+ em.getTransaction().commit();
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test SimpleCase<C,R> when(Expression<? extends C>, R) prototype
+ // Strategy: Execute the query and check whether 3 rows where returned
+ @Test
+ public void testWhenExpressionValue() {
+ try (final EntityManager em = emf.createEntityManager()) {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<NumberEntity> cq = cb.createQuery(NumberEntity.class);
+ Root<NumberEntity> entity = cq.from(NumberEntity.class);
+ cq.select(entity);
+ Expression<Object> selectCase = cb.selectCase(entity.get("longValue"))
+ .when(cb.literal(3), 10)
+ .when(cb.literal(5), 100)
+ .otherwise(0);
+ cq.where(cb.equal(entity.get("doubleValue"), selectCase));
+ List<NumberEntity> result = em.createQuery(cq).getResultList();
+ MatcherAssert.assertThat(result.size(), Matchers.equalTo(3));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ }
+ }
+
+ // Test SimpleCase<C,R> when(Expression<? extends C>, Expression<? extends R>) prototype
+ // Strategy: Execute the query and check whether 3 rows where returned
+ @Test
+ public void testWhenExpressionExpression() {
+ try (final EntityManager em = emf.createEntityManager()) {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<NumberEntity> cq = cb.createQuery(NumberEntity.class);
+ Root<NumberEntity> entity = cq.from(NumberEntity.class);
+ cq.select(entity);
+ Expression<Object> selectCase = cb.selectCase(entity.get("longValue"))
+ .when(cb.literal(3), cb.literal(10))
+ .when(cb.literal(5), cb.literal(100))
+ .otherwise(0);
+ cq.where(cb.equal(entity.get("doubleValue"), selectCase));
+ List<NumberEntity> result = em.createQuery(cq).getResultList();
+ MatcherAssert.assertThat(result.size(), Matchers.equalTo(3));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/DateTimeEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/DateTimeEntity.java
new file mode 100644
index 0000000..79d3930
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/DateTimeEntity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.criteria.model;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+/**
+ * JPA Entity used in {@code LocalTime}/{@code LocalDate}/{@code LocalDateTime} tests.
+ */
+@Entity
+public class DateTimeEntity {
+
+ @Id
+ private Integer id;
+
+ private LocalTime time;
+
+ private LocalDate date;
+
+ private LocalDateTime datetime;
+
+ public DateTimeEntity() {
+ }
+
+ public DateTimeEntity(final Integer id, final LocalTime time, final LocalDate date, final LocalDateTime datetime) {
+ this.setId(id);
+ this.setTime(time);
+ this.setDate(date);
+ this.setDatetime(datetime);
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public LocalTime getTime() {
+ return time;
+ }
+
+ public void setTime(LocalTime time) {
+ this.time = time;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public void setDate(LocalDate date) {
+ this.date = date;
+ }
+
+ public LocalDateTime getDatetime() {
+ return datetime;
+ }
+
+ public void setDatetime(LocalDateTime datetime) {
+ this.datetime = datetime;
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
index 61a6c46..ffb02ef 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -12,12 +12,15 @@
// Contributors:
// 02/01/2022: Tomas Kraus
-// - #N/A: Test extract() in CriteriaBuilder
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.jpa.test.criteria.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
+/**
+ * JPA Entity used in {@code CriteriaBuilder} math functions tests.
+ */
@Entity
public class NumberEntity {
@@ -29,7 +32,6 @@
private Double doubleValue;
public NumberEntity() {
-
}
public NumberEntity(final Integer id, final Long longValue, final Double doubleValue) {
@@ -61,4 +63,5 @@
public void setDoubleValue(final Double doubleValue) {
this.doubleValue = doubleValue;
}
+
}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/TestDateTimeFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/TestDateTimeFunctions.java
new file mode 100644
index 0000000..85d7fc0
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/TestDateTimeFunctions.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.criteria.model;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.List;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.CriteriaUpdate;
+import jakarta.persistence.criteria.Root;
+
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.framework.Property;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@code LocalTime}/{@code LocalDate}/{@code LocalDateTime} functions in CriteriaBuilder.
+ * Added to JPA-API as PR #352
+ */
+@RunWith(EmfRunner.class)
+public class TestDateTimeFunctions {
+
+ @Emf(createTables = DDLGen.DROP_CREATE,
+ classes = {
+ DateTimeEntity.class
+ },
+ properties = {
+ @Property(name = "eclipselink.cache.shared.default", value = "false"),
+ // This property remaps String from VARCHAR->NVARCHAR(or db equivalent)
+ @Property(name = "eclipselink.target-database-properties",
+ value = "UseNationalCharacterVaryingTypeForString=true"),
+ })
+ private EntityManagerFactory emf;
+
+ private final LocalDateTime[] TS = {
+ LocalDateTime.of(1970, 1, 1, 1, 11, 11),
+ LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0)
+ };
+
+ private final DateTimeEntity[] ENTITY = {
+ new DateTimeEntity(1, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalTime
+ new DateTimeEntity(2, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalDate
+ new DateTimeEntity(3, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalDateTime
+ new DateTimeEntity(4, TS[1].toLocalTime(), TS[1].toLocalDate(), TS[1]) // reserved for testCriteriaQueryWhereLocalTime
+ // testCriteriaQueryWhereLocalTimeReturnsEmpty
+ // testCriteriaQueryWhereLocalDate
+ // testCriteriaQueryWhereLocalDateReturnsEmpty
+ // testCriteriaQueryWhereLocalDateTime
+ // testCriteriaQueryWhereLocalDateTimeReturnsEmpty
+ // testCriteriaQuerySelectLocalTime
+ // testCriteriaQuerySelectLocalDate
+ // testCriteriaQuerySelectLocalDateTime
+ };
+
+ @Before
+ public void setup() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ for (DateTimeEntity e : ENTITY) {
+ em.persist(e);
+ }
+ em.flush();
+ em.getTransaction().commit();
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ @After
+ public void cleanup() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ em.createQuery("DELETE FROM DateTimeEntity e").executeUpdate();
+ em.flush();
+ em.getTransaction().commit();
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaUpdate of localTime.
+ // Strategy: Update 1:11:11 to current LocalTime and verify that time in database
+ // differs in less than 30 seconds from current time.
+ // May fail when database time is not set properly.
+ @Test
+ public void testCriteriaUpdateLocalTime() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ // Run the update query: Set current time
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
+ cu.set("time", cb.localTime());
+ cu.where(cb.equal(entity.get("id"), 1));
+ em.createQuery(cu).executeUpdate();
+ em.flush();
+ em.getTransaction().commit();
+ // Verify updated entity
+ DateTimeEntity data = em.find(DateTimeEntity.class, 1);
+ long diffMilis = Duration.between(data.getTime(), LocalTime.now()).toMillis();
+ // Positive value means that test did not pass midnight.
+ if (diffMilis > 0) {
+ MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+ // Midnight pass correction.
+ } else {
+ MatcherAssert.assertThat(86400000L + diffMilis, Matchers.lessThan(30000L));
+ }
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaUpdate of localDate.
+ // Strategy: Update 1. 1. 1970 to current LocalDate and verify that time in database
+ // differs in less than two days from current date. One day difference
+ // may be caused by midnight being just passed.
+ // May fail when database time is not set properly.
+ @Test
+ public void testCriteriaUpdateLocalDate() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ // Run the update query: Set current time
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
+ cu.set("date", cb.localDate());
+ cu.where(cb.equal(entity.get("id"), 2));
+ em.createQuery(cu).executeUpdate();
+ em.flush();
+ em.getTransaction().commit();
+ // Verify updated entity
+ DateTimeEntity data = em.find(DateTimeEntity.class, 2);
+ Period diff = Period.between(data.getDate(), LocalDate.now());
+ MatcherAssert.assertThat(diff.getYears(), Matchers.equalTo(0));
+ MatcherAssert.assertThat(diff.getMonths(), Matchers.equalTo(0));
+ MatcherAssert.assertThat(diff.getDays(), Matchers.lessThan(2));
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaUpdate of localDateTime.
+ // Strategy: Update 1. 1. 1970 1:11:11 to current LocalDateTime and verify that timestamp in database
+ // differs in less than 30 seconds from current timestamp.
+ // May fail when database time is not set properly.
+ @Test
+ public void testCriteriaUpdateLocalDateTime() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ // Run the update query: Set current time
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
+ cu.set("datetime", cb.localDateTime());
+ cu.where(cb.equal(entity.get("id"), 3));
+ em.createQuery(cu).executeUpdate();
+ em.flush();
+ em.getTransaction().commit();
+ // Verify updated entity
+ DateTimeEntity data = em.find(DateTimeEntity.class, 3);
+ long diffMilis = Duration.between(data.getDatetime(), LocalDateTime.now()).toMillis();
+ MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localTime in WHERE condition.
+ // Strategy: SELECT DateTimeEntity WHERE time column < localTime AND id = :id
+ // LocalTime in entity is set to 0:00:00.0.
+ @Test
+ public void testCriteriaQueryWhereLocalTime() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(entity);
+ cq.where(cb.and(cb.lessThan(entity.get("time"), cb.localTime()), cb.equal(entity.get("id"), 4)));
+ em.createQuery(cq).getSingleResult();
+ em.getTransaction().commit();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localTime in WHERE condition with no record matching it.
+ // Strategy: SELECT DateTimeEntity WHERE time column > localTime AND id = :id
+ // localTime in entity is set to 0:00:00.0
+ // Such a record does not exist so empty result is expected.
+ @Test
+ public void testCriteriaQueryWhereLocalTimeReturnsEmpty() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(entity);
+ cq.where(cb.and(cb.greaterThan(entity.get("time"), cb.localTime()), cb.equal(entity.get("id"), 4)));
+ List<DateTimeEntity> data = em.createQuery(cq).getResultList();
+ em.getTransaction().commit();
+ MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localDate in WHERE condition.
+ // Strategy: SELECT DateTimeEntity WHERE time column < localTime AND id = :id
+ // LocalDate in entity is set to 1.1.1970.
+ @Test
+ public void testCriteriaQueryWhereLocalDate() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(entity);
+ cq.where(cb.and(cb.lessThan(entity.get("date"), cb.localDate()), cb.equal(entity.get("id"), 4)));
+ em.createQuery(cq).getSingleResult();
+ em.getTransaction().commit();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localDate in WHERE condition with no record matching it.
+ // Strategy: SELECT DateTimeEntity WHERE time column > localTime AND id = :id
+ // LocalDate in entity is set to 1.1.1970.
+ // Such a record does not exist so empty result is expected
+ @Test
+ public void testCriteriaQueryWhereLocalDateReturnsEmpty() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(entity);
+ cq.where(cb.and(cb.greaterThan(entity.get("date"), cb.localDate()), cb.equal(entity.get("id"), 4)));
+ List<DateTimeEntity> data = em.createQuery(cq).getResultList();
+ em.getTransaction().commit();
+ MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localDateTime in WHERE condition.
+ // Strategy: SELECT DateTimeEntity WHERE time column < localDateTime AND id = :id
+ // LocalDate in entity is set to 1.1.1970 0:00:00.
+ @Test
+ public void testCriteriaQueryWhereLocalDateTime() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(entity);
+ cq.where(cb.and(cb.lessThan(entity.get("datetime"), cb.localDateTime()), cb.equal(entity.get("id"), 4)));
+ em.createQuery(cq).getSingleResult();
+ em.getTransaction().commit();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localDateTime in WHERE condition with no record matching it.
+ // Strategy: SELECT DateTimeEntity WHERE time column > localDateTime AND id = :id
+ // LocalDate in entity is set to 1.1.1970 0:00:00.
+ // Such a record does not exist so empty result is expected
+ @Test
+ public void testCriteriaQueryWhereLocalDateTimeReturnsEmpty() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(entity);
+ cq.where(cb.and(cb.greaterThan(entity.get("datetime"), cb.localDateTime()), cb.equal(entity.get("id"), 4)));
+ List<DateTimeEntity> data = em.createQuery(cq).getResultList();
+ em.getTransaction().commit();
+ MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localTime in SELECT column list
+ @Test
+ public void testCriteriaQuerySelectLocalTime() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<LocalTime> cq = cb.createQuery(LocalTime.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(cb.localTime());
+ cq.where(cb.equal(entity.get("id"), 4));
+ LocalTime time = em.createQuery(cq).getSingleResult();
+ em.getTransaction().commit();
+ long diffMilis = Duration.between(time, LocalTime.now()).toMillis();
+ // Positive value means that test did not pass midnight.
+ if (diffMilis > 0) {
+ MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+ // Midnight pass correction.
+ } else {
+ MatcherAssert.assertThat(86400000L + diffMilis, Matchers.lessThan(30000L));
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localDate in SELECT column list
+ @Test
+ public void testCriteriaQuerySelectLocalDate() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<LocalDate> cq = cb.createQuery(LocalDate.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(cb.localDate());
+ cq.where(cb.equal(entity.get("id"), 4));
+ LocalDate date = em.createQuery(cq).getSingleResult();
+ em.getTransaction().commit();
+ Period diff = Period.between(date, LocalDate.now());
+ MatcherAssert.assertThat(diff.getYears(), Matchers.equalTo(0));
+ MatcherAssert.assertThat(diff.getMonths(), Matchers.equalTo(0));
+ MatcherAssert.assertThat(diff.getDays(), Matchers.lessThan(2));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+ // Test CriteriaQuery with localDateTime in SELECT column list
+ @Test
+ public void testCriteriaQuerySelectLocalDateTime() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ em.getTransaction().begin();
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery<LocalDateTime> cq = cb.createQuery(LocalDateTime.class);
+ Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+ cq.select(cb.localDateTime());
+ cq.where(cb.equal(entity.get("id"), 4));
+ LocalDateTime datetime = em.createQuery(cq).getSingleResult();
+ em.getTransaction().commit();
+ long diffMilis = Duration.between(datetime, LocalDateTime.now()).toMillis();
+ MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ } finally {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ em.close();
+ }
+ }
+
+}
diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
index 03dec36..9568729 100644
--- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
+++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
@@ -13,15 +13,13 @@
// Contributors:
// Gordon Yorke - Initial development
-//
+// 02/01/2022: Tomas Kraus
+// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.internal.jpa.querydef;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -2315,36 +2313,33 @@
}
/**
- * Create expression to return current local date.
+ * Create expression to return local datetime.
*
- * @return expression for current date
- */
- @Override
- public Expression<java.time.LocalDate> localDate() {
- // TODO: Implement
- throw new UnsupportedOperationException("Local date expression is not implemented yet");
- }
-
- /**
- * Create expression to return current local datetime.
- *
- * @return expression for current timestamp
+ * @return expression for local timestamp
*/
@Override
public Expression<java.time.LocalDateTime> localDateTime() {
- // TODO: Implement
- throw new UnsupportedOperationException("Local datetime expression is not implemented yet");
+ return new ExpressionImpl(metamodel, ClassConstants.LOCAL_DATETIME, new ExpressionBuilder().localDateTime());
}
/**
- * Create expression to return current local time.
+ * Create expression to return local date.
*
- * @return expression for current time
+ * @return expression for local date
+ */
+ @Override
+ public Expression<java.time.LocalDate> localDate() {
+ return new ExpressionImpl(metamodel, ClassConstants.LOCAL_DATE, new ExpressionBuilder().localDate());
+ }
+
+ /**
+ * Create expression to return local time.
+ *
+ * @return expression for local time
*/
@Override
public Expression<java.time.LocalTime> localTime() {
- // TODO: Implement
- throw new UnsupportedOperationException("Local time expression is not implemented yet");
+ return new ExpressionImpl(metamodel, ClassConstants.LOCAL_TIME, new ExpressionBuilder().localTime());
}
/**
@@ -2765,12 +2760,12 @@
*/
@Override
public SimpleCase<C, R> when(Expression<? extends C> condition, R result) {
- org.eclipse.persistence.expressions.Expression conditionExp = org.eclipse.persistence.expressions.Expression.from(condition, new ExpressionBuilder());
+ org.eclipse.persistence.expressions.Expression conditionExp = ((InternalSelection)condition).getCurrentNode();
+ conditionExp = org.eclipse.persistence.expressions.Expression.from(conditionExp, currentNode);
((FunctionExpression)currentNode).addChild(conditionExp);
this.expressions.add(condition);
- org.eclipse.persistence.expressions.Expression resultExp = ((InternalSelection)result).getCurrentNode();
- resultExp = org.eclipse.persistence.expressions.Expression.from(resultExp, currentNode);
+ org.eclipse.persistence.expressions.Expression resultExp = org.eclipse.persistence.expressions.Expression.from(result, new ExpressionBuilder());
((FunctionExpression)currentNode).addChild(resultExp);
Expression<R> resultLiteral = internalLiteral(result);
this.expressions.add(resultLiteral);
@@ -2787,7 +2782,8 @@
*/
@Override
public SimpleCase<C, R> when(Expression<? extends C> condition, Expression<? extends R> result) {
- org.eclipse.persistence.expressions.Expression conditionExp = org.eclipse.persistence.expressions.Expression.from(condition, new ExpressionBuilder());
+ org.eclipse.persistence.expressions.Expression conditionExp = ((InternalSelection)condition).getCurrentNode();
+ conditionExp = org.eclipse.persistence.expressions.Expression.from(conditionExp, currentNode);
((FunctionExpression)currentNode).addChild(conditionExp);
this.expressions.add(condition);