From 7f5265de3518ea71b0d867278ffbcfb654d129ff Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Thu, 5 Sep 2024 11:54:26 -0400 Subject: [PATCH] Make compatible with latest OpenJPA: @ElementCollection/@OneToOne metadata is apparently only available after startup, so changed BaseEntityService logic to be lazy fetching --- .../service/BaseEntityService.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/omnifaces/persistence/service/BaseEntityService.java b/src/main/java/org/omnifaces/persistence/service/BaseEntityService.java index edc463f..5bb3ed3 100644 --- a/src/main/java/org/omnifaces/persistence/service/BaseEntityService.java +++ b/src/main/java/org/omnifaces/persistence/service/BaseEntityService.java @@ -17,7 +17,6 @@ import static java.lang.Integer.MAX_VALUE; import static java.lang.String.format; import static java.util.Collections.emptyList; -import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; import static java.util.Optional.ofNullable; import static java.util.logging.Level.FINE; @@ -72,6 +71,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -223,9 +223,9 @@ public abstract class BaseEntityService & Serializable, private Provider provider = Provider.UNKNOWN; private Database database = Database.UNKNOWN; - private Set elementCollections = emptySet(); - private Set manyOrOneToOnes = emptySet(); - private java.util.function.Predicate oneToManys = field -> false; + private Supplier> elementCollections = Collections::emptySet; + private Supplier> manyOrOneToOnes = Collections::emptySet; + private java.util.function.Predicate oneToManys = field -> false; private Validator validator; @PersistenceContext @@ -254,8 +254,8 @@ protected BaseEntityService() { protected void initWithEntityManager() { provider = Provider.of(getEntityManager()); database = Database.of(getEntityManager()); - elementCollections = ELEMENT_COLLECTION_MAPPINGS.computeIfAbsent(entityType, this::computeElementCollectionMapping); - manyOrOneToOnes = MANY_OR_ONE_TO_ONE_MAPPINGS.computeIfAbsent(entityType, this::computeManyOrOneToOneMapping); + elementCollections = () -> ELEMENT_COLLECTION_MAPPINGS.computeIfAbsent(entityType, this::computeElementCollectionMapping); + manyOrOneToOnes = () -> MANY_OR_ONE_TO_ONE_MAPPINGS.computeIfAbsent(entityType, this::computeManyOrOneToOneMapping); oneToManys = field -> ONE_TO_MANY_MAPPINGS.computeIfAbsent(entityType, this::computeOneToManyMapping).stream().anyMatch(oneToMany -> field.startsWith(oneToMany + '.')); if (getValidationMode(getEntityManager()) == ValidationMode.CALLBACK) { @@ -1838,12 +1838,12 @@ private PathResolver buildSelection(PageBuilder pageBuilder, Ab var orderingContainsAggregatedFields = aggregatedFields.removeAll(pageBuilder.getPage().getOrdering().keySet()); pageBuilder.shouldBuildCountSubquery(true); // Normally, building of count subquery is skipped for performance, but when there's a custom mapping, we cannot reliably determine if custom criteria is used, so count subquery building cannot be reliably skipped. pageBuilder.canBuildValueBasedPagingPredicate(getProvider() != HIBERNATE || !orderingContainsAggregatedFields); // Value based paging cannot be used in Hibernate if ordering contains aggregated fields, because Hibernate may return a cartesian product and apply firstResult/maxResults in memory. - return new MappedPathResolver(root, paths, ELEMENT_COLLECTION_MAPPINGS.get(entityType), MANY_OR_ONE_TO_ONE_MAPPINGS.get(entityType)); + return new MappedPathResolver(root, paths, elementCollections.get(), manyOrOneToOnes.get()); } else if (pageBuilder.getResultType() == entityType) { pageBuilder.shouldBuildCountSubquery(mapping != null); // mapping is empty but not null when getPage(..., QueryBuilder) is used. pageBuilder.canBuildValueBasedPagingPredicate(mapping == null); // when mapping is not null, we cannot reliably determine if ordering contains aggregated fields, so value based paging cannot be reliably used. - return new RootPathResolver(root, ELEMENT_COLLECTION_MAPPINGS.get(entityType), MANY_OR_ONE_TO_ONE_MAPPINGS.get(entityType)); + return new RootPathResolver(root, elementCollections.get(), manyOrOneToOnes.get()); } else { throw new IllegalArgumentException(ERROR_ILLEGAL_MAPPING); @@ -1889,7 +1889,7 @@ private void buildOrderBy(PageBuilder pageBuilder, CriteriaQuer private Order buildOrder(Entry order, CriteriaBuilder criteriaBuilder, PathResolver pathResolver, boolean reversed) { var field = order.getKey(); - if (oneToManys.test(field) || elementCollections.contains(field)) { + if (oneToManys.test(field) || elementCollections.get().contains(field)) { if (getProvider() == ECLIPSELINK) { throw new UnsupportedOperationException(ERROR_UNSUPPORTED_ONETOMANY_ORDERBY_ECLIPSELINK); // EclipseLink refuses to perform a JOIN when setFirstResult/setMaxResults is used. } @@ -1993,7 +1993,7 @@ private List buildPredicates(Map criter private Predicate buildPredicate(Entry parameter, AbstractQuery query, CriteriaBuilder criteriaBuilder, PathResolver pathResolver, Map parameters) { var field = parameter.getKey(); - var path = pathResolver.get(elementCollections.contains(field) ? pathResolver.join(field) : field); + var path = pathResolver.get(elementCollections.get().contains(field) ? pathResolver.join(field) : field); var type = ID.equals(field) ? identifierType : path.getJavaType(); return buildTypedPredicate(path, type, field, parameter.getValue(), query, criteriaBuilder, pathResolver, new UncheckedParameterBuilder(field, criteriaBuilder, parameters)); } @@ -2016,7 +2016,7 @@ private Predicate buildTypedPredicate(Expression path, Class else if (value instanceof Criteria criteriaObject) { predicate = criteriaObject.build(path, criteriaBuilder, parameterBuilder); } - else if (elementCollections.contains(field)) { + else if (elementCollections.get().contains(field)) { predicate = buildElementCollectionPredicate(alias, path, type, field, value, query, criteriaBuilder, pathResolver, parameterBuilder); } else if (value instanceof Iterable || value.getClass().isArray()) { @@ -2088,7 +2088,7 @@ private Predicate buildArrayPredicate(Expression path, Class throw new UnsupportedOperationException(ERROR_UNSUPPORTED_ONETOMANY_CRITERIA_ECLIPSELINK); // EclipseLink refuses to perform a JOIN when setFirstResult/setMaxResults is used. } - var elementCollectionField = elementCollections.contains(field); + var elementCollectionField = elementCollections.get().contains(field); Subquery subquery = null; Expression fieldPath; @@ -2097,7 +2097,7 @@ private Predicate buildArrayPredicate(Expression path, Class // Otherwise the main query will return ONLY the matching values while the natural expectation in UI is that they are just all returned. subquery = query.subquery(Long.class); Root subqueryRoot = subquery.from(entityType); - fieldPath = new RootPathResolver(subqueryRoot, elementCollections, manyOrOneToOnes).get(pathResolver.join(field)); + fieldPath = new RootPathResolver(subqueryRoot, elementCollections.get(), manyOrOneToOnes.get()).get(pathResolver.join(field)); subquery.select(criteriaBuilder.countDistinct(fieldPath)).where(criteriaBuilder.equal(subqueryRoot.get(ID), pathResolver.get(ID))); } else {