Skip to content

Commit

Permalink
jakartaee/persistence#454 - introduce FindOption interface and new ov…
Browse files Browse the repository at this point in the history
…erloads of EM.find()

jakartaee/persistence#467 - add joins to entity types (range variables)

Signed-off-by: Tomáš Kraus <[email protected]>
  • Loading branch information
Tomas-Kraus committed Oct 25, 2023
1 parent 8139584 commit 3b500cc
Show file tree
Hide file tree
Showing 11 changed files with 261 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ 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." },
{ "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."},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,13 @@ 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_cacheStoreMode_type", "Unknown {0} type of jakarta.persistence.cache.storeMode 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}" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public static Test suite() {
suite.addTest(new CriteriaQueryMetamodelTest("testMetamodelOnClause"));
suite.addTest(new CriteriaQueryMetamodelTest("testMetamodelOnClauseOverCollection"));
suite.addTest(new CriteriaQueryMetamodelTest("testMetamodelOnClauseWithLeftJoin"));
suite.addTest(new CriteriaQueryMetamodelTest("testMetamodelOnClauseWithLeftJoinOnClass"));
suite.addTest(new CriteriaQueryMetamodelTest("simpleMetamodelCriteriaUpdateTest"));
suite.addTest(new CriteriaQueryMetamodelTest("testMetamodelCriteriaUpdate"));
suite.addTest(new CriteriaQueryMetamodelTest("testMetamodelComplexConditionCaseInCriteriaUpdate"));
Expand Down Expand Up @@ -181,6 +182,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<Employee> entityEmp_ = metamodel.entity(Employee.class);
EntityType<Address> entityAddr_ = metamodel.entity(Address.class);

CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Employee>cq = qb.createQuery(Employee.class);
Root<Employee> root = cq.from(entityEmp_);
Join<Employee, Address> 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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -296,43 +300,44 @@ public TypedQuery<X> 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<X> setLockMode(LockModeType lockMode) {
return (EJBQueryImpl<X>) 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<X> 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<X> 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<X> setTimeout(Integer timeout) {
throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet");
FindOptionUtils.setTimeout(getDatabaseQuery().getProperties(), timeout);
return this;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.util.Map;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.QueryHints;
import org.eclipse.persistence.config.TargetDatabase;
import org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.TableCreationType;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
Expand Down Expand Up @@ -85,7 +86,9 @@ public class EntityManagerFactoryProvider {
{PersistenceUnitProperties.JDBC_PASSWORD , "eclipselink.jdbc.password"},
{PersistenceUnitProperties.WEAVING , "persistence.tools.weaving"},
{PersistenceUnitProperties.LOGGING_LEVEL + "." + SessionLog.METAMODEL, PersistenceUnitProperties.LOGGING_LEVEL + ".jpa_" + SessionLog.METAMODEL},
{PersistenceUnitProperties.LOGGING_LEVEL + "." + SessionLog.METADATA, PersistenceUnitProperties.LOGGING_LEVEL + ".ejb_or_" + SessionLog.METADATA}
{PersistenceUnitProperties.LOGGING_LEVEL + "." + SessionLog.METADATA, PersistenceUnitProperties.LOGGING_LEVEL + ".ejb_or_" + SessionLog.METADATA},
{QueryHints.CACHE_RETRIEVE_MODE, "jakarta.persistence.cacheRetrieveMode"},
{QueryHints.CACHE_STORE_MODE, "jakarta.persistence.cacheStoreMode"}
};

/**
Expand Down Expand Up @@ -132,8 +135,16 @@ protected static void generateDefaultTables(SchemaManager mgr, TableCreationType
* {@link System} property or {@code null} if property identified by {@code propertyKey} does not exist.
*/
public static String getConfigPropertyAsString(final String propertyKey, final Map overrides) {
final String value = overrides != null ? (String)overrides.get(propertyKey) : null;
return value != null ? value : PrivilegedAccessHelper.getSystemProperty(propertyKey);
Object value = overrides != null ? overrides.get(propertyKey) : null;
if (value != null) {
if (value instanceof String strValue) {
return strValue;
} else {
return value.toString();
}
} else {
return PrivilegedAccessHelper.getSystemProperty(propertyKey);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,9 @@ protected Object findInternal(ClassDescriptor descriptor, AbstractSession sessio
}
}

// Translate deprecated properties to the current names
EntityManagerFactoryProvider.translateOldProperties(properties, this.databaseSession);

// Get the read object query and apply the properties to it.
// PERF: use descriptor defined query to avoid extra query creation.
ReadObjectQuery query = descriptor.getQueryManager().getReadObjectQuery();
Expand All @@ -956,8 +959,10 @@ protected Object findInternal(ClassDescriptor descriptor, AbstractSession sessio

// Apply any EclipseLink defaults if they haven't been set through
// the properties.
if (properties == null || ( !properties.containsKey(QueryHints.CACHE_USAGE) && !properties.containsKey(QueryHints.CACHE_RETRIEVE_MODE) && !properties.containsKey(QueryHints.CACHE_STORE_MODE)
&& !properties.containsKey("jakarta.persistence.cacheRetrieveMode") && !properties.containsKey("jakarta.persistence.cacheStoreMode"))) {
if (properties == null ||
( !properties.containsKey(QueryHints.CACHE_USAGE)
&& !properties.containsKey(QueryHints.CACHE_RETRIEVE_MODE)
&& !properties.containsKey(QueryHints.CACHE_STORE_MODE))) {
query.conformResultsInUnitOfWork();
}

Expand Down Expand Up @@ -2911,28 +2916,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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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<String, Object> properties, FindOption... options) {
Expand Down Expand Up @@ -143,4 +150,90 @@ static Options parse(Map<String, Object> properties, FindOption... options) {
return OptionsBuilder.build(properties, options);
}

// Based on EntityManagerImpl#getQueryHints(Object,OperationType)
static CacheRetrieveMode getCacheRetrieveMode(Map<?, Object> properties) {
// QueryHints property
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());
}
// Default value according to JPA spec.
return CacheRetrieveMode.USE;
}

@SuppressWarnings("unchecked")
static void setCacheRetrieveMode(Map<?, Object> properties, CacheRetrieveMode cacheRetrieveMode) {
((Map<String, Object>)properties).put(QueryHints.CACHE_RETRIEVE_MODE, cacheRetrieveMode);
}

static CacheStoreMode getCacheStoreMode(Map<?, Object> properties) {
// QueryHints property
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());
}
// Default value according to JPA spec.
return CacheStoreMode.USE;
}

@SuppressWarnings("unchecked")
static void setCacheStoreMode(Map<?, Object> properties, CacheStoreMode cacheStoreMode) {
((Map<String, Object>)properties).put(QueryHints.CACHE_STORE_MODE, cacheStoreMode);
}

static Integer getTimeout(Map<?, Object> 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<?, Object> 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<String, Object>)properties).put(QueryHints.QUERY_TIMEOUT_UNIT, TimeUnit.MILLISECONDS);
((Map<String, Object>)properties).put(QueryHints.QUERY_TIMEOUT, timeout);
}

}
Loading

0 comments on commit 3b500cc

Please sign in to comment.