Bug 570702: Add support for Embeddable fields as JOIN targets
Signed-off-by: Will Dazey <dazeydev.3@gmail.com>
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/EclipseLinkSemanticValidatorHelper.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/EclipseLinkSemanticValidatorHelper.java
index 0778235..ede18b8 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/EclipseLinkSemanticValidatorHelper.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/EclipseLinkSemanticValidatorHelper.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -378,6 +379,11 @@
}
}
+ @Override
+ public boolean isEmbeddableMapping(Object mapping) {
+ return (mapping instanceof org.eclipse.persistence.mappings.EmbeddableMapping);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestCollectionTableEmbeddable.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestCollectionTableEmbeddable.java
index 1d2bf6f..f6ea116 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestCollectionTableEmbeddable.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestCollectionTableEmbeddable.java
@@ -13,17 +13,25 @@
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.Query;
import org.eclipse.persistence.jpa.embeddable.model.ElementCollectionEmbeddableTemporal;
import org.eclipse.persistence.jpa.embeddable.model.ElementCollectionEntity;
+import org.eclipse.persistence.jpa.embeddable.model.SpecAddress;
+import org.eclipse.persistence.jpa.embeddable.model.SpecContactInfo;
+import org.eclipse.persistence.jpa.embeddable.model.SpecEmployee;
+import org.eclipse.persistence.jpa.embeddable.model.SpecPhone;
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.eclipse.persistence.jpa.test.framework.SQLListener;
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,6 +43,13 @@
@Property(name = "eclipselink.cache.shared.default", value = "false")})
private EntityManagerFactory emf;
+ @Emf(name = "SpecPersistenceUnit", createTables = DDLGen.DROP_CREATE,
+ classes = { SpecAddress.class, SpecContactInfo.class, SpecEmployee.class, SpecPhone.class })
+ private EntityManagerFactory emf2;
+
+ @SQLListener(name = "SpecPersistenceUnit")
+ List<String> _sql;
+
@Test
public void mergeTest() {
if (emf == null)
@@ -64,4 +79,23 @@
em.close();
}
}
+
+ @Test
+ public void JPQLAggregateCollectionTests() {
+ EntityManager em = emf2.createEntityManager();
+ try {
+ /*
+ * ContactInfo.previousAddresses should end up being an AggregateCollectionMapping
+ */
+ Query queryEmbed = em.createQuery("SELECT p.city FROM SpecEmployee e JOIN e.contactInfo.previousAddresses p WHERE e.contactInfo.primaryAddress.zipcode = ?1");
+ queryEmbed.setParameter(1, "95054");
+ queryEmbed.getResultList();
+ Assert.assertEquals(1, _sql.size());
+ Assert.assertEquals("SELECT t0.CITY FROM PREV_ADDRESSES t0, SPECEMPLOYEE t1 WHERE ((t1.ZIPCODE = ?) AND (t0.SpecEmployee_ID = t1.ID))", _sql.remove(0));
+ } finally {
+ if (em.isOpen()) {
+ em.close();
+ }
+ }
+ }
}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestEmbeddable.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestEmbeddable.java
new file mode 100644
index 0000000..2d8fc5b
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestEmbeddable.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021 IBM Corporation. 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:
+// 04/09/2021 - Will Dazey
+// - 570702 : Using embeddable fields in query JOINs
+package org.eclipse.persistence.jpa.embeddable;
+
+import java.util.List;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.Query;
+
+import org.eclipse.persistence.jpa.embeddable.model.SpecAddress;
+import org.eclipse.persistence.jpa.embeddable.model.SpecContactInfo;
+import org.eclipse.persistence.jpa.embeddable.model.SpecEmployee;
+import org.eclipse.persistence.jpa.embeddable.model.SpecPhone;
+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.SQLListener;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(EmfRunner.class)
+public class TestEmbeddable {
+
+ @Emf(name = "SpecPersistenceUnit", createTables = DDLGen.DROP_CREATE,
+ classes = { SpecAddress.class, SpecContactInfo.class, SpecEmployee.class, SpecPhone.class })
+ private EntityManagerFactory emf;
+
+ @SQLListener(name = "SpecPersistenceUnit")
+ List<String> _sql;
+
+ @Test
+ public void JPQLTests() {
+ EntityManager em = emf.createEntityManager();
+ try {
+ // According to the JPA Spec, section 4.4.4, the following two queries are equivalent
+ Query queryEmbed = em.createQuery("SELECT p.vendor FROM SpecEmployee e JOIN e.contactInfo.phones p WHERE e.contactInfo.primaryAddress.zipcode = ?1");
+ queryEmbed.setParameter(1, "95051");
+ queryEmbed.getResultList();
+ Assert.assertEquals(1, _sql.size());
+ Assert.assertEquals("SELECT t0.VENDOR FROM SPECPHONE t0, SPECEMPLOYEE_SPECPHONE t2, SPECEMPLOYEE t1 WHERE ((t1.ZIPCODE = ?) AND ((t2.SpecEmployee_ID = t1.ID) AND (t0.ID = t2.phones_ID)))", _sql.remove(0));
+
+ queryEmbed = em.createQuery("SELECT p.vendor FROM SpecEmployee e JOIN e.contactInfo c JOIN c.phones p WHERE e.contactInfo.primaryAddress.zipcode = ?1");
+ queryEmbed.setParameter(1, "95052");
+ queryEmbed.getResultList();
+ Assert.assertEquals(1, _sql.size());
+ Assert.assertEquals("SELECT t0.VENDOR FROM SPECPHONE t0, SPECEMPLOYEE_SPECPHONE t2, SPECEMPLOYEE t1 WHERE ((t1.ZIPCODE = ?) AND ((t2.SpecEmployee_ID = t1.ID) AND (t0.ID = t2.phones_ID)))", _sql.remove(0));
+ } finally {
+ if (em.isOpen()) {
+ em.close();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestNestedEmbeddable.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestNestedEmbeddable.java
index dd01243..102c40f 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestNestedEmbeddable.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/TestNestedEmbeddable.java
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2016 IBM Corporation. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 IBM Corporation. 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
@@ -20,6 +20,9 @@
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.Query;
+
+import java.util.List;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.jpa.JpaEntityManager;
@@ -28,10 +31,15 @@
import org.eclipse.persistence.jpa.embeddable.model.DeepOrder;
import org.eclipse.persistence.jpa.embeddable.model.Order;
import org.eclipse.persistence.jpa.embeddable.model.OrderPK;
+import org.eclipse.persistence.jpa.embeddable.model.SpecAddress;
+import org.eclipse.persistence.jpa.embeddable.model.SpecContactInfo;
+import org.eclipse.persistence.jpa.embeddable.model.SpecEmployee;
+import org.eclipse.persistence.jpa.embeddable.model.SpecPhone;
import org.eclipse.persistence.jpa.embeddable.model.Zipcode;
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.SQLListener;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,7 +48,14 @@
public class TestNestedEmbeddable {
@Emf(createTables = DDLGen.DROP_CREATE, classes = { DeepOrder.class, DeepOrderPK.class, Order.class, OrderPK.class, Address.class, Zipcode.class })
private EntityManagerFactory emf;
-
+
+ @Emf(name = "SpecPersistenceUnit", createTables = DDLGen.DROP_CREATE,
+ classes = { SpecAddress.class, SpecContactInfo.class, SpecEmployee.class, SpecPhone.class })
+ private EntityManagerFactory emf2;
+
+ @SQLListener(name = "SpecPersistenceUnit")
+ List<String> _sql;
+
@Test
public void persistTest() {
if (emf == null)
@@ -109,4 +124,27 @@
em.close();
}
}
+
+ @Test
+ public void JPQLNestedEmbeddableTests() {
+ EntityManager em = emf2.createEntityManager();
+ try {
+ // Test to make sure that moving around the dot notation for nested embeddables is valid and doesn't change the query
+ Query queryEmbed = em.createQuery("SELECT c.primaryAddress.city FROM SpecEmployee e JOIN e.contactInfo c WHERE e.contactInfo.primaryAddress.zipcode = ?1");
+ queryEmbed.setParameter(1, "95053");
+ queryEmbed.getResultList();
+ Assert.assertEquals(1, _sql.size());
+ Assert.assertEquals("SELECT CITY FROM SPECEMPLOYEE WHERE (ZIPCODE = ?)", _sql.remove(0));
+
+ queryEmbed = em.createQuery("SELECT p.city FROM SpecEmployee e JOIN e.contactInfo.primaryAddress p WHERE e.contactInfo.primaryAddress.zipcode = ?1");
+ queryEmbed.setParameter(1, "95054");
+ queryEmbed.getResultList();
+ Assert.assertEquals(1, _sql.size());
+ Assert.assertEquals("SELECT CITY FROM SPECEMPLOYEE WHERE (ZIPCODE = ?)", _sql.remove(0));
+ } finally {
+ if (em.isOpen()) {
+ em.close();
+ }
+ }
+ }
}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecAddress.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecAddress.java
new file mode 100644
index 0000000..a477bf4
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecAddress.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2021 IBM Corporation. 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:
+// 04/09/2021 - Will Dazey
+// - 570702 : Using embeddable fields in query JOINs
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import jakarta.persistence.Embeddable;
+
+@Embeddable
+public class SpecAddress {
+ private String street;
+ private String city;
+ private String state;
+ private String zipcode;
+}
\ No newline at end of file
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecContactInfo.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecContactInfo.java
new file mode 100644
index 0000000..c708d8b
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecContactInfo.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021 IBM Corporation. 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:
+// 04/09/2021 - Will Dazey
+// - 570702 : Using embeddable fields in query JOINs
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import java.util.List;
+
+import jakarta.persistence.CollectionTable;
+import jakarta.persistence.ElementCollection;
+import jakarta.persistence.Embeddable;
+import jakarta.persistence.Embedded;
+import jakarta.persistence.ManyToMany;
+
+@Embeddable
+public class SpecContactInfo {
+ @Embedded
+ private SpecAddress primaryAddress;
+
+ @ElementCollection
+ @CollectionTable(name="PREV_ADDRESSES")
+ private List<SpecAddress> previousAddresses;
+
+ @ManyToMany
+ private List<SpecPhone> phones;
+}
\ No newline at end of file
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecEmployee.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecEmployee.java
new file mode 100644
index 0000000..fd333ce
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecEmployee.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021 IBM Corporation. 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:
+// 04/09/2021 - Will Dazey
+// - 570702 : Using embeddable fields in query JOINs
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import jakarta.persistence.Embedded;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+@Entity
+public class SpecEmployee {
+ @Id
+ private int id;
+
+ @Embedded
+ private SpecContactInfo contactInfo;
+}
\ No newline at end of file
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecPhone.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecPhone.java
new file mode 100644
index 0000000..f8f43a3
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/embeddable/model/SpecPhone.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021 IBM Corporation. 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:
+// 04/09/2021 - Will Dazey
+// - 570702 : Using embeddable fields in query JOINs
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+@Entity
+public class SpecPhone {
+ @Id
+ private int id;
+
+ private String vendor;
+}
\ No newline at end of file
diff --git a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLValidationTestSuite.java b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLValidationTestSuite.java
index 9d80de0..ad2323e 100644
--- a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLValidationTestSuite.java
+++ b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLValidationTestSuite.java
@@ -129,7 +129,7 @@
suite.addTest(new JUnitJPQLValidationTestSuite("testParameterPositionValidation2"));
suite.addTest(new JUnitJPQLValidationTestSuite("testParameterTypeValidation"));
suite.addTest(new JUnitJPQLValidationTestSuite("testEjbqlCaseSensitivity"));
- suite.addTest(new JUnitJPQLValidationTestSuite("testEjbqlUnsupportJoinArgument"));
+ suite.addTest(new JUnitJPQLValidationTestSuite("testEjbqlSupportJoinArgument"));
suite.addTest(new JUnitJPQLValidationTestSuite("testInvalidSetClause"));
suite.addTest(new JUnitJPQLValidationTestSuite("testUnsupportedCountDistinctOnOuterJoinedCompositePK"));
suite.addTest(new JUnitJPQLValidationTestSuite("testInvalidHint"));
@@ -1181,29 +1181,27 @@
}
}
- public void testEjbqlUnsupportJoinArgument() {
+ public void testEjbqlSupportJoinArgument() {
+ boolean testPass = true;
String ejbqlString;
- List result;
try
{
ejbqlString = "SELECT e.firstName FROM Employee e JOIN e.period ep";
- result = createEntityManager().createQuery(ejbqlString).getResultList();
- fail ("JOINing of embedded entities is not allowed must be thrown");
- } catch(IllegalArgumentException ex)
- {
- Assert.assertTrue(ex.getCause() instanceof JPQLException);
+ createEntityManager().createQuery(ejbqlString).getResultList();
+ } catch(Exception ex) {
+ testPass = false;
}
+ Assert.assertTrue(testPass);
try
{
ejbqlString = "SELECT e.firstName FROM Employee e JOIN FETCH e.period";
- result = createEntityManager().createQuery(ejbqlString).getResultList();
- fail ("JOINing of embedded entities is not allowed must be thrown");
- } catch(IllegalArgumentException ex)
- {
- Assert.assertTrue(ex.getCause() instanceof JPQLException);
+ createEntityManager().createQuery(ejbqlString).getResultList();
+ } catch(Exception ex) {
+ testPass = false;
}
+ Assert.assertTrue(testPass);
}
public void testInvalidSetClause() {
diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java
index 699e8b8..f8a2634 100644
--- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java
+++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2006, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021 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
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -939,6 +940,76 @@
}
/**
+ * Validates the given {@link Expression} and makes sure it's a valid collection value path expression.
+ *
+ * join_collection_valued_path_expression::=
+ * identification_variable.{single_valued_embeddable_object_field.}*collection_valued_field
+ * join_single_valued_path_expression::=
+ * identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field
+ *
+ * @param expression The {@link Expression} to validate
+ * @param collectionTypeOnly <code>true</code> to make sure the path expression resolves to a
+ * collection mapping only; <code>false</code> if it can simply resolves to a relationship mapping
+ */
+ protected boolean validateJoinCollectionValuedPathExpression(Expression expression,
+ boolean collectionTypeOnly) {
+
+ boolean valid = true;
+
+ // The path expression resolves to a collection-valued path expression
+ CollectionValuedPathExpression collectionValuedPathExpression = getCollectionValuedPathExpression(expression);
+
+ if (collectionValuedPathExpression != null &&
+ collectionValuedPathExpression.hasIdentificationVariable() &&
+ !collectionValuedPathExpression.endsWithDot()) {
+
+ // A collection_valued_field is designated by the name of an association field in a
+ // one-to-many or a many-to-many relationship or by the name of an element collection field
+
+ // A single_valued_object_field is designated by the name of an association field in a one-to-one or
+ // many-to-one relationship or a field of embeddable class type
+ Object mapping = helper.resolveMapping(expression);
+ Object type = helper.getMappingType(mapping);
+
+ // Does not resolve to a valid path
+ if (!helper.isTypeResolvable(type) || (mapping == null)) {
+
+ int startPosition = position(expression);
+ int endPosition = startPosition + length(expression);
+
+ addProblem(
+ expression,
+ startPosition,
+ endPosition,
+ CollectionValuedPathExpression_NotResolvable,
+ expression.toParsedText()
+ );
+
+ valid = false;
+ }
+ else if (!helper.isCollectionMapping(mapping) &&
+ !helper.isRelationshipMapping(mapping) &&
+ !helper.isEmbeddableMapping(mapping)) {
+
+ int startPosition = position(expression);
+ int endPosition = startPosition + length(expression);
+
+ addProblem(
+ expression,
+ startPosition,
+ endPosition,
+ CollectionValuedPathExpression_NotCollectionType,
+ expression.toParsedText()
+ );
+
+ valid = false;
+ }
+ }
+
+ return valid;
+ }
+
+ /**
* Validates the left and right expressions of the given {@link ComparisonExpression}. The tests
* to perform are:
* <ul>
@@ -1605,7 +1676,7 @@
if (expression.hasJoinAssociationPath()) {
Expression joinAssociationPath = expression.getJoinAssociationPath();
- validateCollectionValuedPathExpression(joinAssociationPath, false);
+ validateJoinCollectionValuedPathExpression(joinAssociationPath, false);
joinAssociationPath.accept(this);
}
diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/SemanticValidatorHelper.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/SemanticValidatorHelper.java
index 4873e94..e428afa 100644
--- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/SemanticValidatorHelper.java
+++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/SemanticValidatorHelper.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -293,6 +294,18 @@
boolean isCollectionMapping(Object mapping);
/**
+ * Determines whether the given mapping is an embeddable type mapping.
+ * <p>
+ * If it was going through Hermes SPI, the type of the arguments would be
+ * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
+ *
+ * @param mapping The mapping object to verify if it represents an embeddable mapping
+ * @return <code>true</code> if the given mapping is an embeddable mapping; <code>false</code>
+ * otherwise
+ */
+ boolean isEmbeddableMapping(Object mapping);
+
+ /**
* Determines whether the given type represents an {@link Enum}.
* <p>
* If it was going through Hermes SPI, the type of the argument would be
diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/GenericSemanticValidatorHelper.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/GenericSemanticValidatorHelper.java
index 77ada0b..956fe8d 100644
--- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/GenericSemanticValidatorHelper.java
+++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/GenericSemanticValidatorHelper.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -388,6 +389,11 @@
return (mapping != null) && ((IMapping) mapping).isCollection();
}
+ @Override
+ public boolean isEmbeddableMapping(Object mapping) {
+ return (mapping != null) && ((IMapping) mapping).isEmbeddable();
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/FromSubqueryResolver.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/FromSubqueryResolver.java
index cced0d2..2b43c78 100644
--- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/FromSubqueryResolver.java
+++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/FromSubqueryResolver.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -305,6 +306,12 @@
return (mapping != null) ? mapping.isCollection() : false;
}
+ @Override
+ public boolean isEmbeddable() {
+ IMapping mapping = resolver.getMapping();
+ return (mapping != null) ? mapping.isEmbeddable() : false;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/spi/IMapping.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/spi/IMapping.java
index 6bc3490..855bbc5 100644
--- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/spi/IMapping.java
+++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/spi/IMapping.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2006, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2021 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
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -103,6 +104,15 @@
boolean isCollection();
/**
+ * Determines whether this {@link IMapping} is an embeddable type mapping.
+ *
+ * @return <code>true</code> if this {@link IMapping} is an embeddable mapping;
+ * <code>false</code> otherwise
+ * @since 2.4
+ */
+ boolean isEmbeddable();
+
+ /**
* Determines whether this {@link IMapping} is a property type mapping.
*
* @return <code>true</code> if this {@link IMapping} is a property mapping; <code>false</code>
diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/spi/java/AbstractMapping.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/spi/java/AbstractMapping.java
index 1247d7d..af6945d 100644
--- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/spi/java/AbstractMapping.java
+++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/spi/java/AbstractMapping.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2021 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
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -285,6 +286,15 @@
}
}
+ @Override
+ public boolean isEmbeddable() {
+ switch (getMappingType()) {
+ case EMBEDDED:
+ case EMBEDDED_ID: return true;
+ default: return false;
+ }
+ }
+
/**
* {@inheritDoc}
*/