From 18b2d9bee63a60bcf45bf8ce6011ccd741c6eff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kraus?= Date: Wed, 17 Jul 2024 17:26:46 +0200 Subject: [PATCH] JPAQueryBuilder support in Project. (#2212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomáš Kraus --- .../org.eclipse.persistence.core/pom.xml | 2 + .../i18n/ExceptionLocalizationResource.java | 3 +- .../internal/sessions/AbstractSession.java | 11 ++- .../eclipse/persistence/sessions/Project.java | 54 +++++++++++- .../testing/tests/junit/ProjectTest.java | 69 +++++++++++++++ .../testing/tests/junit/SessionTest.java | 83 +++++++++++++++++++ pom.xml | 1 - 7 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/ProjectTest.java create mode 100644 foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/SessionTest.java diff --git a/foundation/org.eclipse.persistence.core/pom.xml b/foundation/org.eclipse.persistence.core/pom.xml index 07b4e97df01..b5ef168ef02 100644 --- a/foundation/org.eclipse.persistence.core/pom.xml +++ b/foundation/org.eclipse.persistence.core/pom.xml @@ -214,6 +214,8 @@ org.eclipse.persistence.core/org.eclipse.persistence.internal.oxm.conversion=org.eclipse.persistence.core.test --add-exports org.eclipse.persistence.core/org.eclipse.persistence.internal.oxm.schema.model=org.eclipse.persistence.core.test + --add-exports + org.eclipse.persistence.core/org.eclipse.persistence.internal.jpa.jpql=org.eclipse.persistence.core.test diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java index 98baa7fcfec..4a8d3becb5f 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java @@ -284,7 +284,8 @@ public class ExceptionLocalizationResource extends ListResourceBundle { { "find_option_class_unknown", "The FindOption implementing the {0} class is not supported"}, { "refresh_option_class_unknown", "The RefreshOption implementing class {0} is not supported"}, { "lock_option_class_unknown", "The LockOption implementing class {0} is not supported"}, - { "typed_query_reference_is_null", "Reference to a named query is null"} + { "typed_query_reference_is_null", "Reference to a named query is null"}, + { "missing_jpql_parser_class", "Could not load the JPQL parser class."} }; /** * Return the lookup table. diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/sessions/AbstractSession.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/sessions/AbstractSession.java index 54905d2ee3f..1296727bd42 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/sessions/AbstractSession.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/sessions/AbstractSession.java @@ -430,7 +430,14 @@ public JPAQueryBuilder getQueryBuilder() { if (parent != null) { this.queryBuilder = parent.getQueryBuilder(); } else { - this.queryBuilder = buildDefaultQueryBuilder(); + // Project may not be set + Project project = getProject(); + if (project != null) { + this.queryBuilder = project.getQueryBuilder(); + } + if (this.queryBuilder == null) { + this.queryBuilder = buildDefaultQueryBuilder(); + } } } return this.queryBuilder; @@ -468,7 +475,7 @@ protected JPAQueryBuilder buildDefaultQueryBuilder() { builder = PrivilegedAccessHelper.newInstanceFromClass(parserClass); } } catch (Exception e) { - throw new IllegalStateException("Could not load the JPQL parser class." /* TODO: Localize string */, e); + throw new IllegalStateException(ExceptionLocalization.buildMessage("missing_jpql_parser_class"), e); } if (validation != null) { builder.setValidationLevel(validation); diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/Project.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/Project.java index 25367396a31..5c5084ff149 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/Project.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/Project.java @@ -40,8 +40,8 @@ // - 533148 : Add the eclipselink.jpa.sql-call-deferral property package org.eclipse.persistence.sessions; -import org.eclipse.persistence.annotations.IdValidation; import org.eclipse.persistence.annotations.CacheIsolationType; +import org.eclipse.persistence.annotations.IdValidation; import org.eclipse.persistence.core.sessions.CoreProject; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.descriptors.MultitenantPolicy; @@ -49,10 +49,12 @@ import org.eclipse.persistence.internal.helper.ConcurrentFixedCache; import org.eclipse.persistence.internal.identitymaps.AbstractIdentityMap; import org.eclipse.persistence.internal.identitymaps.IdentityMap; +import org.eclipse.persistence.internal.localization.ExceptionLocalization; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl; import org.eclipse.persistence.queries.AttributeGroup; import org.eclipse.persistence.queries.DatabaseQuery; +import org.eclipse.persistence.queries.JPAQueryBuilder; import org.eclipse.persistence.queries.QueryResultsCachePolicy; import org.eclipse.persistence.queries.SQLResultSetMapping; import org.eclipse.persistence.sessions.server.ConnectionPolicy; @@ -66,6 +68,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; /** * Purpose: Maintain all of the EclipseLink configuration information for a system. @@ -203,6 +207,9 @@ public class Project extends CoreProject queryBuilderSupplier; + /** * PUBLIC: * Create a new project. @@ -220,6 +227,7 @@ public Project() { this.mappedSuperclassDescriptors = new HashMap<>(2); this.metamodelIdClassMap = new HashMap<>(); this.attributeGroups = new HashMap<>(); + this.queryBuilderSupplier = new DefaultQueryBuilderSupplier<>(); } /** @@ -1621,5 +1629,47 @@ public PartitioningPolicy getPartitioningPolicy(String name) { } return this.partitioningPolicies.get(name); } -} + /** + * Set new {@link JPAQueryBuilder} instance factory. + * + * @param queryBuilderSupplier the new {@link JPAQueryBuilder} instance factory + */ + public void setQueryBuilderSupplier(Supplier queryBuilderSupplier) { + Objects.requireNonNull(queryBuilderSupplier, "Value of queryBuilderSupplier is null"); + this.queryBuilderSupplier = queryBuilderSupplier; + } + + /** + * Create new instance of {@link JPAQueryBuilder}. + * + * @return the JPA query builder + */ + @SuppressWarnings("unchecked") + public T getQueryBuilder() { + return (T) queryBuilderSupplier.get(); + } + + // Default JPAQueryBuilder factory. + // Returns new instance of HermesParser. Based on buildDefaultQueryBuilder() method of AbstractSession. + private static final class DefaultQueryBuilderSupplier implements Supplier { + + private static final String DEFAULT_BUILDER_CLASS_NAME = "org.eclipse.persistence.internal.jpa.jpql.HermesParser"; + + private DefaultQueryBuilderSupplier() { + } + + @Override + public T get() { + try { + @SuppressWarnings({"unchecked"}) + Class parserClass = (Class) Class.forName(DEFAULT_BUILDER_CLASS_NAME); + return parserClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalStateException(ExceptionLocalization.buildMessage("missing_jpql_parser_class"), e); + } + } + + } + +} diff --git a/foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/ProjectTest.java b/foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/ProjectTest.java new file mode 100644 index 00000000000..f0e75773940 --- /dev/null +++ b/foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/ProjectTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 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: +// 07/17/2024-5.0 Tomas Kraus +package org.eclipse.persistence.testing.tests.junit; + +import org.eclipse.persistence.expressions.Expression; +import org.eclipse.persistence.internal.jpa.jpql.HermesParser; +import org.eclipse.persistence.internal.sessions.AbstractSession; +import org.eclipse.persistence.queries.DatabaseQuery; +import org.eclipse.persistence.queries.JPAQueryBuilder; +import org.eclipse.persistence.sessions.Project; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class ProjectTest { + + // Verify that default JPAQueryBuilder returned by Project instance is HermesParser + @Test + public void testDefaultQueryBuilder() { + Project project = new Project(); + JPAQueryBuilder builder = project.getQueryBuilder(); + assertTrue("builder is not an instance of HermesParser class", + HermesParser.class.isAssignableFrom(builder.getClass())); + } + + // Verify that JPAQueryBuilder returned by Project instance matches CustomQueryBuilder + // after CustomQueryBuilder::new is set as JPAQueryBuilder instance factory + @Test + public void testCustomQueryBuilder() { + + class CustomQueryBuilder implements JPAQueryBuilder { + @Override + public void setValidationLevel(String level) { + throw new UnsupportedOperationException(); + } + @Override + public DatabaseQuery buildQuery(CharSequence jpqlQuery, AbstractSession session) { + throw new UnsupportedOperationException(); + } + @Override + public Expression buildSelectionCriteria(String entityName, String selectionCriteria, AbstractSession session) { + throw new UnsupportedOperationException(); + } + @Override + public void populateQuery(CharSequence jpqlQuery, DatabaseQuery query, AbstractSession session) { + throw new UnsupportedOperationException(); + } + } + + Project project = new Project(); + project.setQueryBuilderSupplier(CustomQueryBuilder::new); + JPAQueryBuilder builder = project.getQueryBuilder(); + assertTrue("builder is not an instance of CustomQueryBuilder class", + CustomQueryBuilder.class.isAssignableFrom(builder.getClass())); + } + +} diff --git a/foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/SessionTest.java b/foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/SessionTest.java new file mode 100644 index 00000000000..9f98a9770c2 --- /dev/null +++ b/foundation/org.eclipse.persistence.core/src/test/java/org/eclipse/persistence/testing/tests/junit/SessionTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024 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: +// 07/17/2024-5.0 Tomas Kraus + +package org.eclipse.persistence.testing.tests.junit; + +import org.eclipse.persistence.expressions.Expression; +import org.eclipse.persistence.internal.jpa.jpql.HermesParser; +import org.eclipse.persistence.internal.sessions.AbstractSession; +import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl; +import org.eclipse.persistence.queries.DatabaseQuery; +import org.eclipse.persistence.queries.JPAQueryBuilder; +import org.eclipse.persistence.sessions.Project; +import org.junit.Test; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class SessionTest { + + // AbstractSession test. Using DatabaseSessionImpl which does not override tested code. + // Verify that default JPAQueryBuilder returned by session Project instance is HermesParser + @Test + public void testDefaultProjectQueryBuilder() { + AbstractSession session = new DatabaseSessionImpl(); + // Session parent may affect returned JPAQueryBuilder so make sure no one exists + assertNull(session.getParent()); + // Set Project used to initialize JPAQueryBuilder instance + session.setProject(new Project()); + JPAQueryBuilder builder = session.getQueryBuilder(); + assertTrue("builder is not an instance of HermesParser class", + HermesParser.class.isAssignableFrom(builder.getClass())); + } + + // AbstractSession test. Using DatabaseSessionImpl which does not override tested code. + // Verify that JPAQueryBuilder returned by session Project instance matches CustomQueryBuilder + // after CustomQueryBuilder::new is set as JPAQueryBuilder instance factory + @Test + public void testCustomProjectQueryBuilder() { + + class CustomQueryBuilder implements JPAQueryBuilder { + @Override + public void setValidationLevel(String level) { + throw new UnsupportedOperationException(); + } + @Override + public DatabaseQuery buildQuery(CharSequence jpqlQuery, AbstractSession session) { + throw new UnsupportedOperationException(); + } + @Override + public Expression buildSelectionCriteria(String entityName, String selectionCriteria, AbstractSession session) { + throw new UnsupportedOperationException(); + } + @Override + public void populateQuery(CharSequence jpqlQuery, DatabaseQuery query, AbstractSession session) { + throw new UnsupportedOperationException(); + } + } + + Project project = new Project(); + project.setQueryBuilderSupplier(CustomQueryBuilder::new); + AbstractSession session = new DatabaseSessionImpl(); + // Session parent may affect returned JPAQueryBuilder so make sure no one exists + assertNull(session.getParent()); + // Set Project used to initialize JPAQueryBuilder instance + session.setProject(project); + JPAQueryBuilder builder = session.getQueryBuilder(); + assertTrue("builder is not an instance of CustomQueryBuilder class", + CustomQueryBuilder.class.isAssignableFrom(builder.getClass())); + } + +} diff --git a/pom.xml b/pom.xml index eb44460fcfb..49895dfc912 100644 --- a/pom.xml +++ b/pom.xml @@ -234,7 +234,6 @@ 2.2.224 4.13.4 - 1.3 8.0.1.Final 10.15.0