From c6e2b2e8c93c7e6f550744c6b58f9f7bc7b12433 Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Wed, 4 Sep 2024 09:21:46 -0400 Subject: [PATCH] Bump versions to at least Java 17, JEE 10, Hibernate 6; fixed failing logic and tests caused by changes in Hibernate 6; catch up Java 17 features such as var and instanceof var --- pom.xml | 90 ++--- .../org/omnifaces/persistence/Provider.java | 21 +- .../service/BaseEntityService.java | 309 ++++++++---------- .../persistence/test/OmniPersistenceTest.java | 194 +++++------ .../test/service/StartupService.java | 22 +- 5 files changed, 302 insertions(+), 334 deletions(-) diff --git a/pom.xml b/pom.xml index d02a65b..15f2f9a 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.omnifaces omnipersistence - 0.20.J2 + 0.21.J1-SNAPSHOT jar OmniPersistence @@ -62,21 +62,20 @@ - 11 - 9 - + 17 + 10 + UTF-8 UTF-8 - ${javase.version} - ${javase.version} + ${javase.version} true - 26.1.1.Final - 6.2022.1.Alpha2 - 6.2.5 - 1.4.200 + 33.0.1.Final + 6.2024.8 + 7.0.17 + 2.3.232 @@ -100,7 +99,7 @@ org.hibernate.orm hibernate-jpamodelgen - 6.0.2.Final + 6.6.0.Final provided @@ -108,43 +107,23 @@ org.junit.jupiter junit-jupiter - 5.8.2 - test - - - org.jboss.arquillian.junit5 - arquillian-junit5-container - test - - - org.jboss.shrinkwrap.resolver - shrinkwrap-resolver-depchain - pom + 5.11.0 test + + org.jboss.arquillian.junit5 + arquillian-junit5-container + 1.9.1.Final + test + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-impl-maven-archive + 3.3.1 + test + - - - - - - org.jboss.arquillian - arquillian-bom - 1.7.0.Alpha10 - pom - import - - - org.jboss.shrinkwrap.resolver - shrinkwrap-resolver-bom - 3.1.4 - pom - test - - - - @@ -152,7 +131,7 @@ com.mycila license-maven-plugin - 4.1 + 4.5
license.txt
@@ -176,7 +155,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.2 + 3.4.2 @@ -202,7 +181,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.1 attach-sources @@ -217,7 +196,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.0 + 3.10.0 ${java.home}/bin/javadoc ${javase.version} @@ -242,7 +221,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.7.0 true ossrh @@ -267,7 +246,7 @@ com.mycila license-maven-plugin - [4.1,) + [4.4,) format @@ -287,7 +266,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.3.0 + 3.8.0 process-test-classes @@ -303,7 +282,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.5.0 false @@ -331,7 +310,7 @@ org.wildfly.arquillian wildfly-arquillian-container-managed - 3.0.1.Final + 5.0.1.Final test @@ -361,7 +340,6 @@ ${project.build.directory}/wildfly-preview-${test.wildfly.version} - org.jboss.logmanager.LogManager @@ -376,7 +354,7 @@ org.omnifaces.arquillian arquillian-glassfish-server-managed - 1.1 + 1.6 test @@ -438,7 +416,7 @@ org.omnifaces.arquillian arquillian-glassfish-server-managed - 1.1 + 1.6 test diff --git a/src/main/java/org/omnifaces/persistence/Provider.java b/src/main/java/org/omnifaces/persistence/Provider.java index bed9f3a..444cae9 100644 --- a/src/main/java/org/omnifaces/persistence/Provider.java +++ b/src/main/java/org/omnifaces/persistence/Provider.java @@ -24,7 +24,6 @@ import java.io.Serializable; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -50,7 +49,7 @@ public enum Provider { @Override public String getDialectName(EntityManagerFactory entityManagerFactory) { - Object unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory); + var unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory); if (HIBERNATE_SESSION_FACTORY.get().isInstance(unwrappedEntityManagerFactory)) { // 5.2+ has merged hibernate-entitymanager into hibernate-core, and made EntityManagerFactory impl an instance of SessionFactory, and removed getDialect() shortcut method. @@ -66,8 +65,10 @@ public boolean isAggregation(Expression expression) { if (HIBERNATE_6_0_0_AGGREGATE_FUNCTION.isPresent()) { return HIBERNATE_6_0_0_AGGREGATE_FUNCTION.get().isInstance(expression); } - return (HIBERNATE_BASIC_FUNCTION_EXPRESSION.get().isInstance(expression) && (boolean) invokeMethod(expression, "isAggregation")) - || (HIBERNATE_COMPARISON_PREDICATE.get().isInstance(expression) && (isAggregation(invokeMethod(expression, "getLeftHandOperand")) || isAggregation(invokeMethod(expression, "getRightHandOperand")))); + else { + return HIBERNATE_BASIC_FUNCTION_EXPRESSION.get().isInstance(expression) && (boolean) invokeMethod(expression, "isAggregation") + || HIBERNATE_COMPARISON_PREDICATE.get().isInstance(expression) && (isAggregation(invokeMethod(expression, "getLeftHandOperand")) || isAggregation(invokeMethod(expression, "getRightHandOperand"))); + } } @Override @@ -105,7 +106,7 @@ private & Serializable, E extends BaseEntity> T i @Override public String getDialectName(EntityManagerFactory entityManagerFactory) { - Object unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory); + var unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory); return invokeMethod(invokeMethod(unwrappedEntityManagerFactory, "getDatabaseSession"), "getDatasourcePlatform").getClass().getSimpleName(); } @@ -119,7 +120,7 @@ public boolean isAggregation(Expression expression) { @Override public String getDialectName(EntityManagerFactory entityManagerFactory) { - Object unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory); + var unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory); return invokeMethod(invokeMethod(unwrappedEntityManagerFactory, "getConfiguration"), "getDBDictionaryInstance").getClass().getSimpleName(); } @@ -164,10 +165,10 @@ public boolean isOneToMany(Attribute attribute) { private static final Set AGGREGATE_FUNCTIONS = unmodifiableSet("MIN", "MAX", "SUM", "AVG", "COUNT"); private static Object unwrapEntityManagerFactoryIfNecessary(EntityManagerFactory entityManagerFactory) { - String packageName = entityManagerFactory.getClass().getPackage().getName(); + var packageName = entityManagerFactory.getClass().getPackage().getName(); if (packageName.startsWith("org.apache.openejb.")) { - Optional getDelegate = findMethod(entityManagerFactory, "getDelegate"); + var getDelegate = findMethod(entityManagerFactory, "getDelegate"); return getDelegate.isPresent() ? invokeMethod(entityManagerFactory, getDelegate.get()) : entityManagerFactory; } @@ -175,7 +176,7 @@ private static Object unwrapEntityManagerFactoryIfNecessary(EntityManagerFactory } public static Provider of(EntityManager entityManager) { - String packageName = entityManager.getDelegate().getClass().getPackage().getName(); + var packageName = entityManager.getDelegate().getClass().getPackage().getName(); if (packageName.startsWith("org.hibernate.")) { return HIBERNATE; @@ -253,7 +254,7 @@ public & Serializable, E extends BaseEntity> String } Class entityType = getEntityType(entity); - Table table = entityType.getAnnotation(Table.class); + var table = entityType.getAnnotation(Table.class); return table != null ? table.name() : entityType.getSimpleName().toUpperCase(); } diff --git a/src/main/java/org/omnifaces/persistence/service/BaseEntityService.java b/src/main/java/org/omnifaces/persistence/service/BaseEntityService.java index 0c4a219..edc463f 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.emptyMap; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; import static java.util.Optional.ofNullable; @@ -86,7 +85,6 @@ import jakarta.persistence.CacheStoreMode; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; -import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.GeneratedValue; @@ -161,7 +159,7 @@ *
  • {@link VersionedEntity} * * - *

    Logging

    + *

    Logging

    *

    * {@link BaseEntityService} uses JULI {@link Logger} for logging. *

      @@ -241,8 +239,8 @@ public abstract class BaseEntityService & Serializable, * The I and E will be resolved to a concrete Class<?>. */ @SuppressWarnings("unchecked") - public BaseEntityService() { - Entry, Class> typeMapping = TYPE_MAPPINGS.computeIfAbsent(getClass(), BaseEntityService::computeTypeMapping); + protected BaseEntityService() { + var typeMapping = TYPE_MAPPINGS.computeIfAbsent(getClass(), BaseEntityService::computeTypeMapping); identifierType = (Class) typeMapping.getKey(); entityType = (Class) typeMapping.getValue(); generatedId = GENERATED_ID_MAPPINGS.computeIfAbsent(entityType, BaseEntityService::computeGeneratedIdMapping); @@ -267,54 +265,54 @@ protected void initWithEntityManager() { @SuppressWarnings("rawtypes") private static Entry, Class> computeTypeMapping(Class subclass) { - List> actualTypeArguments = getActualTypeArguments(subclass, BaseEntityService.class); - Class identifierType = actualTypeArguments.get(0); - Class entityType = actualTypeArguments.get(1); + var actualTypeArguments = getActualTypeArguments(subclass, BaseEntityService.class); + var identifierType = actualTypeArguments.get(0); + var entityType = actualTypeArguments.get(1); logger.log(FINE, () -> format(LOG_FINE_COMPUTED_TYPE_MAPPING, subclass, identifierType, entityType)); return new SimpleEntry<>(identifierType, entityType); } private static boolean computeGeneratedIdMapping(Class entityType) { - boolean generatedId = GeneratedIdEntity.class.isAssignableFrom(entityType) || !listAnnotatedFields(entityType, Id.class, GeneratedValue.class).isEmpty(); + var generatedId = GeneratedIdEntity.class.isAssignableFrom(entityType) || !listAnnotatedFields(entityType, Id.class, GeneratedValue.class).isEmpty(); logger.log(FINE, () -> format(LOG_FINE_COMPUTED_GENERATED_ID_MAPPING, entityType, generatedId)); return generatedId; } private static SoftDeleteData computeSoftDeleteMapping(Class entityType) { - SoftDeleteData softDeleteData = new SoftDeleteData(entityType); + var softDeleteData = new SoftDeleteData(entityType); logger.log(FINE, () -> format(LOG_FINE_COMPUTED_SOFT_DELETE_MAPPING, entityType, softDeleteData)); return softDeleteData; } private Set computeElementCollectionMapping(Class> entityType) { - Set elementCollectionMapping = computeEntityMapping(entityType, "", new HashSet<>(), getProvider()::isElementCollection); + var elementCollectionMapping = computeEntityMapping(entityType, "", new HashSet<>(), getProvider()::isElementCollection); logger.log(FINE, () -> format(LOG_FINE_COMPUTED_ELEMENTCOLLECTION_MAPPING, entityType, elementCollectionMapping)); return elementCollectionMapping; } private Set computeManyOrOneToOneMapping(Class> entityType) { - Set manyOrOneToOneMapping = computeEntityMapping(entityType, "", new HashSet<>(), getProvider()::isManyOrOneToOne); + var manyOrOneToOneMapping = computeEntityMapping(entityType, "", new HashSet<>(), getProvider()::isManyOrOneToOne); logger.log(FINE, () -> format(LOG_FINE_COMPUTED_MANY_OR_ONE_TO_ONE_MAPPING, entityType, manyOrOneToOneMapping)); return manyOrOneToOneMapping; } private Set computeOneToManyMapping(Class> entityType) { - Set oneToManyMapping = computeEntityMapping(entityType, "", new HashSet<>(), getProvider()::isOneToMany); + var oneToManyMapping = computeEntityMapping(entityType, "", new HashSet<>(), getProvider()::isOneToMany); logger.log(FINE, () -> format(LOG_FINE_COMPUTED_ONE_TO_MANY_MAPPING, entityType, oneToManyMapping)); return oneToManyMapping; } private Set computeEntityMapping(Class type, String basePath, Set> nestedTypes, java.util.function.Predicate> attributePredicate) { - Set entityMapping = new HashSet<>(2); - EntityType entity = getEntityManager().getMetamodel().entity(type); + var entityMapping = new HashSet(2); + var entity = getEntityManager().getMetamodel().entity(type); - for (Attribute attribute : entity.getAttributes()) { + for (var attribute : entity.getAttributes()) { if (attributePredicate.test(attribute)) { entityMapping.add(basePath + attribute.getName()); } - if (attribute instanceof Bindable) { - Class nestedType = ((Bindable) attribute).getBindableJavaType(); + if (attribute instanceof Bindable bindable) { + Class nestedType = bindable.getBindableJavaType(); if (BaseEntity.class.isAssignableFrom(nestedType) && nestedType != entityType && nestedTypes.add(nestedType)) { entityMapping.addAll(computeEntityMapping(nestedType, basePath + attribute.getName() + '.', nestedTypes, attributePredicate)); @@ -712,7 +710,7 @@ public E getById(I id) { * @return Found entity, or null if there is none. */ protected E getById(I id, boolean includeSoftDeleted) { - E entity = getEntityManager().find(entityType, id); + var entity = getEntityManager().find(entityType, id); if (entity != null && !includeSoftDeleted && softDeleteData.isSoftDeleted(entity)) { return null; @@ -728,9 +726,8 @@ protected E getById(I id, boolean includeSoftDeleted) { * @return Found entity, or null if there is none. */ public E getByIdWithLoadGraph(I id, String entityGraphName) { - EntityGraph entityGraph = entityManager.getEntityGraph(entityGraphName); - - Map properties = new HashMap<>(); + var entityGraph = entityManager.getEntityGraph(entityGraphName); + var properties = new HashMap(); properties.put(QUERY_HINT_LOAD_GRAPH, entityGraph); properties.put(QUERY_HINT_CACHE_RETRIEVE_MODE, BYPASS); @@ -745,7 +742,7 @@ public E getByIdWithLoadGraph(I id, String entityGraphName) { */ public E getSoftDeletedById(I id) { softDeleteData.checkSoftDeletable(); - E entity = getEntityManager().find(entityType, id); + var entity = getEntityManager().find(entityType, id); if (entity != null && !softDeleteData.isSoftDeleted(entity)) { return null; @@ -775,7 +772,7 @@ protected List getByIds(Iterable ids, boolean includeSoftDeleted) { return emptyList(); } - String whereClause = softDeleteData.getWhereClause(includeSoftDeleted); + var whereClause = softDeleteData.getWhereClause(includeSoftDeleted); return list(select("") + whereClause + (whereClause.isEmpty() ? " WHERE" : " AND") + " e.id IN (:ids)" + " ORDER BY e.id DESC", p -> p.put("ids", ids)); @@ -788,7 +785,7 @@ protected List getByIds(Iterable ids, boolean includeSoftDeleted) { * @return Whether entity with given entity exists. */ protected boolean exists(E entity) { - I id = getProvider().getIdentifier(entity); + var id = getProvider().getIdentifier(entity); return id != null && createLongQuery("SELECT COUNT(e) FROM " + entityType.getSimpleName() + " e WHERE e.id = :id") .setParameter("id", id) .getSingleResult().intValue() > 0; @@ -913,28 +910,28 @@ private String update(String jpql) { } private TypedQuery createQuery(String jpql, Object... parameters) { - TypedQuery query = getEntityManager().createQuery(jpql, entityType); + var query = getEntityManager().createQuery(jpql, entityType); setPositionalParameters(query, parameters); return query; } private TypedQuery createQuery(String jpql, Consumer> parameters) { - TypedQuery query = getEntityManager().createQuery(jpql, entityType); + var query = getEntityManager().createQuery(jpql, entityType); setSuppliedParameters(query, parameters); return query; } private TypedQuery createQuery(CriteriaQueryBuilder queryBuilder, Consumer> parameters) { - CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder(); - CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(entityType); - Root root = buildRoot(criteriaQuery); + var criteriaBuilder = getEntityManager().getCriteriaBuilder(); + var criteriaQuery = criteriaBuilder.createQuery(entityType); + var root = buildRoot(criteriaQuery); queryBuilder.build(criteriaBuilder, criteriaQuery, root); - TypedQuery query = getEntityManager().createQuery(criteriaQuery); + var query = getEntityManager().createQuery(criteriaQuery); - if (root instanceof EclipseLinkRoot) { - ((EclipseLinkRoot) root).runPostponedFetches(query); + if (root instanceof EclipseLinkRoot eclipseLinkRoot) { + eclipseLinkRoot.runPostponedFetches(query); } setSuppliedParameters(query, parameters); @@ -1022,17 +1019,17 @@ public E update(E entity) { * @throws IllegalEntityStateException When entity is not persisted or its ID is not generated. */ protected E updateAndFlush(E entity) { - E updatedEntity = update(entity); + var updatedEntity = update(entity); getEntityManager().flush(); return updatedEntity; } - private void logConstraintViolations(Set> constraintViolations) { + private static void logConstraintViolations(Set> constraintViolations) { constraintViolations.forEach(violation -> { - String constraintName = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(); - String beanName = violation.getRootBeanClass().getSimpleName(); - String propertyName = violation.getPropertyPath().toString(); - String violationMessage = violation.getMessage(); + var constraintName = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(); + var beanName = violation.getRootBeanClass().getSimpleName(); + var propertyName = violation.getPropertyPath().toString(); + var violationMessage = violation.getMessage(); Object beanInstance = violation.getRootBean(); logger.severe(format(LOG_SEVERE_CONSTRAINT_VIOLATION, constraintName, beanName, propertyName, violationMessage, beanInstance)); }); @@ -1045,7 +1042,7 @@ private void logConstraintViolations(Set> const * @throws IllegalEntityStateException When at least one entity has no ID. */ public List update(Iterable entities) { - return stream(entities).map(this::update).collect(toList()); + return stream(entities).map(this::update).toList(); } /** @@ -1103,13 +1100,12 @@ protected int update(String jpql, Consumer> parameters) { * @return Saved entity. */ public E save(E entity) { - if ((generatedId && entity.getId() == null) || (!generatedId && !exists(entity))) { + if (generatedId && entity.getId() == null || !generatedId && !exists(entity)) { persist(entity); return entity; } else { - E update = update(entity); - return update; + return update(entity); } } @@ -1121,7 +1117,7 @@ public E save(E entity) { * @return Saved entity. */ protected E saveAndFlush(E entity) { - E savedEntity = save(entity); + var savedEntity = save(entity); getEntityManager().flush(); return savedEntity; } @@ -1223,7 +1219,7 @@ protected E manage(E entity) { throw new NullPointerException("Entity is null."); } - I id = getProvider().getIdentifier(entity); + var id = getProvider().getIdentifier(entity); if (id == null) { throw new IllegalEntityStateException(entity, "Entity has no ID."); @@ -1233,7 +1229,7 @@ protected E manage(E entity) { return entity; } - E managed = getEntityManager().find(getProvider().getEntityType(entity), id); + var managed = getEntityManager().find(getProvider().getEntityType(entity), id); if (managed == null) { throw new EntityNotFoundException("Entity has in meanwhile been deleted."); @@ -1258,14 +1254,13 @@ protected E manageIfNecessary(E entity) { return null; } - if (!(entity instanceof BaseEntity)) { + if (!(entity instanceof BaseEntity baseEntity)) { throw new IllegalArgumentException(); } - BaseEntity baseEntity = (BaseEntity) entity; - I id = getProvider().getIdentifier(baseEntity); + var id = getProvider().getIdentifier(baseEntity); - if (id == null || (entity.getClass().getAnnotation(Entity.class) != null && getEntityManager().contains(entity))) { + if (id == null || entity.getClass().getAnnotation(Entity.class) != null && getEntityManager().contains(entity)) { return entity; } @@ -1287,7 +1282,7 @@ public void reset(E entity) { throw new IllegalEntityStateException(entity, "Only unmanaged entities can be resetted."); } - E managed = manage(entity); + var managed = manage(entity); getMetamodel(entity).getAttributes().stream().map(Attribute::getJavaMember).filter(Field.class::isInstance).forEach(field -> map(field, managed, entity)); // Note: EntityManager#refresh() is insuitable as it requires a managed entity and thus merge() could unintentionally persist changes before resetting. } @@ -1403,7 +1398,7 @@ protected Optional fetchLazyBlobs(Optional entity) { @SuppressWarnings("unchecked") // Unfortunately, @SafeVarargs cannot be used as it requires a final method. private E fetchPluralAttributes(E entity, java.util.function.Predicate ofType, Function... getters) { if (isEmpty(getters)) { - for (PluralAttribute a : getMetamodel().getPluralAttributes()) { + for (var a : getMetamodel().getPluralAttributes()) { if (ofType.test(a.getCollectionType())) { ofNullable(invokeGetter(entity, a.getName())).ifPresent(c -> invokeMethod(c, "size")); } @@ -1417,11 +1412,11 @@ private E fetchPluralAttributes(E entity, java.util.function.Predicate> ofType) { - E managed = getById(entity.getId()); + var managed = getById(entity.getId()); - for (Attribute a : getMetamodel().getSingularAttributes()) { + for (var a : getMetamodel().getSingularAttributes()) { if (ofType.test(a.getJavaType())) { - String name = capitalize(a.getName()); + var name = capitalize(a.getName()); invokeSetter(entity, name, invokeGetter(managed, name)); } } @@ -1635,10 +1630,10 @@ protected PartialResultList getPage(Page page, boolean count, String... fetch */ protected PartialResultList getPage(Page page, boolean count, boolean cacheable, String... fetchFields) { return getPage(page, count, cacheable, entityType, (builder, query, root) -> { - for (String fetchField : fetchFields) { + for (var fetchField : fetchFields) { FetchParent fetchParent = root; - for (String attribute : fetchField.split("\\.")) { + for (var attribute : fetchField.split("\\.")) { fetchParent = fetchParent.fetch(attribute); } } @@ -1730,10 +1725,10 @@ protected PartialResultList getPage(Page page, boolean count, b try { logger.log(FINER, () -> format(LOG_FINER_GET_PAGE, page, count, cacheable, resultType)); - PageBuilder pageBuilder = new PageBuilder<>(page, cacheable, resultType, queryBuilder); - CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder(); + var pageBuilder = new PageBuilder<>(page, cacheable, resultType, queryBuilder); + var criteriaBuilder = getEntityManager().getCriteriaBuilder(); TypedQuery entityQuery = buildEntityQuery(pageBuilder, criteriaBuilder); - TypedQuery countQuery = count ? buildCountQuery(pageBuilder, criteriaBuilder) : null; + var countQuery = count ? buildCountQuery(pageBuilder, criteriaBuilder) : null; PartialResultList resultList = executeQuery(pageBuilder, entityQuery, countQuery); logger.log(FINER, () -> format(LOG_FINER_QUERY_RESULT, resultList, resultList.getEstimatedTotalNumberOfResults())); return resultList; @@ -1747,84 +1742,68 @@ protected PartialResultList getPage(Page page, boolean count, b // Query actions -------------------------------------------------------------------------------------------------- private TypedQuery buildEntityQuery(PageBuilder pageBuilder, CriteriaBuilder criteriaBuilder) { - CriteriaQuery entityQuery = criteriaBuilder.createQuery(pageBuilder.getResultType()); - Root entityQueryRoot = buildRoot(entityQuery); - PathResolver pathResolver = buildSelection(pageBuilder, entityQuery, entityQueryRoot, criteriaBuilder); + var entityQuery = criteriaBuilder.createQuery(pageBuilder.getResultType()); + var entityQueryRoot = buildRoot(entityQuery); + var pathResolver = buildSelection(pageBuilder, entityQuery, entityQueryRoot, criteriaBuilder); buildOrderBy(pageBuilder, entityQuery, criteriaBuilder, pathResolver); - Map parameters = buildRestrictions(pageBuilder, entityQuery, criteriaBuilder, pathResolver); - return buildTypedQuery(pageBuilder, entityQuery, entityQueryRoot, parameters); + return buildTypedQuery(pageBuilder, entityQuery, entityQueryRoot, buildRestrictions(pageBuilder, entityQuery, criteriaBuilder, pathResolver)); } private TypedQuery buildCountQuery(PageBuilder pageBuilder, CriteriaBuilder criteriaBuilder) { - CriteriaQuery countQuery = criteriaBuilder.createQuery(Long.class); - Root countQueryRoot = countQuery.from(entityType); + var countQuery = criteriaBuilder.createQuery(Long.class); + var countQueryRoot = countQuery.from(entityType); countQuery.select(criteriaBuilder.count(countQueryRoot)); - Map parameters = pageBuilder.shouldBuildCountSubquery() ? buildCountSubquery(pageBuilder, countQuery, countQueryRoot, criteriaBuilder) : emptyMap(); + var parameters = pageBuilder.shouldBuildCountSubquery() ? buildCountSubquery(pageBuilder, countQuery, countQueryRoot, criteriaBuilder) : Collections.emptyMap(); return buildTypedQuery(pageBuilder, countQuery, null, parameters); } private Map buildCountSubquery(PageBuilder pageBuilder, CriteriaQuery countQuery, Root countRoot, CriteriaBuilder criteriaBuilder) { - Subquery countSubquery = countQuery.subquery(pageBuilder.getResultType()); - Root countSubqueryRoot = buildRoot(countSubquery); - PathResolver subqueryPathResolver = buildSelection(pageBuilder, countSubquery, countSubqueryRoot, criteriaBuilder); - Map parameters = buildRestrictions(pageBuilder, countSubquery, criteriaBuilder, subqueryPathResolver); - - if (getProvider() == HIBERNATE) { - // SELECT COUNT(e) FROM E e WHERE e IN (SELECT t FROM T t WHERE [restrictions]) - countQuery.where(criteriaBuilder.in(countRoot).value(countSubquery)); - // EclipseLink (tested 2.6.4) fails here with an incorrect selection in subquery: SQLException: Database "T1" not found; SQL statement: SELECT COUNT(t0.ID) FROM PERSON t0 WHERE t0.ID IN (SELECT DISTINCT t1.ID.t1.ID FROM PERSON t1 WHERE [...]) - // OpenJPA (tested 2.4.2) fails here as it doesn't interpret root as @Id: org.apache.openjpa.persistence.ArgumentException: Filter invalid. Cannot compare value of type optimusfaces.test.Person to value of type java.lang.Long. - } - else if (getProvider() == OPENJPA) { - // SELECT COUNT(e) FROM E e WHERE e.id IN (SELECT t.id FROM T t WHERE [restrictions]) - countQuery.where(criteriaBuilder.in(countRoot.get(ID)).value(countSubquery)); - // Hibernate (tested 5.0.10) fails here when DTO is used as it does not have a mapped ID. - // EclipseLink (tested 2.6.4) fails here with an incorrect selection in subquery: SQLException: Database "T1" not found; SQL statement: SELECT COUNT(t0.ID) FROM PERSON t0 WHERE t0.ID IN (SELECT DISTINCT t1.ID.t1.ID FROM PERSON t1 WHERE [...]) - } - else { - // SELECT COUNT(e) FROM E e WHERE EXISTS (SELECT t.id FROM T t WHERE [restrictions] AND t.id = e.id) - countQuery.where(criteriaBuilder.exists(countSubquery.where(conjunctRestrictionsIfNecessary(criteriaBuilder, countSubquery.getRestriction(), criteriaBuilder.equal(countSubqueryRoot.get(ID), countRoot.get(ID)))))); - // Hibernate (tested 5.0.10) and OpenJPA (tested 2.4.2) also support this but this is a tad less efficient than IN. - } + var countSubquery = countQuery.subquery(pageBuilder.getResultType()); + var countSubqueryRoot = buildRoot(countSubquery); + var subqueryPathResolver = buildSelection(pageBuilder, countSubquery, countSubqueryRoot, criteriaBuilder); + var parameters = buildRestrictions(pageBuilder, countSubquery, criteriaBuilder, subqueryPathResolver); + + // SELECT COUNT(e) FROM E e WHERE EXISTS (SELECT t.id FROM T t WHERE [restrictions] AND t.id = e.id) + countQuery.where(criteriaBuilder.exists(countSubquery.where(conjunctRestrictionsIfNecessary(criteriaBuilder, countSubquery.getRestriction(), criteriaBuilder.equal(countSubqueryRoot.get(ID), countRoot.get(ID)))))); return parameters; } private TypedQuery buildTypedQuery(PageBuilder pageBuilder, CriteriaQuery criteriaQuery, Root root, Map parameters) { - TypedQuery typedQuery = getEntityManager().createQuery(criteriaQuery); + var typedQuery = getEntityManager().createQuery(criteriaQuery); buildRange(pageBuilder, typedQuery, root); setMappedParameters(typedQuery, parameters); onPage(pageBuilder.getResultType(), pageBuilder.isCacheable()).accept(typedQuery); return typedQuery; } - private void setPositionalParameters(TypedQuery typedQuery, Object[] positionalParameters) { + private static void setPositionalParameters(TypedQuery typedQuery, Object[] positionalParameters) { logger.log(FINER, () -> format(LOG_FINER_SET_PARAMETER_VALUES, Arrays.toString(positionalParameters))); range(0, positionalParameters.length).forEach(i -> typedQuery.setParameter(i, positionalParameters[i])); } - private void setMappedParameters(TypedQuery typedQuery, Map mappedParameters) { + private static void setMappedParameters(TypedQuery typedQuery, Map mappedParameters) { logger.log(FINER, () -> format(LOG_FINER_SET_PARAMETER_VALUES, mappedParameters)); mappedParameters.entrySet().forEach(parameter -> typedQuery.setParameter(parameter.getKey(), parameter.getValue())); } - private void setSuppliedParameters(TypedQuery typedQuery, Consumer> suppliedParameters) { - Map mappedParameters = new HashMap<>(); + private static void setSuppliedParameters(TypedQuery typedQuery, Consumer> suppliedParameters) { + var mappedParameters = new HashMap(); suppliedParameters.accept(mappedParameters); setMappedParameters(typedQuery, mappedParameters); } private PartialResultList executeQuery(PageBuilder pageBuilder, TypedQuery entityQuery, TypedQuery countQuery) { - Page page = pageBuilder.getPage(); - List entities = entityQuery.getResultList(); + var page = pageBuilder.getPage(); + var entities = entityQuery.getResultList(); if (pageBuilder.canBuildValueBasedPagingPredicate() && page.isReversed()) { - List reversed = new ArrayList<>(entities); + var reversed = new ArrayList<>(entities); Collections.reverse(reversed); entities = reversed; } - int estimatedTotalNumberOfResults = (countQuery != null) ? countQuery.getSingleResult().intValue() : -1; + var estimatedTotalNumberOfResults = countQuery != null ? countQuery.getSingleResult().intValue() : -1; return new PartialResultList<>(entities, page.getOffset(), estimatedTotalNumberOfResults); } @@ -1832,22 +1811,22 @@ private PartialResultList executeQuery(PageBuilder pageBuild // Selection actions ---------------------------------------------------------------------------------------------- private Root buildRoot(AbstractQuery query) { - Root root = query.from(entityType); - return (query instanceof Subquery) ? new SubqueryRoot<>(root) : (getProvider() == ECLIPSELINK) ? new EclipseLinkRoot<>(root) : root; + var root = query.from(entityType); + return query instanceof Subquery ? new SubqueryRoot<>(root) : getProvider() == ECLIPSELINK ? new EclipseLinkRoot<>(root) : root; } private PathResolver buildSelection(PageBuilder pageBuilder, AbstractQuery query, Root root, CriteriaBuilder criteriaBuilder) { - Map, Expression> mapping = pageBuilder.getQueryBuilder().build(criteriaBuilder, query, root); + var mapping = pageBuilder.getQueryBuilder().build(criteriaBuilder, query, root); - if (query instanceof Subquery) { - ((Subquery) query).select(root.get(ID)); + if (query instanceof Subquery subQuery) { + subQuery.select(root.get(ID)); } if (!isEmpty(mapping)) { // mapping is not empty when getPage(..., MappedQueryBuilder) is used. - Map> paths = stream(mapping).collect(toMap(e -> e.getKey().getPropertyName(), e -> e.getValue(), (l, r) -> l, LinkedHashMap::new)); + Map> paths = stream(mapping).collect(toMap(e -> e.getKey().getPropertyName(), Entry::getValue, (l, r) -> l, LinkedHashMap::new)); - if (query instanceof CriteriaQuery) { - ((CriteriaQuery) query).multiselect(stream(paths).map(Alias::as).collect(toList())); + if (query instanceof CriteriaQuery criteriaQuery) { + criteriaQuery.multiselect(stream(paths).map(Alias::as).collect(toList())); } Set aggregatedFields = paths.entrySet().stream().filter(e -> getProvider().isAggregation(e.getValue())).map(Entry::getKey).collect(toSet()); @@ -1856,7 +1835,7 @@ private PathResolver buildSelection(PageBuilder pageBuilder, Ab groupByIfNecessary(query, root); } - boolean orderingContainsAggregatedFields = aggregatedFields.removeAll(pageBuilder.getPage().getOrdering().keySet()); + 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)); @@ -1876,8 +1855,8 @@ private void buildRange(PageBuilder pageBuilder, Query query, R return; } - boolean hasJoins = hasJoins(root); - Page page = pageBuilder.getPage(); + var hasJoins = hasJoins(root); + var page = pageBuilder.getPage(); if ((hasJoins || page.getOffset() > 0) && !pageBuilder.canBuildValueBasedPagingPredicate()) { query.setFirstResult(page.getOffset()); @@ -1887,8 +1866,8 @@ private void buildRange(PageBuilder pageBuilder, Query query, R query.setMaxResults(page.getLimit()); } - if (hasJoins && root instanceof EclipseLinkRoot) { - ((EclipseLinkRoot) root).runPostponedFetches(query); + if (hasJoins && root instanceof EclipseLinkRoot eclipseLinkRoot) { + eclipseLinkRoot.runPostponedFetches(query); } } @@ -1896,19 +1875,19 @@ private void buildRange(PageBuilder pageBuilder, Query query, R // Sorting actions ------------------------------------------------------------------------------------------------ private void buildOrderBy(PageBuilder pageBuilder, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder, PathResolver pathResolver) { - Page page = pageBuilder.getPage(); - Map ordering = page.getOrdering(); + var page = pageBuilder.getPage(); + var ordering = page.getOrdering(); if (ordering.isEmpty() || page.getLimit() - page.getOffset() == 1) { return; } - boolean reversed = pageBuilder.canBuildValueBasedPagingPredicate() && page.isReversed(); + var reversed = pageBuilder.canBuildValueBasedPagingPredicate() && page.isReversed(); criteriaQuery.orderBy(stream(ordering).map(order -> buildOrder(order, criteriaBuilder, pathResolver, reversed)).collect(toList())); } private Order buildOrder(Entry order, CriteriaBuilder criteriaBuilder, PathResolver pathResolver, boolean reversed) { - String field = order.getKey(); + var field = order.getKey(); if (oneToManys.test(field) || elementCollections.contains(field)) { if (getProvider() == ECLIPSELINK) { @@ -1919,18 +1898,18 @@ else if (getProvider() == OPENJPA) { } } - Expression path = pathResolver.get(field); - return (order.getValue() ^ reversed) ? criteriaBuilder.asc(path) : criteriaBuilder.desc(path); + var path = pathResolver.get(field); + return order.getValue() ^ reversed ? criteriaBuilder.asc(path) : criteriaBuilder.desc(path); } // Searching actions ----------------------------------------------------------------------------------------------- private Map buildRestrictions(PageBuilder pageBuilder, AbstractQuery query, CriteriaBuilder criteriaBuilder, PathResolver pathResolver) { - Page page = pageBuilder.getPage(); - Map parameters = new HashMap<>(page.getRequiredCriteria().size() + page.getOptionalCriteria().size()); - List requiredPredicates = buildPredicates(page.getRequiredCriteria(), query, criteriaBuilder, pathResolver, parameters); - List optionalPredicates = buildPredicates(page.getOptionalCriteria(), query, criteriaBuilder, pathResolver, parameters); + var page = pageBuilder.getPage(); + var parameters = new HashMap(page.getRequiredCriteria().size() + page.getOptionalCriteria().size()); + var requiredPredicates = buildPredicates(page.getRequiredCriteria(), query, criteriaBuilder, pathResolver, parameters); + var optionalPredicates = buildPredicates(page.getOptionalCriteria(), query, criteriaBuilder, pathResolver, parameters); Predicate restriction = null; if (!optionalPredicates.isEmpty()) { @@ -1940,23 +1919,23 @@ private Map buildRestrictions(PageBuilder pageB if (!requiredPredicates.isEmpty()) { pageBuilder.shouldBuildCountSubquery(true); - List wherePredicates = requiredPredicates.stream().filter(Alias::isWhere).collect(toList()); + var wherePredicates = requiredPredicates.stream().filter(Alias::isWhere).collect(toList()); if (!wherePredicates.isEmpty()) { restriction = conjunctRestrictionsIfNecessary(criteriaBuilder, restriction, wherePredicates); } - List inPredicates = wherePredicates.stream().filter(Alias::isIn).collect(toList()); + var inPredicates = wherePredicates.stream().filter(Alias::isIn).collect(toList()); - for (Predicate inPredicate : inPredicates) { - Predicate countPredicate = buildCountPredicateIfNecessary(inPredicate, criteriaBuilder, query, pathResolver); + for (var inPredicate : inPredicates) { + var countPredicate = buildCountPredicateIfNecessary(inPredicate, criteriaBuilder, query, pathResolver); if (countPredicate != null) { requiredPredicates.add(countPredicate); } } - List havingPredicates = requiredPredicates.stream().filter(Alias::isHaving).collect(toList()); + var havingPredicates = requiredPredicates.stream().filter(Alias::isHaving).collect(toList()); if (!havingPredicates.isEmpty()) { groupByIfNecessary(query, pathResolver.get(null)); @@ -1969,7 +1948,7 @@ private Map buildRestrictions(PageBuilder pageB } if (restriction != null) { - boolean distinct = !optionalPredicates.isEmpty() || hasFetches((From) pathResolver.get(null)); + var distinct = !optionalPredicates.isEmpty() || hasFetches((From) pathResolver.get(null)); query.distinct(distinct).where(conjunctRestrictionsIfNecessary(criteriaBuilder, query.getRestriction(), restriction)); } @@ -1981,24 +1960,24 @@ private > Predicate buildValueBasedPagingPr // Value based paging https://blog.novatec-gmbh.de/art-pagination-offset-vs-value-based-paging/ is on large offsets much faster than offset based paging. // (orderByField1 > ?1) OR (orderByField1 = ?1 AND orderByField2 > ?2) OR (orderByField1 = ?1 AND orderByField2 = ?2 AND orderByField3 > ?3) [...] - List predicates = new ArrayList<>(page.getOrdering().size()); - Map, ParameterExpression> orderByFields = new HashMap<>(); - T last = (T) page.getLast(); + var predicates = new ArrayList(page.getOrdering().size()); + var orderByFields = new HashMap, ParameterExpression>(); + var last = (T) page.getLast(); - for (Entry order : page.getOrdering().entrySet()) { - String field = order.getKey(); - V value = invokeGetter(last, field); - Expression path = (Expression) pathResolver.get(field); + for (var order : page.getOrdering().entrySet()) { + var field = order.getKey(); + var value = invokeGetter(last, field); + var path = (Expression) pathResolver.get(field); ParameterExpression parameter = new UncheckedParameterBuilder(field, criteriaBuilder, parameters).create(value); - Predicate predicate = order.getValue() ^ page.isReversed() ? criteriaBuilder.greaterThan(path, parameter) : criteriaBuilder.lessThan(path, parameter); + var predicate = order.getValue() ^ page.isReversed() ? criteriaBuilder.greaterThan(path, parameter) : criteriaBuilder.lessThan(path, parameter); - for (Entry, ParameterExpression> previousOrderByField : orderByFields.entrySet()) { - Expression previousPath = previousOrderByField.getKey(); - ParameterExpression previousParameter = previousOrderByField.getValue(); - predicate = criteriaBuilder.and(predicate, (previousParameter == null) ? criteriaBuilder.isNull(previousPath) : criteriaBuilder.equal(previousPath, previousParameter)); + for (var previousOrderByField : orderByFields.entrySet()) { + var previousPath = previousOrderByField.getKey(); + var previousParameter = previousOrderByField.getValue(); + predicate = criteriaBuilder.and(predicate, previousParameter == null ? criteriaBuilder.isNull(previousPath) : criteriaBuilder.equal(previousPath, previousParameter)); } - orderByFields.put(path, (value == null) ? null : parameter); + orderByFields.put(path, value == null ? null : parameter); predicates.add(predicate); } @@ -2013,17 +1992,17 @@ private List buildPredicates(Map criter } private Predicate buildPredicate(Entry parameter, AbstractQuery query, CriteriaBuilder criteriaBuilder, PathResolver pathResolver, Map parameters) { - String field = parameter.getKey(); - Expression path = pathResolver.get(elementCollections.contains(field) ? pathResolver.join(field) : field); - Class type = ID.equals(field) ? identifierType : path.getJavaType(); + var field = parameter.getKey(); + var path = pathResolver.get(elementCollections.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)); } @SuppressWarnings("unchecked") private Predicate buildTypedPredicate(Expression path, Class type, String field, Object criteria, AbstractQuery query, CriteriaBuilder criteriaBuilder, PathResolver pathResolver, ParameterBuilder parameterBuilder) { - Alias alias = Alias.create(getProvider(), path, field); - Object value = criteria; - boolean negated = value instanceof Not; + var alias = Alias.create(getProvider(), path, field); + var value = criteria; + var negated = value instanceof Not; Predicate predicate; if (negated) { @@ -2031,11 +2010,11 @@ private Predicate buildTypedPredicate(Expression path, Class } try { - if (value == null || (value instanceof Criteria && ((Criteria) value).getValue() == null)) { + if (value == null || value instanceof Criteria criteriaObject && criteriaObject.getValue() == null) { predicate = criteriaBuilder.isNull(path); } - else if (value instanceof Criteria) { - predicate = ((Criteria) value).build(path, criteriaBuilder, parameterBuilder); + else if (value instanceof Criteria criteriaObject) { + predicate = criteriaObject.build(path, criteriaBuilder, parameterBuilder); } else if (elementCollections.contains(field)) { predicate = buildElementCollectionPredicate(alias, path, type, field, value, query, criteriaBuilder, pathResolver, parameterBuilder); @@ -2076,7 +2055,7 @@ else if (String.class.isAssignableFrom(type) || value instanceof String) { } private Predicate buildElementCollectionPredicate(Alias alias, Expression path, Class type, String field, Object value, AbstractQuery query, CriteriaBuilder criteriaBuilder, PathResolver pathResolver, ParameterBuilder parameterBuilder) { - if (getProvider() == ECLIPSELINK || (getProvider() == HIBERNATE && getDatabase() == POSTGRESQL)) { + if (getProvider() == ECLIPSELINK || getProvider() == HIBERNATE && getDatabase() == POSTGRESQL) { // EclipseLink refuses to perform GROUP BY on IN clause on @ElementCollection, causing a cartesian product. // Hibernate + PostgreSQL bugs on IN clause on @ElementCollection as PostgreSQL strictly requires an additional GROUP BY, but Hibernate didn't set it. return buildArrayPredicate(path, type, field, value, query, criteriaBuilder, pathResolver, parameterBuilder); @@ -2087,8 +2066,8 @@ private Predicate buildElementCollectionPredicate(Alias alias, Exp } } - private Predicate buildInPredicate(Alias alias, Expression path, Class type, Object value, ParameterBuilder parameterBuilder) { - List> in = stream(value) + private static Predicate buildInPredicate(Alias alias, Expression path, Class type, Object value, ParameterBuilder parameterBuilder) { + var in = stream(value) .map(item -> createElementCollectionCriteria(type, item).getValue()) .filter(Objects::nonNull) .map(parameterBuilder::create) @@ -2103,13 +2082,13 @@ private Predicate buildInPredicate(Alias alias, Expression path, Class typ } private Predicate buildArrayPredicate(Expression path, Class type, String field, Object value, AbstractQuery query, CriteriaBuilder criteriaBuilder, PathResolver pathResolver, ParameterBuilder parameterBuilder) { - boolean oneToManyField = oneToManys.test(field); + var oneToManyField = oneToManys.test(field); if (oneToManyField && getProvider() == ECLIPSELINK) { throw new UnsupportedOperationException(ERROR_UNSUPPORTED_ONETOMANY_CRITERIA_ECLIPSELINK); // EclipseLink refuses to perform a JOIN when setFirstResult/setMaxResults is used. } - boolean elementCollectionField = elementCollections.contains(field); + var elementCollectionField = elementCollections.contains(field); Subquery subquery = null; Expression fieldPath; @@ -2125,7 +2104,7 @@ private Predicate buildArrayPredicate(Expression path, Class fieldPath = path; } - List predicates = stream(value) + var predicates = stream(value) .map(item -> elementCollectionField ? createElementCollectionCriteria(type, item).build(fieldPath, criteriaBuilder, parameterBuilder) : buildTypedPredicate(fieldPath, type, field, item, query, criteriaBuilder, pathResolver, parameterBuilder)) @@ -2136,7 +2115,7 @@ private Predicate buildArrayPredicate(Expression path, Class throw new IllegalArgumentException(value.toString()); } - Predicate predicate = criteriaBuilder.or(toArray(predicates)); + var predicate = criteriaBuilder.or(toArray(predicates)); if (subquery != null) { // SELECT e FROM E e WHERE (SELECT COUNT(DISTINCT field) FROM T t WHERE [restrictions] AND t.id = e.id) = ACTUALCOUNT @@ -2148,7 +2127,7 @@ private Predicate buildArrayPredicate(Expression path, Class } @SuppressWarnings("unchecked") - private Criteria createElementCollectionCriteria(Class type, Object value) { + private static Criteria createElementCollectionCriteria(Class type, Object value) { return type.isEnum() ? Enumerated.parse(value, (Class>) type) : IgnoreCase.value(value.toString()); } @@ -2164,7 +2143,7 @@ private Criteria createElementCollectionCriteria(Class type, Object value) @SuppressWarnings("unchecked") public static BaseEntityService getCurrentInstance() { try { - SessionContext ejbContext = (SessionContext) new InitialContext().lookup("java:comp/EJBContext"); + var ejbContext = (SessionContext) new InitialContext().lookup("java:comp/EJBContext"); return (BaseEntityService) ejbContext.getBusinessObject(ejbContext.getInvokedBusinessInterface()); } catch (Exception e) { @@ -2185,11 +2164,11 @@ private static Predicate conjunctRestrictionsIfNecessary(CriteriaBuilder criteri } private static Predicate buildCountPredicateIfNecessary(Predicate inPredicate, CriteriaBuilder criteriaBuilder, AbstractQuery query, PathResolver pathResolver) { - Entry fieldAndCount = Alias.getFieldAndCount(inPredicate); + var fieldAndCount = Alias.getFieldAndCount(inPredicate); if (fieldAndCount.getValue() > 1) { Expression join = pathResolver.get(pathResolver.join(fieldAndCount.getKey())); - Predicate countPredicate = criteriaBuilder.equal(criteriaBuilder.countDistinct(join), fieldAndCount.getValue()); + var countPredicate = criteriaBuilder.equal(criteriaBuilder.countDistinct(join), fieldAndCount.getValue()); Alias.setHaving(inPredicate, countPredicate); groupByIfNecessary(query, pathResolver.get(fieldAndCount.getKey())); return countPredicate; @@ -2199,10 +2178,10 @@ private static Predicate buildCountPredicateIfNecessary(Predicate inPredicate, C } private static void groupByIfNecessary(AbstractQuery query, Expression path) { - Expression groupByPath = (path instanceof RootWrapper) ? ((RootWrapper) path).getWrapped() : path; + var groupByPath = path instanceof RootWrapper rootWrapper ? rootWrapper.getWrapped() : path; if (!query.getGroupList().contains(groupByPath)) { - List> groupList = new ArrayList<>(query.getGroupList()); + var groupList = new ArrayList<>(query.getGroupList()); groupList.add(groupByPath); query.groupBy(groupList); } @@ -2213,8 +2192,8 @@ private static boolean hasJoins(From from) { } private static boolean hasFetches(From from) { - return from.getFetches().stream().anyMatch(fetch -> fetch instanceof Path) - || (from instanceof EclipseLinkRoot && ((EclipseLinkRoot) from).hasPostponedFetches()); + return from.getFetches().stream().anyMatch(Path.class::isInstance) + || from instanceof EclipseLinkRoot eclipseLinkRoot && eclipseLinkRoot.hasPostponedFetches(); } private static T noop() { diff --git a/src/test/java/org/omnifaces/persistence/test/OmniPersistenceTest.java b/src/test/java/org/omnifaces/persistence/test/OmniPersistenceTest.java index 922d3b2..f38fba4 100644 --- a/src/test/java/org/omnifaces/persistence/test/OmniPersistenceTest.java +++ b/src/test/java/org/omnifaces/persistence/test/OmniPersistenceTest.java @@ -16,14 +16,17 @@ import static java.lang.System.getenv; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.omnifaces.persistence.test.service.StartupService.TOTAL_PHONES_PER_PERSON_0; import static org.omnifaces.persistence.test.service.StartupService.TOTAL_RECORDS; import java.time.LocalDate; import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.Map; import jakarta.ejb.EJB; @@ -33,30 +36,27 @@ import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.resolver.api.maven.Maven; -import org.jboss.shrinkwrap.resolver.api.maven.MavenResolverSystem; import org.jboss.shrinkwrap.resolver.api.maven.archive.importer.MavenImporter; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.omnifaces.persistence.exception.IllegalEntityStateException; import org.omnifaces.persistence.exception.NonSoftDeletableEntityException; import org.omnifaces.persistence.model.dto.Page; -import org.omnifaces.persistence.test.model.Comment; import org.omnifaces.persistence.test.model.Gender; import org.omnifaces.persistence.test.model.Lookup; import org.omnifaces.persistence.test.model.Person; -import org.omnifaces.persistence.test.model.Text; import org.omnifaces.persistence.test.service.CommentService; import org.omnifaces.persistence.test.service.LookupService; import org.omnifaces.persistence.test.service.PersonService; +import org.omnifaces.persistence.test.service.PhoneService; import org.omnifaces.persistence.test.service.TextService; -import org.omnifaces.utils.collection.PartialResultList; @ExtendWith(ArquillianExtension.class) public class OmniPersistenceTest { @Deployment public static WebArchive createDeployment() { - MavenResolverSystem maven = Maven.resolver(); + var maven = Maven.resolver(); return create(WebArchive.class) .addPackages(true, OmniPersistenceTest.class.getPackage()) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") @@ -70,8 +70,11 @@ public static WebArchive createDeployment() { .addAsLibraries(maven.resolve("com.h2database:h2:" + getProperty("test.h2.version")).withTransitivity().asFile()); } - @EJB - private PersonService personService; + @EJB + private PersonService personService; + + @EJB + private PhoneService phoneService; @EJB private TextService textService; @@ -89,24 +92,24 @@ protected static boolean isEclipseLink() { // Basic ---------------------------------------------------------------------------------------------------------- @Test - public void testFindPerson() { - Optional existingPerson = personService.findById(1L); + void testFindPerson() { + var existingPerson = personService.findById(1L); assertTrue(existingPerson.isPresent(), "Existing person"); - Optional nonExistingPerson = personService.findById(0L); - assertTrue(!nonExistingPerson.isPresent(), "Non-existing person"); + var nonExistingPerson = personService.findById(0L); + assertFalse(nonExistingPerson.isPresent(), "Non-existing person"); } @Test - public void testGetPerson() { - Person existingPerson = personService.getById(1L); - assertTrue(existingPerson != null, "Existing person"); - Person nonExistingPerson = personService.getById(0L); - assertTrue(nonExistingPerson == null, "Non-existing person"); + void testGetPerson() { + var existingPerson = personService.getById(1L); + assertNotNull(existingPerson, "Existing person"); + var nonExistingPerson = personService.getById(0L); + assertNull(nonExistingPerson, "Non-existing person"); } @Test - public void testPersistAndDeleteNewPerson() { - Person newPerson = createNewPerson("testPersistNewPerson@example.com"); + void testPersistAndDeleteNewPerson() { + var newPerson = createNewPerson("testPersistNewPerson@example.com"); personService.persist(newPerson); Long expectedNewId = TOTAL_RECORDS + 1L; assertEquals(expectedNewId, newPerson.getId(), "New person ID"); @@ -117,53 +120,53 @@ public void testPersistAndDeleteNewPerson() { } @Test - public void testPersistExistingPerson() { - Person existingPerson = createNewPerson("testPersistExistingPerson@example.com"); + void testPersistExistingPerson() { + var existingPerson = createNewPerson("testPersistExistingPerson@example.com"); existingPerson.setId(1L); assertThrows(IllegalEntityStateException.class, () -> personService.persist(existingPerson)); } @Test - public void testUpdateExistingPerson() { - Person existingPerson = personService.getById(1L); - assertTrue(existingPerson != null, "Existing person"); - String newEmail = "testUpdateExistingPerson@example.com"; + void testUpdateExistingPerson() { + var existingPerson = personService.getById(1L); + assertNotNull(existingPerson, "Existing person"); + var newEmail = "testUpdateExistingPerson@example.com"; existingPerson.setEmail(newEmail); personService.update(existingPerson); - Person existingPersonAfterUpdate = personService.getById(1L); + var existingPersonAfterUpdate = personService.getById(1L); assertEquals(newEmail, existingPersonAfterUpdate.getEmail(), "Email updated"); } @Test - public void testUpdateNewPerson() { - Person newPerson = createNewPerson("testUpdateNewPerson@example.com"); + void testUpdateNewPerson() { + var newPerson = createNewPerson("testUpdateNewPerson@example.com"); assertThrows(IllegalEntityStateException.class, () -> personService.update(newPerson)); } @Test - public void testResetExistingPerson() { - Person existingPerson = personService.getById(1L); - assertTrue(existingPerson != null, "Existing person"); - String oldEmail = existingPerson.getEmail(); + void testResetExistingPerson() { + var existingPerson = personService.getById(1L); + assertNotNull(existingPerson, "Existing person"); + var oldEmail = existingPerson.getEmail(); existingPerson.setEmail("testResetExistingPerson@example.com"); personService.reset(existingPerson); assertEquals(oldEmail, existingPerson.getEmail(), "Email resetted"); } @Test - public void testResetNonExistingPerson() { - Person nonExistingPerson = createNewPerson("testResetNonExistingPerson@example.com"); + void testResetNonExistingPerson() { + var nonExistingPerson = createNewPerson("testResetNonExistingPerson@example.com"); assertThrows(IllegalEntityStateException.class, () -> personService.reset(nonExistingPerson)); } @Test - public void testDeleteNonExistingPerson() { - Person nonExistingPerson = createNewPerson("testDeleteNonExistingPerson@example.com"); + void testDeleteNonExistingPerson() { + var nonExistingPerson = createNewPerson("testDeleteNonExistingPerson@example.com"); assertThrows(IllegalEntityStateException.class, () -> personService.delete(nonExistingPerson)); } private static Person createNewPerson(String email) { - Person person = new Person(); + var person = new Person(); person.setEmail(email); person.setGender(Gender.OTHER); person.setDateOfBirth(LocalDate.now()); @@ -174,133 +177,140 @@ private static Person createNewPerson(String email) { // Page ----------------------------------------------------------------------------------------------------------- @Test - public void testPage() { - PartialResultList persons = personService.getPage(Page.ALL, true); + void testPage() { + var persons = personService.getPage(Page.ALL, true); assertEquals(TOTAL_RECORDS, persons.size(), "There are 200 records"); - PartialResultList males = personService.getPage(Page.with().anyMatch(Collections.singletonMap("gender", Gender.MALE)).build(), true); + var males = personService.getPage(Page.with().anyMatch(Collections.singletonMap("gender", Gender.MALE)).build(), true); assertTrue(males.size() < TOTAL_RECORDS, "There are less than 200 records"); } + @Test + void testPageByLazyManyToOne() { // This was failing since Hibernate 6 upgrade. + var person = personService.getById(1L); + var phones = phoneService.getPage(Page.with().allMatch(Map.of("owner", person)).build(), true); + assertEquals(TOTAL_PHONES_PER_PERSON_0, phones.size(), "There are 3 phones"); + } + // @SoftDeletable ------------------------------------------------------------------------------------------------- @Test - public void testSoftDelete() { - List allTexts = textService.list(); - List allComments = commentService.list(); + void testSoftDelete() { + var allTexts = textService.list(); + var allComments = commentService.list(); - Text activeText = textService.getById(1L); + var activeText = textService.getById(1L); textService.softDelete(activeText); - Text activeTextAfterSoftDelete = textService.getSoftDeletedById(1L); - assertTrue(!activeTextAfterSoftDelete.isActive(), "Text entity was soft deleted"); - assertEquals(textService.list().size(), allTexts.size() - 1, "Total records for texts"); - assertEquals(textService.listSoftDeleted().size(), 1, "Total deleted records for texts"); + var activeTextAfterSoftDelete = textService.getSoftDeletedById(1L); + assertFalse(activeTextAfterSoftDelete.isActive(), "Text entity was soft deleted"); + assertEquals(allTexts.size() - 1, textService.list().size(), "Total records for texts"); + assertEquals(1, textService.listSoftDeleted().size(), "Total deleted records for texts"); - Comment activeComment = commentService.getById(1L); + var activeComment = commentService.getById(1L); commentService.softDelete(activeComment); - Comment activeCommentAfterSoftDelete = commentService.getSoftDeletedById(1L); + var activeCommentAfterSoftDelete = commentService.getSoftDeletedById(1L); assertTrue(activeCommentAfterSoftDelete.isDeleted(), "Comment entity was soft deleted"); - assertEquals(commentService.list().size(), allComments.size() - 1, "Total records for comments"); - assertEquals(commentService.listSoftDeleted().size(), 1, "Total deleted records for comments"); + assertEquals(allComments.size() - 1, commentService.list().size(), "Total records for comments"); + assertEquals(1, commentService.listSoftDeleted().size(), "Total deleted records for comments"); - Text deletedText = textService.getSoftDeletedById(1L); + var deletedText = textService.getSoftDeletedById(1L); textService.softUndelete(deletedText); - Text deletedTextAfterSoftUndelete = textService.getById(1L); + var deletedTextAfterSoftUndelete = textService.getById(1L); assertTrue(deletedTextAfterSoftUndelete.isActive(), "Text entity was soft undeleted"); - assertEquals(textService.list().size(), allTexts.size(), "Total records for texts"); - assertEquals(textService.listSoftDeleted().size(), 0, "Total deleted records for texts"); + assertEquals(allTexts.size(), textService.list().size(), "Total records for texts"); + assertEquals(0, textService.listSoftDeleted().size(), "Total deleted records for texts"); - Comment deletedComment = commentService.getSoftDeletedById(1L); + var deletedComment = commentService.getSoftDeletedById(1L); commentService.softUndelete(deletedComment); - Comment deletedCommentAfterSoftUndelete = commentService.getById(1L); - assertTrue(!deletedCommentAfterSoftUndelete.isDeleted(), "Comment entity was soft undeleted"); - assertEquals(commentService.list().size(), allComments.size(), "Total records for comments"); - assertEquals(commentService.listSoftDeleted().size(), 0, "Total deleted records for comments"); + var deletedCommentAfterSoftUndelete = commentService.getById(1L); + assertFalse(deletedCommentAfterSoftUndelete.isDeleted(), "Comment entity was soft undeleted"); + assertEquals(allComments.size(), commentService.list().size(), "Total records for comments"); + assertEquals(0, commentService.listSoftDeleted().size(), "Total deleted records for comments"); textService.softDelete(allTexts); - assertEquals(textService.list().size(), 0, "Total records for texts"); - assertEquals(textService.listSoftDeleted().size(), allTexts.size(), "Total deleted records for texts"); + assertEquals(0, textService.list().size(), "Total records for texts"); + assertEquals(allTexts.size(), textService.listSoftDeleted().size(), "Total deleted records for texts"); commentService.softDelete(allComments); - assertEquals(commentService.list().size(), 0, "Total records for comments"); - assertEquals(commentService.listSoftDeleted().size(), allComments.size(), "Total deleted records for comments"); + assertEquals(0, commentService.list().size(), "Total records for comments"); + assertEquals(allComments.size(), commentService.listSoftDeleted().size(), "Total deleted records for comments"); } @Test - public void testGetAllSoftDeletedForNonSoftDeletable() { + void testGetAllSoftDeletedForNonSoftDeletable() { assertThrows(NonSoftDeletableEntityException.class, () -> personService.listSoftDeleted()); } @Test - public void testSoftDeleteNonSoftDeletable() { - Person person = personService.getById(1L); + void testSoftDeleteNonSoftDeletable() { + var person = personService.getById(1L); assertThrows(NonSoftDeletableEntityException.class, () -> personService.softDelete(person)); } @Test - public void testSoftUndeleteNonSoftDeletable() { - Person person = personService.getById(1L); + void testSoftUndeleteNonSoftDeletable() { + var person = personService.getById(1L); assertThrows(NonSoftDeletableEntityException.class, () -> personService.softUndelete(person)); } @Test - public void testGetSoftDeletableById() { + void testGetSoftDeletableById() { lookupService.persist(new Lookup("aa")); - Lookup activeLookup = lookupService.getById("aa"); - assertTrue(activeLookup != null, "Got active entity with getById method"); + var activeLookup = lookupService.getById("aa"); + assertNotNull(activeLookup, "Got active entity with getById method"); lookupService.softDelete(activeLookup); - Lookup softDeletedLookup = lookupService.getById("aa"); - assertTrue(softDeletedLookup == null, "Not able to get deleted entity with getById method"); + var softDeletedLookup = lookupService.getById("aa"); + assertNull(softDeletedLookup, "Not able to get deleted entity with getById method"); softDeletedLookup = lookupService.getSoftDeletedById("aa"); - assertTrue(softDeletedLookup != null, "Got deleted entity with getSoftDeletedById method"); + assertNotNull(softDeletedLookup, "Got deleted entity with getSoftDeletedById method"); } @Test - public void testFindSoftDeletableById() { + void testFindSoftDeletableById() { lookupService.persist(new Lookup("bb")); - Optional activeLookup = lookupService.findById("bb"); + var activeLookup = lookupService.findById("bb"); assertTrue(activeLookup.isPresent(), "Got active entity with findById method"); lookupService.softDelete(activeLookup.get()); - Optional softDeletedLookup = lookupService.findById("bb"); - assertTrue(!softDeletedLookup.isPresent(), "Not able to get deleted entity with findById method"); + var softDeletedLookup = lookupService.findById("bb"); + assertFalse(softDeletedLookup.isPresent(), "Not able to get deleted entity with findById method"); softDeletedLookup = lookupService.findSoftDeletedById("bb"); assertTrue(softDeletedLookup.isPresent(), "Got deleted entity with findSoftDeletedById method"); } @Test - public void testSave() { - Lookup lookup = new Lookup("cc"); + void testSave() { + var lookup = new Lookup("cc"); lookupService.save(lookup); - Lookup persistedLookup = lookupService.getById("cc"); - assertTrue(persistedLookup != null, "New entity was persisted with save method"); + var persistedLookup = lookupService.getById("cc"); + assertNotNull(persistedLookup, "New entity was persisted with save method"); persistedLookup.setActive(false); lookupService.save(persistedLookup); persistedLookup = lookupService.getSoftDeletedById("cc"); - assertTrue(persistedLookup != null && !persistedLookup.isActive(), "Entity was merged with save method"); + assertFalse(persistedLookup.isActive(), "Entity was merged with save method"); persistedLookup.setActive(true); lookupService.update(persistedLookup); persistedLookup = lookupService.getById("cc"); - assertTrue(persistedLookup != null && persistedLookup.isActive(), "Entity was merged with update method"); + assertTrue(persistedLookup.isActive(), "Entity was merged with update method"); } @Test - public void testPersistExistingLookup() { - Lookup lookup = new Lookup("dd"); + void testPersistExistingLookup() { + var lookup = new Lookup("dd"); lookupService.save(lookup); - Lookup persistedLookup = lookupService.getById("dd"); + var persistedLookup = lookupService.getById("dd"); persistedLookup.setActive(false); assertThrows(IllegalEntityStateException.class, () -> lookupService.persist(lookup)); } @Test - public void testUpdateNewLookup() { - Lookup lookup = new Lookup("ee"); + void testUpdateNewLookup() { + var lookup = new Lookup("ee"); assertThrows(IllegalEntityStateException.class, () -> lookupService.update(lookup)); } diff --git a/src/test/java/org/omnifaces/persistence/test/service/StartupService.java b/src/test/java/org/omnifaces/persistence/test/service/StartupService.java index ee99df7..78f6ca7 100644 --- a/src/test/java/org/omnifaces/persistence/test/service/StartupService.java +++ b/src/test/java/org/omnifaces/persistence/test/service/StartupService.java @@ -17,7 +17,6 @@ import java.time.LocalDate; import java.util.Arrays; import java.util.Collections; -import java.util.List; import java.util.concurrent.ThreadLocalRandom; import jakarta.annotation.PostConstruct; @@ -39,6 +38,7 @@ public class StartupService { public static final int TOTAL_RECORDS = 200; public static final int ROWS_PER_PAGE = 10; + public static final int TOTAL_PHONES_PER_PERSON_0 = 3; @Inject private TextService textService; @@ -57,18 +57,18 @@ public void init() { } private void createTestPersons() { - Gender[] genders = Gender.values(); - Phone.Type[] phoneTypes = Phone.Type.values(); - List groups = Arrays.asList(Group.values()); - ThreadLocalRandom random = ThreadLocalRandom.current(); + var genders = Gender.values(); + var phoneTypes = Phone.Type.values(); + var groups = Arrays.asList(Group.values()); + var random = ThreadLocalRandom.current(); - for (int i = 0; i < TOTAL_RECORDS; i++) { - Person person = new Person(); + for (var i = 0; i < TOTAL_RECORDS; i++) { + var person = new Person(); person.setEmail("name" + i + "@example.com"); person.setGender(genders[random.nextInt(genders.length)]); person.setDateOfBirth(LocalDate.ofEpochDay(random.nextLong(LocalDate.of(1900, 1, 1).toEpochDay(), LocalDate.of(2000, 1, 1).toEpochDay()))); - Address address = new Address(); + var address = new Address(); address.setStreet("Street" + i); address.setHouseNumber("" + i); address.setPostcode("Postcode" + i); @@ -76,9 +76,9 @@ private void createTestPersons() { address.setCountry("Country" + i); person.setAddress(address); - int totalPhones = random.nextInt(1, 6); - for (int j = 0; j < totalPhones; j++) { - Phone phone = new Phone(); + var totalPhones = i == 0 ? TOTAL_PHONES_PER_PERSON_0 : random.nextInt(1, 6); + for (var j = 0; j < totalPhones; j++) { + var phone = new Phone(); phone.setType(phoneTypes[random.nextInt(phoneTypes.length)]); phone.setNumber("0" + abs(random.nextInt())); phone.setOwner(person);