public static boolean containsRelation(Object expr) { if (expr instanceof Join) { return true; } else if (expr instanceof SingularAttribute) { SingularAttribute<?, ?> attr = (SingularAttribute<?, ?>) expr; return attr.isAssociation(); } else if (expr instanceof Path) { Path<?> attrPath = (Path<?>) expr; Bindable<?> model = attrPath.getModel(); Path<?> parent = attrPath.getParentPath(); return containsRelation(parent) || containsRelation(model); } else { // we may can do better here... return false; } }
private Map<String, String> getReferenceColumnNamesMapForReferenceAttribute(SingularAttribute<?, ?> attribute, ManagedType<?> targetEntity) { List<String> idAttributeNames = targetEntity.getSingularAttributes().stream() .filter(this::isIdAttribute) .map(this::getSingularAttributeColumnName) .collect(Collectors.toList()); JoinColumns joinColumnsAnnotation = ((AnnotatedElement) attribute.getJavaMember()).getAnnotation(JoinColumns.class); JoinColumn joinColumnAnnotation = ((AnnotatedElement) attribute.getJavaMember()).getAnnotation(JoinColumn.class); JoinColumn[] joinColumns = joinColumnsAnnotation != null ? joinColumnsAnnotation.value() : joinColumnAnnotation != null ? new JoinColumn[]{joinColumnAnnotation} : null; Map<String, String> referenceColumnNamesMap; if (joinColumns != null) { referenceColumnNamesMap = Arrays.stream(joinColumns) .collect(Collectors.toMap(JoinColumn::name, joinColumn -> joinColumn.referencedColumnName().length() > 0 ? joinColumn.referencedColumnName() : idAttributeNames.get(0))); } else { referenceColumnNamesMap = idAttributeNames.stream() .collect(Collectors.toMap(idAttributeName -> attribute.getName().toUpperCase() + "_" + idAttributeName, idAttributeName -> idAttributeName)); } return referenceColumnNamesMap; }
public static Set<String> getIdFieldNames(Metamodel metamodel, String entityName) { Set<String> ids = entityIdMap.get(entityName); if(ids == null) { ids = new HashSet<>(); for(EntityType<?> et : metamodel.getEntities()) { if(et.getJavaType().getName().equals(entityName)) { if(et.hasSingleIdAttribute()) { ids.add(et.getId(et.getIdType().getJavaType()).getName()); } else { for(SingularAttribute<?, ?> idAttribute : et.getIdClassAttributes()) { ids.add(idAttribute.getName()); } } } } } if(ids.size() == 0) { ids.add("id"); } return ids; }
@SuppressWarnings("rawtypes") public LinqImpl(Class<?> domainClass, Class<?> resultClass, EntityManager entityManager) { super(domainClass, entityManager); if (Tuple.class.isAssignableFrom(resultClass)) { criteria = cb.createTupleQuery(); root = criteria.from(domainClass); } else if (Map.class.isAssignableFrom(resultClass)) { criteria = cb.createQuery(Object[].class); root = criteria.from(domainClass); resultTransformer = Transformers.ALIAS_TO_MAP; Set<?> attrs = em.getMetamodel().entity(domainClass).getDeclaredSingularAttributes(); String[] selections = new String[attrs.size()]; int i = 0; for (Object attr : attrs) { selections[i] = ((SingularAttribute)attr).getName(); i++; } select(selections); } else { criteria = cb.createQuery(resultClass); root = criteria.from(domainClass); } this.resultClass = resultClass; }
/** * Return JPA managed properties. * * @param <T> * Bean type. * @param beanType * the bean type. * @return the headers built from given type. */ public <T> String[] getJpaHeaders(final Class<T> beanType) { // Build descriptor list respecting the declaration order final OrderedFieldCallback fieldCallBack = new OrderedFieldCallback(); ReflectionUtils.doWithFields(beanType, fieldCallBack); final List<String> orderedDescriptors = fieldCallBack.descriptorsOrdered; // Now filter the properties final List<String> descriptorsFiltered = new ArrayList<>(); final ManagedType<T> managedType = transactionManager.getEntityManagerFactory().getMetamodel().managedType(beanType); for (final String propertyDescriptor : orderedDescriptors) { for (final Attribute<?, ?> attribute : managedType.getAttributes()) { // Match only basic attributes if (attribute instanceof SingularAttribute<?, ?> && propertyDescriptor.equals(attribute.getName())) { descriptorsFiltered.add(attribute.getName()); break; } } } // Initialize the CSV reader return descriptorsFiltered.toArray(new String[descriptorsFiltered.size()]); }
public static <T, U> BiConsumer<T, U> createSetterInvoker( @Nonnull final SingularAttribute<? super T, U> attribute) { Objects.requireNonNull(attribute); final Class<?> declaringClass = attribute.getDeclaringType().getJavaType(); final String setterName = getSetterName(attribute.getName()); final Method setterMethod = BeanUtils.findDeclaredMethod(declaringClass, setterName, attribute.getJavaType()); if (setterMethod == null) { throw new IllegalStateException(String.format( "Class %s does not declare method named '%s'", declaringClass.getName(), setterName)); } setterMethod.setAccessible(true); return (object, value) -> { try { setterMethod.invoke(object, value); } catch (final IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } }; }
private void testGetObservationFieldMetadataForSingleSpecies_forState( final SingularAttribute<ObservationContextSensitiveFields, Required> fieldAttribute, final Required stateRequirement, final ObservedGameState expectedStateValue) { final GameSpecies species = newGameSpecies(); final ObservationContextSensitiveFields ctxFields = newObservationContextSensitiveFields(species, Required.NO, Required.VOLUNTARY); CriteriaUtils.createSetterInvoker(fieldAttribute).accept(ctxFields, stateRequirement); persistInCurrentlyOpenTransaction(); final ContextSensitiveFieldSetDTO ctxFieldsDTO = invokeServiceAndGetContextSensitiveFieldSetDTO(species, ctxFields.getObservationType()); assertEquals(stateRequirement, ctxFieldsDTO.getState()); assertEquals(EnumSet.of(HEALTHY, ILL, expectedStateValue), ctxFieldsDTO.getAllowedStates()); }
private Class<?> getSelectedType(Path entityPath, Set<TypeDefinition> typeDefinitions) { if (entityPath.isKeyPath()) { TypeDefinition typeDefinition = typeForAlias(entityPath.getRootAlias()) .withMetamodel(metamodel) .filter(typeDefinitions); MapAttribute<?, ?, ?> mapAttribute = (MapAttribute<?, ?, ?>)attributeForPath(typeDefinition.getJoinPath()) .withMetamodel(metamodel) .filter(typeDefinitions); Class<?> keyType = mapAttribute.getKeyJavaType(); if (!entityPath.hasSubpath()) { return keyType; } return attributeForPath(new Path(entityPath.getSubpath())) .withMetamodel(metamodel) .withRootType(keyType) .filter() .getJavaType(); } else if (entityPath.hasSubpath()) { SingularAttribute<?, ?> attribute = (SingularAttribute<?, ?>)attributeForPath(entityPath) .withMetamodel(metamodel) .filter(typeDefinitions); return attribute.getType().getJavaType(); } else { return typeForAlias(entityPath.getRootAlias()).withMetamodel(metamodel).filter(typeDefinitions).getType(); } }
public boolean visit(JpqlPath node, Set<TypeDefinition> typeDefinitions) { Alias alias = new Alias(node.jjtGetChild(0).getValue()); Class<?> type = getType(alias, typeDefinitions); for (int i = 1; i < node.jjtGetNumChildren(); i++) { ManagedType<?> managedType = forModel(metamodel).filter(type); String attributeName = node.jjtGetChild(i).getValue(); Attribute<?, ?> attribute = managedType.getAttribute(attributeName); if (attribute instanceof SingularAttribute && ((SingularAttribute<?, ?>)attribute).getType().getPersistenceType() == PersistenceType.BASIC && i < node.jjtGetNumChildren() - 1) { String error = "Cannot navigate through simple property " + attributeName + " in class " + type.getName(); throw new PersistenceException(error); } type = attribute.getJavaType(); } return false; }
public Attribute<?, ?> filter() { Type<?> type = forModel(metamodel).filter(rootType); Attribute<?, ?> result = null; for (int i = 1; i < pathElements.length; i++) { if (!(type instanceof ManagedType)) { throw new PersistenceException("Cannot navigate through simple property " + pathElements[i] + " of type " + type.getJavaType()); } result = ((ManagedType<?>)type).getAttribute(pathElements[i]); if (result.isCollection()) { type = ((PluralAttribute<?, ?, ?>)result).getElementType(); } else { type = ((SingularAttribute<?, ?>)result).getType(); } } return result; }
/** * Invoke byExample method for each not null x-to-one association when their pk is not set. This allows you to search entities based on an associated * entity's properties value. */ @SuppressWarnings("unchecked") public <T extends Identifiable<?>, M2O extends Identifiable<?>> List<Predicate> byExampleOnXToOne(ManagedType<T> mt, Root<T> mtPath, final T mtValue, SearchParameters sp, CriteriaBuilder builder) { List<Predicate> predicates = newArrayList(); for (SingularAttribute<? super T, ?> attr : mt.getSingularAttributes()) { if (attr.getPersistentAttributeType() == MANY_TO_ONE || attr.getPersistentAttributeType() == ONE_TO_ONE) { // M2O m2oValue = (M2O) getValue(mtValue, mt.getAttribute(attr.getName())); if (m2oValue != null && !m2oValue.isIdSet()) { Class<M2O> m2oType = (Class<M2O>) attr.getBindableJavaType(); ManagedType<M2O> m2oMt = em.getMetamodel().entity(m2oType); Path<M2O> m2oPath = (Path<M2O>) mtPath.get(attr); predicates.addAll(byExample(m2oMt, m2oPath, m2oValue, sp, builder)); } } } return predicates; }
@Override public <S> S sum(Class<S> resultClass, Specification<E> spec, LockModeType lockMode, List<SingularAttribute<E, ? extends Number>> properties) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<S> query = builder.createQuery(resultClass); Root<E> root = query.from(getDomainClass()); List<Selection<?>> selectionList = Lists.newArrayList(); for (SingularAttribute<E, ? extends Number> property : properties) { selectionList.add(builder.sum(root.get(property))); } return aggregate(builder, query, root, spec, selectionList, lockMode); }
@Override @SuppressWarnings("unchecked") @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public <S> S sum(Class<S> resultClass, Specification<E> spec, SingularAttribute<E, ? extends Number>... properties) { return getRepository().sum(resultClass, spec, properties); }
private String getSingularAttributeColumnName(SingularAttribute attribute) { Column columnAnnotation = ((AnnotatedElement) attribute.getJavaMember()).getAnnotation(Column.class); if (columnAnnotation != null && columnAnnotation.name().length() > 0) { return columnAnnotation.name().toUpperCase(); } else { return attribute.getName().toUpperCase(); } }
@Override public String getCanonicalDataTypeName(schemacrawler.schema.Column column) { SingularAttribute singularAttribute; if (column.isPartOfForeignKey()) { Optional<ForeignKeyColumnReference> foreignKeyColumnReferenceOptional = column.getParent().getForeignKeys().stream() .flatMap(foreignKeyColumnReferences -> foreignKeyColumnReferences.getColumnReferences().stream()) .filter(foreignKeyColumnReference -> foreignKeyColumnReference.getForeignKeyColumn().getName().equals(column.getName())) .findFirst(); if (foreignKeyColumnReferenceOptional.isPresent()) { ForeignKeyColumnReference ref = foreignKeyColumnReferenceOptional.get(); SingularAttribute targetSingularAttribute = singularAttributesByColumnName.get(new QualifiedColumnName(ref.getPrimaryKeyColumn().getParent().getName(), ref.getPrimaryKeyColumn().getName())); if (targetSingularAttribute != null) { return targetSingularAttribute.getJavaType().getCanonicalName(); } else { LOG.warn("Could not find target singular attribute for column " + column.getParent().getName() + "." + column.getName()); return fallbackDataTypeProvider.getCanonicalDataTypeName(column); } } else { return fallbackDataTypeProvider.getCanonicalDataTypeName(column); } } else { singularAttribute = singularAttributesByColumnName.get(new QualifiedColumnName(column.getParent().getName(), column.getName())); if (singularAttribute != null) { return singularAttribute.getJavaType().getCanonicalName(); } else { return fallbackDataTypeProvider.getCanonicalDataTypeName(column); } } }
@Override public <Y, X extends Y> Linu set(SingularAttribute<? super Object, Y> attribute, X value) { if (!beforeMethodInvoke()) { return this; } criteria.set(attribute, value);; return this; }
@Override public <Y> Linu set(SingularAttribute<? super Object, Y> attribute, Expression<? extends Y> value) { if (!beforeMethodInvoke()) { return this; } criteria.set(attribute, value); return this; }
@Nonnull public static <T, U> Specification<T> equalValueWithHarvestOrObservation( @Nonnull final SingularAttribute<? super T, Harvest> harvestAttribute, @Nonnull final SingularAttribute<? super T, Observation> observationAttribute, @Nonnull final SingularAttribute<? super GameDiaryEntry, U> valueAttribute, @Nullable final U value) { Objects.requireNonNull(harvestAttribute, "harvestAttribute must not be null"); Objects.requireNonNull(observationAttribute, "observationAttribute must not be null"); Objects.requireNonNull(valueAttribute, "valueAttribute must not be null"); return (root, query, cb) -> cb.or( JpaPreds.equal(cb, root.join(harvestAttribute, JoinType.LEFT).get(valueAttribute), value), JpaPreds.equal(cb, root.join(observationAttribute, JoinType.LEFT).get(valueAttribute), value)); }
@Nonnull public static <P, S> JpaSubQuery<P, S> inverseOf(@Nonnull final SingularAttribute<S, P> attribute) { return new AbstractReverseSubQuery<P, S, SingularAttribute<S, P>>(Objects.requireNonNull(attribute)) { @Override protected Path<P> getPathToParentRoot(final Root<S> root) { return root.get(attribute); } }; }
@Nullable @Override public Function<?, ?> load(@Nullable final SingularAttribute<?, ?> key) { if (key == null) { return null; } @SuppressWarnings("rawtypes") final SingularAttribute attribute = key; @SuppressWarnings("unchecked") final Function<?, ?> result = jpaPropertyInternal(attribute); return result; }
@Nonnull static <T, U> Function<? super T, U> jpaProperty(@Nonnull final SingularAttribute<? super T, U> attribute) { Objects.requireNonNull(attribute); try { @SuppressWarnings("unchecked") final Function<? super T, U> result = (Function<? super T, U>) JPA_PROPERTY_FUNCTIONS.get(attribute); return result; } catch (final ExecutionException e) { throw new RuntimeException(e); } }
@Nonnull public static <ID extends Serializable, T extends Persistable<ID> & HasID<ID>, U> Function<T, Long> createAssociationCountFunction( @Nonnull final Iterable<? extends T> collection, @Nonnull final Class<U> associatedClass, @Nonnull final SingularAttribute<? super U, T> associationAttribute, @Nonnull final EntityManager entityManager) { Objects.requireNonNull(collection, "collection is null"); Objects.requireNonNull(associatedClass, "associatedClass is null"); Objects.requireNonNull(associationAttribute, "associationAttribute is null"); Objects.requireNonNull(entityManager, "entityManager is null"); if (Iterables.isEmpty(collection)) { return t -> 0L; } final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaQuery<Object> query = cb.createQuery(); final Root<U> root = query.from(associatedClass); final EntityType<T> targetEntityType = entityManager.getMetamodel().entity(associationAttribute.getJavaType()); final Path<ID> targetEntityIdPath = root.get(associationAttribute).get(getSingularIdAttribute(targetEntityType)); query.select(cb.construct(Tuple2.class, targetEntityIdPath, cb.count(root))) .where(targetEntityIdPath.in(cb.literal(F.getUniqueIds(collection)))) .groupBy(targetEntityIdPath); @SuppressWarnings({ "rawtypes", "unchecked" }) final List<Tuple2<ID, Long>> pairs = (List) entityManager.createQuery(query).getResultList(); if (pairs.isEmpty()) { return t -> 0L; } final HashMap<ID, Long> idToCount = HashMap.ofEntries(pairs); return t -> idToCount.get(F.getId(t)).getOrElse(0L); }
@Nonnull private static <T, U> Function<T, U> jpaPropertyInternal(@Nonnull final SingularAttribute<? super T, U> attr) { Objects.requireNonNull(attr); final Method readMethod = BeanUtils.getPropertyDescriptor(attr.getDeclaringType().getJavaType(), attr.getName()) .getReadMethod(); return obj -> invokeAndCast(readMethod, obj, attr.getJavaType()); }
private static <ID extends Serializable, T extends Persistable<ID>> SingularAttribute<? super T, ID> getSingularIdAttribute( final EntityType<T> entityType) { if (!entityType.hasSingleIdAttribute()) { throw new UnsupportedOperationException("Multi-attribute-ID not supported."); } @SuppressWarnings("unchecked") final Class<ID> idClass = (Class<ID>) entityType.getIdType().getJavaType(); return entityType.getId(idClass); }
@Nonnull public static <T, U> Map<T, List<U>> groupRelations( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository) { return groupRelations(objects, singularAttribute, repository, null, null); }
@Nonnull public static <T, U> Map<T, List<U>> groupRelations( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Specification<U> constraint) { return groupRelations(objects, singularAttribute, repository, constraint, null); }
@Nonnull public static <T, U> Map<T, List<U>> groupRelations( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Sort sortCriteria) { return groupRelations(objects, singularAttribute, repository, null, sortCriteria); }
@Nonnull public static <T, U> Map<T, List<U>> groupRelations( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Specification<U> constraint, @Nullable final Sort sortCriteria) { final Specification<U> inSpec = inCollection(singularAttribute, objects); return groupRelationsInternal( objects, inSpec, repository, CriteriaUtils.jpaProperty(singularAttribute), constraint, sortCriteria); }
@Nonnull public static <ID extends Serializable, T extends HasID<ID>, U> Map<ID, List<U>> groupRelationsById( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Specification<U> constraint) { return groupRelationsById(objects, singularAttribute, repository, constraint, null); }
@Nonnull public static <ID extends Serializable, T extends HasID<ID>, U> Map<ID, List<U>> groupRelationsById( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Specification<U> constraint, @Nullable final Sort sortCriteria) { final Specification<U> inSpec = inCollection(singularAttribute, objects); final Function<U, ID> idFunction = Functions.idOf(CriteriaUtils.jpaProperty(singularAttribute)); return groupRelationsInternal(objects, inSpec, repository, idFunction, constraint, sortCriteria); }
@Nonnull public static <ID extends Serializable, T extends HasID<ID>, U> Map<ID, List<U>> groupRelationsById( @Nonnull final Collection<ID> ids, @Nonnull final SingularAttribute<? super U, T> associationAttribute, @Nonnull final SingularAttribute<? super T, ID> idAttribute, @Nonnull final JpaSpecificationExecutor<U> repository) { return groupRelationsById(ids, associationAttribute, idAttribute, repository, null, null); }
@Nonnull public static <ID extends Serializable, T extends HasID<ID>, U> Map<ID, List<U>> groupRelationsById( @Nonnull final Collection<ID> ids, @Nonnull final SingularAttribute<? super U, T> associationAttribute, @Nonnull final SingularAttribute<? super T, ID> idAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Specification<U> constraint) { return groupRelationsById(ids, associationAttribute, idAttribute, repository, constraint, null); }
@Nonnull public static <ID extends Serializable, T extends HasID<ID>, U> Map<ID, List<U>> groupRelationsById( @Nonnull final Collection<ID> ids, @Nonnull final SingularAttribute<? super U, T> associationAttribute, @Nonnull final SingularAttribute<? super T, ID> idAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Specification<U> constraint, @Nullable final Sort sortCriteria) { final Specification<U> inSpec = inIdCollection(associationAttribute, idAttribute, ids); final Function<U, ID> idFunction = Functions.idOf(CriteriaUtils.jpaProperty(associationAttribute)); return groupRelationsInternal(ids, inSpec, repository, idFunction, constraint, sortCriteria); }
public static <ID extends Serializable, T extends HasID<ID>, U> Function<T, List<U>> createInverseMappingFunction( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, final boolean letFunctionThrowExceptionOnEmptyResult) { return createInverseMappingFunction( objects, singularAttribute, repository, null, letFunctionThrowExceptionOnEmptyResult); }
public static <ID extends Serializable, T extends HasID<ID>, U> Function<T, List<U>> createInverseMappingFunction( @Nonnull final Collection<? extends T> objects, @Nonnull final SingularAttribute<? super U, T> singularAttribute, @Nonnull final JpaSpecificationExecutor<U> repository, @Nullable final Specification<U> constraint, final boolean letFunctionThrowExceptionOnEmptyResult) { Objects.requireNonNull(objects, "objects must not be null"); Objects.requireNonNull(singularAttribute, "singularAttribute must not be null"); Objects.requireNonNull(repository, "repository must not be null"); if (Iterables.isEmpty(objects)) { return input -> Collections.emptyList(); } final Specification<U> relationSpec = inCollection(singularAttribute, objects); final Specification<U> compoundSpec = constraint != null ? JpaSpecs.and(relationSpec, constraint) : relationSpec; final List<U> relatedObjects = repository.findAll(compoundSpec); if (relatedObjects.isEmpty()) { return input -> Collections.emptyList(); } final Map<ID, List<U>> index = F.groupByIdAfterTransform(relatedObjects, CriteriaUtils.jpaProperty(singularAttribute)); final Function<ID, List<U>> indexFunc = letFunctionThrowExceptionOnEmptyResult ? forMap(index) : forMap(index, null); return indexFunc.compose(F::getId); }
@Nonnull public static <ID extends Serializable, T extends HasID<ID>> Specification<T> withId( @Nonnull final SingularAttribute<? super T, ID> attribute, @Nonnull final ID id) { Objects.requireNonNull(attribute, "attribute must not be null"); Objects.requireNonNull(id, "id must not be null"); return (root, query, cb) -> cb.equal(root.get(attribute), id); }
@Nonnull public static <T, U> Specification<T> equal( @Nonnull final SingularAttribute<? super T, U> attribute, @Nullable final U value) { Objects.requireNonNull(attribute, "attribute must not be null"); return (root, query, cb) -> JpaPreds.equal(cb, root.get(attribute), value); }
@Nonnull public static <T, X, Y> Specification<T> equal( @Nonnull final SingularAttribute<? super T, X> attribute1, @Nonnull final SingularAttribute<? super X, Y> attribute2, @Nullable final Y value) { Objects.requireNonNull(attribute1, "attribute1 must not be null"); Objects.requireNonNull(attribute2, "attribute2 must not be null"); return (root, query, cb) -> JpaPreds.equal(cb, root.join(attribute1).get(attribute2), value); }
@Nonnull public static <T, X, Y> Specification<T> equal( @Nonnull final PluralAttribute<? super T, ?, X> attribute1, @Nonnull final SingularAttribute<? super X, Y> attribute2, @Nullable final Y value) { Objects.requireNonNull(attribute1, "attribute1 must not be null"); Objects.requireNonNull(attribute2, "attribute2 must not be null"); return (root, query, cb) -> JpaPreds.equal(cb, CriteriaUtils.join(root, attribute1).get(attribute2), value); }
@Nonnull public static <T, X, Y, Z> Specification<T> equal( @Nonnull final SingularAttribute<? super T, X> attribute1, @Nonnull final SingularAttribute<? super X, Y> attribute2, @Nonnull final SingularAttribute<? super Y, Z> attribute3, @Nullable final Z value) { Objects.requireNonNull(attribute1, "attribute1 must not be null"); Objects.requireNonNull(attribute2, "attribute2 must not be null"); Objects.requireNonNull(attribute3, "attribute3 must not be null"); return (root, query, cb) -> JpaPreds.equal(cb, root.join(attribute1).join(attribute2).get(attribute3), value); }