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 19, 2023
1 parent ae7f32c commit d24cba0
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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."},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<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 @@ -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);
}

/**
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,122 @@ static Options parse(Map<String, Object> 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<?, Object> 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<?, Object> 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<String, Object>)properties).put(QueryHints.CACHE_RETRIEVE_MODE, cacheRetrieveMode);
}

private static final String LEGACY_CACHE_STORE_MODE = "jakarta.persistence.cacheStoreMode";

static CacheStoreMode getCacheStoreMode(Map<?, Object> 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<?, Object> 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<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);
}

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

/**
Expand Down
Loading

0 comments on commit d24cba0

Please sign in to comment.