From d24cba019a894e0265e8ca9a325dfe165b52d148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kraus?= Date: Thu, 19 Oct 2023 15:34:03 +0200 Subject: [PATCH] jakartaee/persistence#454 - introduce FindOption interface and new overloads of EM.find() jakartaee/persistence#467 - add joins to entity types (range variables) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomáš Kraus --- .../i18n/ExceptionLocalizationResource.java | 3 + .../i18n/LoggingLocalizationResource.java | 9 ++ .../advanced/CriteriaQueryMetamodelTest.java | 28 ++++ .../internal/jpa/EJBQueryImpl.java | 31 +++-- .../internal/jpa/EntityManagerImpl.java | 12 +- .../internal/jpa/FindOptionUtils.java | 127 +++++++++++++++++- .../jpa/StoredProcedureQueryImpl.java | 15 ++- .../internal/jpa/metamodel/MetamodelImpl.java | 27 +++- .../internal/jpa/querydef/FromImpl.java | 46 ++++++- 9 files changed, 265 insertions(+), 33 deletions(-) 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 8f8e3bfc608..4a87f900d72 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 @@ -254,6 +254,9 @@ public class ExceptionLocalizationResource extends ListResourceBundle { { "getpersistenceunitutil_called_on_closed_emf", "getPersistenceUnitUtil() was called on a closed EntityManagerFactory."}, { "named_entity_graph_exists", "NamedEntityGraph with name {0} found on {1} already exists in this persistence unit."}, { "cannot_get_from_non_correlated_query", "getCorrelationParent() called on a from-clause that was not obtained through correlation." }, + { "multiple_keys_in_entity", "Cannot add join of {0} to {1} because target class contains multiple attributes of source type"}, + { "no_key_in_entity", "Cannot add join of {0} to {1} because target class is missing attribute of source type"}, + { "RIGHT_JOIN_NOT_SUPPORTED", "Right join is not supported"}, { "wrap_convert_exception", "An exception occurred while calling {0} on converter class {1} with value {2}"}, { "ora_pessimistic_locking_with_rownum", "Pessimistic locking with query row limits is not supported."}, { "bean_validation_constraint_violated", "One or more Bean Validation constraints were violated while executing Automatic Bean Validation on callback event: {0} for class: {1}. Please refer to the embedded constraint violations for details."}, diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java index 855deba7ab1..8fc2cfd3abe 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java @@ -474,6 +474,15 @@ public class LoggingLocalizationResource extends ListResourceBundle { { "dbws_orm_metadata_read_error", "The [{0}] ORM metadata could not be read."}, { "dbws_oxm_metadata_read_error", "The [{0}] OXM metadata could not be read."}, { "dbws_no_wsdl_inline_schema", "The [{0}] WSDL inline schema could not be read."}, + // JPA 3.2 + { "unknown_cacheRetrieveMode_type", "Unknown {0} type of jakarta.persistence.cache.retrieveMode property"}, + { "unknown_legacy_cacheRetrieveMode_type", "Unknown {0} type of jakarta.persistence.cacheRetrieveMode property"}, + { "unknown_cacheStoreMode_type", "Unknown {0} type of jakarta.persistence.cache.storeMode property"}, + { "unknown_legacy_cacheStoreMode_type", "Unknown {0} type of jakarta.persistence.cacheStoreMode property"}, + { "unknown_queryTimeoutUnit_type", "Unknown {0} type of eclipselink.query.timeout.unit property"}, + { "unknown_queryTimeout_type", "Unknown {0} type of jakarta.persistence.query.timeout property"}, + { "error_queryTimeoutParse", "Could not parse jakarta.persistence.query.timeout property value {0}: {1}"}, + { "validate_object_space", "validate object space." }, { "stack_of_visited_objects_that_refer_to_the_corrupt_object", "stack of visited objects that refer to the corrupt object: {0}" }, { "corrupt_object_referenced_through_mapping", "The following corrupt object is referenced through mapping: {0}" }, diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced2/src/test/java/org/eclipse/persistence/testing/tests/jpa21/advanced/CriteriaQueryMetamodelTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced2/src/test/java/org/eclipse/persistence/testing/tests/jpa21/advanced/CriteriaQueryMetamodelTest.java index 3d28a6e70ae..8097199a63f 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.advanced2/src/test/java/org/eclipse/persistence/testing/tests/jpa21/advanced/CriteriaQueryMetamodelTest.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced2/src/test/java/org/eclipse/persistence/testing/tests/jpa21/advanced/CriteriaQueryMetamodelTest.java @@ -181,6 +181,34 @@ public void testMetamodelOnClauseWithLeftJoin() { } } + // Join directly on Address class must return the same results as join on attribute + public void testMetamodelOnClauseWithLeftJoinOnClass() { + EntityManager em = createEntityManager(); + Query query = em.createQuery("Select e from Employee e left join e.address a on a.city = 'Ottawa' " + + "where a.postalCode is not null"); + List baseResult = query.getResultList(); + + Metamodel metamodel = em.getMetamodel(); + EntityType entityEmp_ = metamodel.entity(Employee.class); + EntityType
entityAddr_ = metamodel.entity(Address.class); + + CriteriaBuilder qb = em.getCriteriaBuilder(); + CriteriaQuerycq = qb.createQuery(Employee.class); + Root root = cq.from(entityEmp_); + Join address = root.join(Address.class, JoinType.LEFT); + address.on(qb.equal(address.get(entityAddr_.getSingularAttribute("city", String.class)), "Ottawa")); + cq.where(qb.isNotNull(address.get(entityAddr_.getSingularAttribute("postalCode", String.class)))); + List testResult = em.createQuery(cq).getResultList(); + + clearCache(); + closeEntityManager(em); + + if (baseResult.size() != testResult.size()) { + fail("Criteria query using ON clause with a left join did not match JPQL results; " + +baseResult.size()+" were expected, while criteria query returned "+testResult.size()); + } + } + /////UPDATE Criteria tests: public void simpleMetamodelCriteriaUpdateTest() { diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EJBQueryImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EJBQueryImpl.java index fd842fdddde..8953db3049d 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EJBQueryImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EJBQueryImpl.java @@ -41,8 +41,10 @@ import jakarta.persistence.Parameter; import jakarta.persistence.PersistenceException; import jakarta.persistence.TemporalType; +import jakarta.persistence.Timeout; import jakarta.persistence.TypedQuery; +import org.eclipse.persistence.config.QueryHints; import org.eclipse.persistence.exceptions.QueryException; import org.eclipse.persistence.expressions.Expression; import org.eclipse.persistence.internal.databaseaccess.DatasourcePlatform; @@ -54,6 +56,8 @@ import org.eclipse.persistence.internal.queries.JPQLCallQueryMechanism; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.jpa.JpaQuery; +import org.eclipse.persistence.logging.AbstractSessionLog; +import org.eclipse.persistence.logging.SessionLog; import org.eclipse.persistence.queries.Cursor; import org.eclipse.persistence.queries.DataReadQuery; import org.eclipse.persistence.queries.DatabaseQuery; @@ -296,43 +300,44 @@ public TypedQuery setHint(String hintName, Object value) { * if not a Java Persistence query language SELECT query */ @Override - public EJBQueryImpl setLockMode(LockModeType lockMode) { - return (EJBQueryImpl) super.setLockMode(lockMode); + @SuppressWarnings("unchecked") + public EJBQueryImpl setLockMode(LockModeType lockMode) { + return (EJBQueryImpl) super.setLockMode(lockMode); } - // TODO-API-3.2 + + // Based on EntityManagerImpl#getQueryHints(Object,OperationType) @Override public CacheRetrieveMode getCacheRetrieveMode() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getCacheRetrieveMode(getDatabaseQuery().getProperties()); } - // TODO-API-3.2 @Override public TypedQuery setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setCacheRetrieveMode(getDatabaseQuery().getProperties(), cacheRetrieveMode); + return this; } - // TODO-API-3.2 @Override public CacheStoreMode getCacheStoreMode() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getCacheStoreMode(getDatabaseQuery().getProperties()); } - // TODO-API-3.2 @Override public TypedQuery setCacheStoreMode(CacheStoreMode cacheStoreMode) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setCacheStoreMode(getDatabaseQuery().getProperties(), cacheStoreMode); + return this; } - // TODO-API-3.2 @Override public Integer getTimeout() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getTimeout(getDatabaseQuery().getProperties()); } @Override public TypedQuery setTimeout(Integer timeout) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setTimeout(getDatabaseQuery().getProperties(), timeout); + return this; } /** diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerImpl.java index 93a319de633..62b390eb0ad 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerImpl.java @@ -2911,28 +2911,24 @@ public LockModeType getLockMode(Object entity) { } } - // TODO-API-3.2 @Override public CacheRetrieveMode getCacheRetrieveMode() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getCacheRetrieveMode(properties); } - // TODO-API-3.2 @Override public void setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setCacheRetrieveMode(properties, cacheRetrieveMode); } - // TODO-API-3.2 @Override public CacheStoreMode getCacheStoreMode() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getCacheStoreMode(properties); } - // TODO-API-3.2 @Override public void setCacheStoreMode(CacheStoreMode cacheStoreMode) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setCacheStoreMode(properties, cacheStoreMode); } /** diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/FindOptionUtils.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/FindOptionUtils.java index e0e91fe6484..ed95f6013cd 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/FindOptionUtils.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/FindOptionUtils.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import jakarta.persistence.CacheRetrieveMode; @@ -26,7 +27,12 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.Timeout; +import jakarta.persistence.TypedQuery; import org.eclipse.persistence.config.QueryHints; +import org.eclipse.persistence.internal.sessions.AbstractSession; +import org.eclipse.persistence.logging.AbstractSessionLog; +import org.eclipse.persistence.logging.SessionLog; +import org.eclipse.persistence.queries.DatabaseQuery; /** * {@link FindOption} processing tools. @@ -95,7 +101,8 @@ private static void pessimisticLockScope(OptionsBuilder builder, FindOption cach } private static void timeout(OptionsBuilder builder, FindOption timeout) { - builder.properties.put(QueryHints.QUERY_TIMEOUT, timeout); + builder.properties.put(QueryHints.QUERY_TIMEOUT_UNIT, java.util.concurrent.TimeUnit.MILLISECONDS); + builder.properties.put(QueryHints.QUERY_TIMEOUT, ((Timeout)timeout).milliseconds()); } private static Options build(Map properties, FindOption... options) { @@ -143,4 +150,122 @@ static Options parse(Map properties, FindOption... options) { return OptionsBuilder.build(properties, options); } + private static final String LEGACY_CACHE_RETRIEVE_MODE = "jakarta.persistence.cacheRetrieveMode"; + + // Based on EntityManagerImpl#getQueryHints(Object,OperationType) + static CacheRetrieveMode getCacheRetrieveMode(Map properties) { + // Try QueryHints property 1st + Object propertyValue = properties.get(QueryHints.CACHE_RETRIEVE_MODE); + if (propertyValue instanceof CacheRetrieveMode) { + return (CacheRetrieveMode) propertyValue; + } else if (propertyValue != null) { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.QUERY, + "unknown_cacheRetrieveMode_type", + propertyValue.getClass().getName()); + } + // Try legacy property as fallback option + propertyValue = properties.get(LEGACY_CACHE_RETRIEVE_MODE); + if (propertyValue instanceof CacheRetrieveMode) { + return (CacheRetrieveMode) propertyValue; + } else if (propertyValue != null) { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.QUERY, + "unknown_legacy_cacheRetrieveMode_type", + propertyValue.getClass().getName()); + } + // Default value according to JPA spec. + return CacheRetrieveMode.USE; + } + + @SuppressWarnings("unchecked") + static void setCacheRetrieveMode(Map properties, CacheRetrieveMode cacheRetrieveMode) { + // Remove legacy property if exists, will be overwritten + properties.remove(LEGACY_CACHE_RETRIEVE_MODE); + // Store new cache retrieve mode as QueryHints.CACHE_RETRIEVE_MODE. + // Previous value, if exists, is overwritten + ((Map)properties).put(QueryHints.CACHE_RETRIEVE_MODE, cacheRetrieveMode); + } + + private static final String LEGACY_CACHE_STORE_MODE = "jakarta.persistence.cacheStoreMode"; + + static CacheStoreMode getCacheStoreMode(Map properties) { + // Try QueryHints property 1st + Object propertyValue = properties.get(QueryHints.CACHE_STORE_MODE); + if (propertyValue instanceof CacheStoreMode) { + return (CacheStoreMode) propertyValue; + } else if (propertyValue != null) { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.QUERY, + "unknown_cacheStoreMode_type", + propertyValue.getClass().getName()); + } + // Try legacy property as fallback option + propertyValue = properties.get(LEGACY_CACHE_STORE_MODE); + if (propertyValue instanceof CacheStoreMode) { + return (CacheStoreMode) propertyValue; + } else if (propertyValue != null) { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.QUERY, + "unknown_legacy_cacheStoreMode_type", + propertyValue.getClass().getName()); + } + // Default value according to JPA spec. + return CacheStoreMode.USE; + } + + @SuppressWarnings("unchecked") + static void setCacheStoreMode(Map properties, CacheStoreMode cacheStoreMode) { + // Remove legacy property if exists, will be overwritten + properties.remove(LEGACY_CACHE_STORE_MODE); + // Store new cache retrieve mode as QueryHints.CACHE_STORE_MODE. + // Previous value, if exists, is overwritten + ((Map)properties).put(QueryHints.CACHE_STORE_MODE, cacheStoreMode); + } + + static Integer getTimeout(Map properties) { + // QueryHints.QUERY_TIMEOUT_UNIT may contain TimeUnit + TimeUnit timeUnit = TimeUnit.MILLISECONDS; + Object propertyValue = properties.get(QueryHints.QUERY_TIMEOUT_UNIT); + if (propertyValue instanceof TimeUnit) { + timeUnit = (TimeUnit)propertyValue; + } else if (propertyValue != null) { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.QUERY, + "unknown_queryTimeoutUnit_type", + propertyValue.getClass().getName()); + } + // QueryHints.QUERY_TIMEOUT must be converted from actual units to milliseconds + propertyValue = properties.get(QueryHints.QUERY_TIMEOUT); + if (propertyValue instanceof Number n) { + return (int)TimeUnit.MILLISECONDS.convert(n.longValue(), timeUnit); + } else if (propertyValue instanceof String s) { + try { + long value = Long.parseLong(s); + return (int)TimeUnit.MILLISECONDS.convert(value, timeUnit); + } catch (NumberFormatException e) { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.QUERY, + "error_queryTimeoutParse", + s, + e.getLocalizedMessage()); + } + } else { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.QUERY, + "unknown_queryTimeout_type", + propertyValue.getClass().getName()); + } + // Return default value (means no timeout was set) + return null; + } + + @SuppressWarnings("unchecked") + static void setTimeout(Map properties, Integer timeout) { + // Javadoc does not specify units. Default QueryHints.QUERY_TIMEOUT unit is milliseconds + // so timeout argument is expected to be miliseconds too. + ((Map)properties).put(QueryHints.QUERY_TIMEOUT_UNIT, TimeUnit.MILLISECONDS); + ((Map)properties).put(QueryHints.QUERY_TIMEOUT, timeout); + } + } diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java index 3101a289dd8..2d7a23caec6 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java @@ -883,37 +883,40 @@ public StoredProcedureQueryImpl setFlushMode(FlushModeType flushMode) { // TODO-API-3.2 @Override public CacheRetrieveMode getCacheRetrieveMode() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getCacheRetrieveMode(getDatabaseQuery().getProperties()); } // TODO-API-3.2 @Override public StoredProcedureQueryImpl setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setCacheRetrieveMode(getDatabaseQuery().getProperties(), cacheRetrieveMode); + return this; } // TODO-API-3.2 @Override public CacheStoreMode getCacheStoreMode() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getCacheStoreMode(getDatabaseQuery().getProperties()); } // TODO-API-3.2 @Override public StoredProcedureQueryImpl setCacheStoreMode(CacheStoreMode cacheStoreMode) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setCacheStoreMode(getDatabaseQuery().getProperties(), cacheStoreMode); + return this; } // TODO-API-3.2 @Override public Integer getTimeout() { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return FindOptionUtils.getTimeout(getDatabaseQuery().getProperties()); } // TODO-API-3.2 @Override public StoredProcedureQueryImpl setTimeout(Integer timeout) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + FindOptionUtils.setTimeout(getDatabaseQuery().getProperties(), timeout); + return this; } /** diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/MetamodelImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/MetamodelImpl.java index 0dd848a0100..e63e1900974 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/MetamodelImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/MetamodelImpl.java @@ -185,10 +185,35 @@ private void entityEmbeddableManagedTypeNotFound(Map typeMap, Object aType, Clas } } + private void entityEmbeddableManagedTypeNotFound(Map typeMap, Object aType, String entityName, String metamodelType, String metamodelTypeName) { + // 338837: verify that the collection is not empty - this would mean entities did not make it into the search path + if (typeMap.isEmpty()) { + AbstractSessionLog.getLog().log(SessionLog.WARNING, + SessionLog.METAMODEL, + "metamodel_type_collection_empty_during_lookup", + entityName, + metamodelTypeName); + } + if (null == aType) { + throw new IllegalArgumentException(ExceptionLocalization.buildMessage( + "metamodel_class_null_type_instance", + new Object[] {entityName, metamodelTypeName, metamodelType})); + } else { + throw new IllegalArgumentException(ExceptionLocalization.buildMessage( + "metamodel_class_incorrect_type_instance", + new Object[] {entityName, metamodelTypeName, aType})); + } + } + + // TODO-API-3.2 @Override public EntityType entity(String entityName) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + EntityTypeImpl aType = this.entities.get(entityName); + if (aType == null) { + entityEmbeddableManagedTypeNotFound(entities, null, entityName, "Entity", "EntityType"); + } + return aType; } /** diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/FromImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/FromImpl.java index 8f721e46fe3..3bcf5ed8c38 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/FromImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/FromImpl.java @@ -50,6 +50,7 @@ import org.eclipse.persistence.internal.expressions.ObjectExpression; import org.eclipse.persistence.internal.helper.ClassConstants; +import org.eclipse.persistence.internal.jpa.metamodel.ManagedTypeImpl; import org.eclipse.persistence.internal.localization.ExceptionLocalization; /** @@ -528,25 +529,62 @@ public Join join(String attributeName, JoinType jt) { // TODO-API-3.2 @Override public Join join(Class entityClass) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return join(entityClass, JoinType.INNER); } // TODO-API-3.2 @Override public Join join(Class entityClass, JoinType joinType) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + SingularAttribute key = null; + // Search target class attributes for SingularAttribute matching source class + for (Attribute attribute : ((ManagedType)managedType).getAttributes()) { + if (attribute instanceof SingularAttribute + && ((SingularAttribute)attribute).getBindableJavaType().isAssignableFrom(entityClass)) { + if (key == null) { + key = (SingularAttribute)attribute; + // Multiple matching attributes found, can't select the proper one + } else { + throw new IllegalStateException( + ExceptionLocalization.buildMessage("multiple_keys_in_entity", + new String[] { + entityClass.getName(), + this.managedType.getJavaType().getName()})); + } + } + } + if (key == null) { + throw new IllegalStateException( + ExceptionLocalization.buildMessage("no_key_in_entity", + new String[] { + entityClass.getName(), + this.managedType.getJavaType().getName()})); + } + ObjectExpression exp = ((ObjectExpression)this.currentNode).newDerivedExpressionNamed(key.getName()); + switch(joinType) { + case LEFT: + exp.doUseOuterJoin(); + break; + case RIGHT: + throw new UnsupportedOperationException(ExceptionLocalization.buildMessage("RIGHT_JOIN_NOT_SUPPORTED")); + case INNER: + exp.doNotUseOuterJoin(); + } + JoinImpl join = new JoinImpl<>(this, managedType, this.metamodel, entityClass, exp, key, joinType); + this.joins.add(join); + join.isJoin = true; + return join; } // TODO-API-3.2 @Override public Join join(EntityType entity) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return join(entity, JoinType.INNER); } // TODO-API-3.2 @Override public Join join(EntityType entity, JoinType joinType) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return join(entity.getJavaType(), joinType); } @Override