Skip to content

Commit

Permalink
JPAQueryBuilder support in Project. (#2212)
Browse files Browse the repository at this point in the history
Signed-off-by: Tomáš Kraus <[email protected]>
  • Loading branch information
Tomas-Kraus committed Jul 17, 2024
1 parent 76e9e04 commit 18b2d9b
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 6 deletions.
2 changes: 2 additions & 0 deletions foundation/org.eclipse.persistence.core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@
<arg>org.eclipse.persistence.core/org.eclipse.persistence.internal.oxm.conversion=org.eclipse.persistence.core.test</arg>
<arg>--add-exports</arg>
<arg>org.eclipse.persistence.core/org.eclipse.persistence.internal.oxm.schema.model=org.eclipse.persistence.core.test</arg>
<arg>--add-exports</arg>
<arg>org.eclipse.persistence.core/org.eclipse.persistence.internal.jpa.jpql=org.eclipse.persistence.core.test</arg>
</compilerArgs>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,21 @@
// - 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;
import org.eclipse.persistence.descriptors.partitioning.PartitioningPolicy;
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;
Expand All @@ -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;

/**
* <b>Purpose</b>: Maintain all of the EclipseLink configuration information for a system.
Expand Down Expand Up @@ -203,6 +207,9 @@ public class Project extends CoreProject<ClassDescriptor, Login, DatabaseSession
/** Force all queries and relationships to use deferred lock strategy during object building and L2 cache population. */
protected boolean queryCacheForceDeferredLocks = false;

/** {@link JPAQueryBuilder} instance factory. */
private Supplier<? extends JPAQueryBuilder> queryBuilderSupplier;

/**
* PUBLIC:
* Create a new project.
Expand All @@ -220,6 +227,7 @@ public Project() {
this.mappedSuperclassDescriptors = new HashMap<>(2);
this.metamodelIdClassMap = new HashMap<>();
this.attributeGroups = new HashMap<>();
this.queryBuilderSupplier = new DefaultQueryBuilderSupplier<>();
}

/**
Expand Down Expand Up @@ -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<? extends JPAQueryBuilder> 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 extends JPAQueryBuilder> 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<T extends JPAQueryBuilder> implements Supplier<T> {

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<T> parserClass = (Class<T>) Class.forName(DEFAULT_BUILDER_CLASS_NAME);
return parserClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("missing_jpql_parser_class"), e);
}
}

}

}
Original file line number Diff line number Diff line change
@@ -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()));
}

}
Original file line number Diff line number Diff line change
@@ -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()));
}

}
1 change: 0 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@
<h2.version>2.2.224</h2.version>
<!-- CQ #21134, 21135, 21136, 21137 -->
<exam.version>4.13.4</exam.version>
<hamcrest.version>1.3</hamcrest.version>
<!-- CQ #23048 -->
<hibernate.version>8.0.1.Final</hibernate.version>
<checkstyle.version>10.15.0</checkstyle.version>
Expand Down

0 comments on commit 18b2d9b

Please sign in to comment.