下面列出了怎么用javax.persistence.criteria.SetJoin的API类实例代码及写法,或者点击链接到github查看源代码。
@Test
public void getBooks() {
log.info("... getBooks ...");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> cq = cb.createQuery(Book.class);
Root<Book> root = cq.from(Book.class);
SetJoin<Book, Author> authors = root.join(Book_.authors);
ParameterExpression<String> paramFirstName = cb.parameter(String.class);
ParameterExpression<String> paramLastName = cb.parameter(String.class);
cq.where(
cb.and(
cb.equal(authors.get(Author_.firstName), paramFirstName),
cb.equal(authors.get(Author_.lastName), paramLastName)));
TypedQuery<Book> query = em.createQuery(cq);
query.setParameter(paramFirstName, "Thorben");
query.setParameter(paramLastName, "Janssen");
List<Book> books = query.getResultList();
Assert.assertEquals(1, books.size());
for (Book b : books) {
log.info(b);
}
em.getTransaction().commit();
em.close();
}
@Override
@SuppressWarnings({"unchecked"})
public <X, Y> SetJoin<X, Y> joinSet(String attributeName, JoinType jt) {
final Attribute<X, ?> attribute = (Attribute<X, ?>) locateAttribute( attributeName );
if ( !attribute.isCollection() ) {
throw new IllegalArgumentException( "Requested attribute was not a set" );
}
final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
if ( !PluralAttribute.CollectionType.SET.equals( pluralAttribute.getCollectionType() ) ) {
throw new IllegalArgumentException( "Requested attribute was not a set" );
}
return (SetJoin<X, Y>) join( (SetAttribute) attribute, jt );
}
/**
* Helper function to return a specification for filtering on one-to-many or many-to-many reference.Where equality, less
than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are
supported. Usage:
<pre><code>
* Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(
* criteria.getEmployeId(),
* root -> root.get(Project_.company).join(Company_.employees),
* entity -> entity.get(Employee_.id));
* Specification<Employee> specByProjectName = buildReferringEntitySpecification(
* criteria.getProjectName(),
* root -> root.get(Project_.project)
* entity -> entity.get(Project_.name));
* </code>
* </pre>
*
* @param <X> The type of the attribute which is filtered.
* @param filter the filter object which contains a value, which needs to match or a flag if emptiness is
* checked.
* @param functionToEntity the function, which joins he current entity to the entity set, on which the filtering is applied.
* @param entityToColumn the function, which of the static metamodel of the referred entity, where the equality should be
* checked.
* @param <OTHER> The type of the referenced entity.
* @param <MISC> The type of the entity which is the last before the OTHER in the chain.
* @return a Specification
*/
protected <OTHER, MISC, X extends Comparable<? super X>> Specification<ENTITY> buildReferringEntitySpecification(final RangeFilter<X> filter,
Function<Root<ENTITY>, SetJoin<MISC, OTHER>> functionToEntity,
Function<SetJoin<MISC, OTHER>, Expression<X>> entityToColumn) {
Function<Root<ENTITY>, Expression<X>> fused = functionToEntity.andThen(entityToColumn);
if (filter.getEquals() != null) {
return equalsSpecification(fused, filter.getEquals());
} else if (filter.getIn() != null) {
return valueIn(fused, filter.getIn());
}
Specification<ENTITY> result = Specification.where(null);
if (filter.getSpecified() != null) {
// Interestingly, 'functionToEntity' doesn't work, we need the longer lambda formula
result = result.and(byFieldSpecified(root -> functionToEntity.apply(root), filter.getSpecified()));
}
if (filter.getNotEquals() != null) {
result = result.and(notEqualsSpecification(fused, filter.getNotEquals()));
}
if (filter.getNotIn() != null) {
result = result.and(valueNotIn(fused, filter.getNotIn()));
}
if (filter.getGreaterThan() != null) {
result = result.and(greaterThan(fused, filter.getGreaterThan()));
}
if (filter.getGreaterThanOrEqual() != null) {
result = result.and(greaterThanOrEqualTo(fused, filter.getGreaterThanOrEqual()));
}
if (filter.getLessThan() != null) {
result = result.and(lessThan(fused, filter.getLessThan()));
}
if (filter.getLessThanOrEqual() != null) {
result = result.and(lessThanOrEqualTo(fused, filter.getLessThanOrEqual()));
}
return result;
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by "has at
* least one of the given tag names".
*
* @param tagNames
* to be filtered on
* @param selectDSWithNoTag
* flag to select distribution sets with no tag
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> hasTags(final Collection<String> tagNames,
final Boolean selectDSWithNoTag) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaDistributionSet, JpaDistributionSetTag> tags = targetRoot.join(JpaDistributionSet_.tags,
JoinType.LEFT);
final Predicate predicate = getPredicate(tags, tagNames, selectDSWithNoTag, cb);
query.distinct(true);
return predicate;
};
}
private static Predicate getPredicate(final SetJoin<JpaDistributionSet, JpaDistributionSetTag> tags,
final Collection<String> tagNames, final Boolean selectDSWithNoTag, final CriteriaBuilder cb) {
tags.get(JpaDistributionSetTag_.name);
final Path<String> exp = tags.get(JpaDistributionSetTag_.name);
if (selectDSWithNoTag != null && selectDSWithNoTag) {
if (!CollectionUtils.isEmpty(tagNames)) {
return cb.or(exp.isNull(), exp.in(tagNames));
} else {
return exp.isNull();
}
} else {
return exp.in(tagNames);
}
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by tag.
*
* @param tagId
* the ID of the distribution set which must be assigned
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> hasTag(final Long tagId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaDistributionSet, JpaDistributionSetTag> tags = targetRoot.join(JpaDistributionSet_.tags,
JoinType.LEFT);
return cb.equal(tags.get(JpaDistributionSetTag_.id), tagId);
};
}
private static Predicate getPredicate(final Root<JpaTarget> targetRoot, final CriteriaBuilder cb,
final Boolean selectTargetWithNoTag, final String[] tagNames) {
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
final Path<String> exp = tags.get(JpaTargetTag_.name);
if (selectTargetWithNoTag) {
if (tagNames != null) {
return cb.or(exp.isNull(), exp.in(tagNames));
} else {
return exp.isNull();
}
} else {
return exp.in(tagNames);
}
}
/**
* {@link Specification} for retrieving {@link Target}s by tag.
*
* @param tagId
* the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTag(final Long tagId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
return cb.equal(tags.get(JpaTargetTag_.id), tagId);
};
}
/**
* This predicate returns all the ProductVersions linked to a specified BuildConfiguration
*/
public static Predicate<ProductVersion> withBuildConfigurationId(Integer buildConfigurationId) {
return (root, query, cb) -> {
SetJoin<ProductVersion, BuildConfiguration> buildConfigurationJoin = root
.join(ProductVersion_.buildConfigurations);
return cb.equal(buildConfigurationJoin.get(BuildConfiguration_.id), buildConfigurationId);
};
}
public static Predicate<BuildConfiguration> withDependantConfiguration(Integer parentBuildConfigurationId) {
return (root, query, cb) -> {
SetJoin<BuildConfiguration, BuildConfiguration> dependantBuildConfigurationsJoin = root
.join(BuildConfiguration_.dependants);
return cb.equal(dependantBuildConfigurationsJoin.get(BuildConfiguration_.id), parentBuildConfigurationId);
};
}
public static Predicate<BuildConfiguration> withBuildConfigurationSetId(Integer buildConfigurationSetId) {
return (root, query, cb) -> {
SetJoin<BuildConfiguration, BuildConfigurationSet> configurationBuildConfigurationSetSetJoin = root
.join(BuildConfiguration_.buildConfigurationSets);
return cb.equal(
configurationBuildConfigurationSetSetJoin.get(BuildConfigurationSet_.id),
buildConfigurationSetId);
};
}
public static Predicate<BuildRecord> withArtifactDistributedInMilestone(Integer productMilestoneId) {
return (root, query, cb) -> {
SetJoin<BuildRecord, Artifact> builtArtifacts = root.join(BuildRecord_.builtArtifacts);
SetJoin<Artifact, ProductMilestone> productMilestones = builtArtifacts
.join(Artifact_.distributedInProductMilestones);
return cb.equal(productMilestones.get(ProductMilestone_.id), productMilestoneId);
};
}
public static Predicate<BuildRecord> withAttribute(String key, String value) {
return (root, query, cb) -> {
SetJoin<BuildRecord, BuildRecordAttribute> joinAttributer = root.join(BuildRecord_.attributes);
return query
.where(
cb.and(cb.equal(joinAttributer.get(BuildRecordAttribute_.key), key)),
cb.equal(joinAttributer.get(BuildRecordAttribute_.value), value))
.getRestriction();
};
}
public static Predicate<BuildConfigurationSet> withBuildConfigurationId(Integer buildConfigurationId) {
return (root, query, cb) -> {
SetJoin<BuildConfigurationSet, BuildConfiguration> buildConfigsJoin = root
.join(BuildConfigurationSet_.buildConfigurations);
return cb.equal(buildConfigsJoin.get(BuildConfiguration_.id), buildConfigurationId);
};
}
private List<Integer> getDependentMilestoneIds(CriteriaBuilder cb, String id) {
CriteriaQuery<Integer> buildQuery = cb.createQuery(Integer.class);
Root<Artifact> artifact = buildQuery.from(Artifact.class);
SetJoin<Artifact, BuildRecord> build = artifact.join(Artifact_.dependantBuildRecords);
buildQuery.where(cb.equal(artifact.get(Artifact_.id), Integer.valueOf(id)));
buildQuery.select(build.get(BuildRecord_.productMilestone).get(ProductMilestone_.id));
buildQuery.distinct(true);
List<Integer> resultList = em.createQuery(buildQuery).getResultList();
return resultList;
}
@Override
@SuppressWarnings("unchecked")
public <X, T, E extends T> SetJoin<X, E> treat(SetJoin<X, T> join, Class<E> type) {
return treat( join, type, (j, t) -> ((SetJoinImplementor) j).treatAs( t ) );
}
@Override
public <X, Y> SetJoin<X, Y> correlate(SetJoin<X, Y> source) {
final SetJoinImplementor<X,Y> correlation = ( (SetJoinImplementor<X,Y>) source ).correlateTo( this );
queryStructure.addCorrelationRoot( correlation );
return correlation;
}
@Override
public <Y> SetJoin<X, Y> join(SetAttribute<? super X, Y> set) {
return join( set, DEFAULT_JOIN_TYPE );
}
@Override
public <X, Y> SetJoin<X, Y> joinSet(String attributeName) {
return joinSet( attributeName, DEFAULT_JOIN_TYPE );
}
public static Predicate<BuildRecord> withArtifactProduced(Integer artifactId) {
return (root, query, cb) -> {
SetJoin<BuildRecord, Artifact> builtArtifacts = root.join(BuildRecord_.builtArtifacts);
return cb.equal(builtArtifacts.get(Artifact_.id), artifactId);
};
}
public static Predicate<BuildRecord> withArtifactDependency(Integer artifactId) {
return (root, query, cb) -> {
SetJoin<BuildRecord, Artifact> dependencies = root.join(BuildRecord_.dependencies);
return cb.equal(dependencies.get(Artifact_.id), artifactId);
};
}
@BeforeEach
@SuppressWarnings("unchecked")
void setup() {
this.root = (Root<ClusterEntity>) Mockito.mock(Root.class);
this.cq = Mockito.mock(CriteriaQuery.class);
this.cb = Mockito.mock(CriteriaBuilder.class);
final Path<Long> idPath = (Path<Long>) Mockito.mock(Path.class);
Mockito.when(this.root.get(ClusterEntity_.id)).thenReturn(idPath);
final Path<String> clusterNamePath = (Path<String>) Mockito.mock(Path.class);
final Predicate likeNamePredicate = Mockito.mock(Predicate.class);
final Predicate equalNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ClusterEntity_.name)).thenReturn(clusterNamePath);
Mockito.when(this.cb.like(clusterNamePath, NAME)).thenReturn(likeNamePredicate);
Mockito.when(this.cb.equal(clusterNamePath, NAME)).thenReturn(equalNamePredicate);
final Path<Instant> minUpdatePath = (Path<Instant>) Mockito.mock(Path.class);
final Predicate greaterThanOrEqualToPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ClusterEntity_.updated)).thenReturn(minUpdatePath);
Mockito.when(this.cb.greaterThanOrEqualTo(minUpdatePath, MIN_UPDATE_TIME))
.thenReturn(greaterThanOrEqualToPredicate);
final Path<Instant> maxUpdatePath = (Path<Instant>) Mockito.mock(Path.class);
final Predicate lessThanPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ClusterEntity_.updated)).thenReturn(maxUpdatePath);
Mockito.when(this.cb.lessThan(maxUpdatePath, MAX_UPDATE_TIME)).thenReturn(lessThanPredicate);
final Path<String> statusPath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalStatusPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ClusterEntity_.status)).thenReturn(statusPath);
Mockito
.when(this.cb.equal(Mockito.eq(statusPath), Mockito.anyString()))
.thenReturn(equalStatusPredicate);
this.tagEntityJoin = (SetJoin<ClusterEntity, TagEntity>) Mockito.mock(SetJoin.class);
Mockito.when(this.root.join(ClusterEntity_.tags)).thenReturn(this.tagEntityJoin);
final Predicate tagInPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.tagEntityJoin.in(TAGS)).thenReturn(tagInPredicate);
final Expression<Long> idCountExpression = (Expression<Long>) Mockito.mock(Expression.class);
Mockito.when(this.cb.count(Mockito.any())).thenReturn(idCountExpression);
final Predicate havingPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.cb.equal(idCountExpression, TAGS.size())).thenReturn(havingPredicate);
}
@BeforeEach
@SuppressWarnings("unchecked")
void setup() {
this.root = (Root<CommandEntity>) Mockito.mock(Root.class);
this.cq = Mockito.mock(CriteriaQuery.class);
this.cb = Mockito.mock(CriteriaBuilder.class);
final Path<Long> idPath = (Path<Long>) Mockito.mock(Path.class);
Mockito.when(this.root.get(CommandEntity_.id)).thenReturn(idPath);
final Path<String> commandNamePath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalNamePredicate = Mockito.mock(Predicate.class);
final Predicate likeNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(CommandEntity_.name)).thenReturn(commandNamePath);
Mockito.when(this.cb.equal(commandNamePath, NAME)).thenReturn(equalNamePredicate);
Mockito.when(this.cb.like(commandNamePath, NAME)).thenReturn(likeNamePredicate);
final Path<String> userNamePath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalUserNamePredicate = Mockito.mock(Predicate.class);
final Predicate likeUserNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(CommandEntity_.user)).thenReturn(userNamePath);
Mockito.when(this.cb.equal(userNamePath, USER_NAME)).thenReturn(equalUserNamePredicate);
Mockito.when(this.cb.like(userNamePath, USER_NAME)).thenReturn(likeUserNamePredicate);
final Path<String> statusPath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalStatusPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(CommandEntity_.status)).thenReturn(statusPath);
Mockito
.when(this.cb.equal(Mockito.eq(statusPath), Mockito.anyString()))
.thenReturn(equalStatusPredicate);
this.tagEntityJoin = (SetJoin<CommandEntity, TagEntity>) Mockito.mock(SetJoin.class);
Mockito.when(this.root.join(CommandEntity_.tags)).thenReturn(this.tagEntityJoin);
final Predicate tagInPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.tagEntityJoin.in(TAGS)).thenReturn(tagInPredicate);
final Expression<Long> idCountExpression = (Expression<Long>) Mockito.mock(Expression.class);
Mockito.when(this.cb.count(Mockito.any())).thenReturn(idCountExpression);
final Predicate havingPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.cb.equal(idCountExpression, TAGS.size())).thenReturn(havingPredicate);
}
@BeforeEach
@SuppressWarnings("unchecked")
void setup() {
this.root = (Root<ApplicationEntity>) Mockito.mock(Root.class);
this.cq = Mockito.mock(CriteriaQuery.class);
this.cb = Mockito.mock(CriteriaBuilder.class);
final Path<Long> idPath = (Path<Long>) Mockito.mock(Path.class);
Mockito.when(this.root.get(ApplicationEntity_.id)).thenReturn(idPath);
final Path<String> namePath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalNamePredicate = Mockito.mock(Predicate.class);
final Predicate likeNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.name)).thenReturn(namePath);
Mockito.when(this.cb.equal(namePath, NAME)).thenReturn(equalNamePredicate);
Mockito.when(this.cb.like(namePath, NAME)).thenReturn(likeNamePredicate);
final Path<String> userNamePath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalUserNamePredicate = Mockito.mock(Predicate.class);
final Predicate likeUserNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.user)).thenReturn(userNamePath);
Mockito.when(this.cb.equal(userNamePath, USER_NAME)).thenReturn(equalUserNamePredicate);
Mockito.when(this.cb.like(userNamePath, USER_NAME)).thenReturn(likeUserNamePredicate);
final Path<String> statusPath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalStatusPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.status)).thenReturn(statusPath);
Mockito
.when(this.cb.equal(Mockito.eq(statusPath), Mockito.anyString()))
.thenReturn(equalStatusPredicate);
this.tagEntityJoin = (SetJoin<ApplicationEntity, TagEntity>) Mockito.mock(SetJoin.class);
Mockito.when(this.root.join(ApplicationEntity_.tags)).thenReturn(this.tagEntityJoin);
final Predicate tagInPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.tagEntityJoin.in(TAGS)).thenReturn(tagInPredicate);
final Expression<Long> idCountExpression = (Expression<Long>) Mockito.mock(Expression.class);
Mockito.when(this.cb.count(Mockito.any())).thenReturn(idCountExpression);
final Predicate havingPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.cb.equal(idCountExpression, TAGS.size())).thenReturn(havingPredicate);
final Path<String> typePath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalTypePredicate = Mockito.mock(Predicate.class);
final Predicate likeTypePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.type)).thenReturn(typePath);
Mockito.when(this.cb.equal(typePath, TYPE)).thenReturn(equalTypePredicate);
Mockito.when(this.cb.like(typePath, TYPE)).thenReturn(likeTypePredicate);
}
/**
* Helper function to return a specification for filtering on one-to-many or many-to-many reference.Usage:<pre>
* Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(
* criteria.getEmployeId(),
* root -> root.get(Project_.company).join(Company_.employees),
* entity -> entity.get(Employee_.id));
* Specification<Employee> specByProjectName = buildReferringEntitySpecification(
* criteria.getProjectName(),
* root -> root.get(Project_.project)
* entity -> entity.get(Project_.name));
* </pre>
*
* @param filter the filter object which contains a value, which needs to match or a flag if emptiness is
* checked.
* @param functionToEntity the function, which joins he current entity to the entity set, on which the filtering is applied.
* @param entityToColumn the function, which of the static metamodel of the referred entity, where the equality should be
* checked.
* @param <OTHER> The type of the referenced entity.
* @param <MISC> The type of the entity which is the last before the OTHER in the chain.
* @param <X> The type of the attribute which is filtered.
* @return a Specification
*/
protected <OTHER, MISC, X> Specification<ENTITY> buildReferringEntitySpecification(Filter<X> filter,
Function<Root<ENTITY>, SetJoin<MISC, OTHER>> functionToEntity,
Function<SetJoin<MISC, OTHER>, Expression<X>> entityToColumn) {
if (filter.getEquals() != null) {
return equalsSpecification(functionToEntity.andThen(entityToColumn), filter.getEquals());
} else if (filter.getSpecified() != null) {
// Interestingly, 'functionToEntity' doesn't work, we need the longer lambda formula
return byFieldSpecified(root -> functionToEntity.apply(root), filter.getSpecified());
}
return null;
}
/**
* Specification which joins all necessary tables to retrieve the dependency
* between a target and a local file assignment through the assigned action
* of the target. All actions are included, not only active actions.
*
* @param controllerId
* the target to verify if the given artifact is currently
* assigned or had been assigned
* @param sha1Hash
* of the local artifact to check wherever the target had ever
* been assigned
* @return a specification to use with spring JPA
*/
public static Specification<JpaAction> hasTargetAssignedArtifact(final String controllerId, final String sha1Hash) {
return (actionRoot, query, criteriaBuilder) -> {
final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet);
final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules);
final ListJoin<JpaSoftwareModule, JpaArtifact> artifactsJoin = modulesJoin
.join(JpaSoftwareModule_.artifacts);
return criteriaBuilder.and(criteriaBuilder.equal(artifactsJoin.get(JpaArtifact_.sha1Hash), sha1Hash),
criteriaBuilder.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.controllerId),
controllerId));
};
}
/**
* Specification which joins all necessary tables to retrieve the dependency
* between a target and a local file assignment through the assigned action
* of the target. All actions are included, not only active actions.
*
* @param targetId
* the target to verify if the given artifact is currently
* assigned or had been assigned
* @param sha1Hash
* of the local artifact to check wherever the target had ever
* been assigned
* @return a specification to use with spring JPA
*/
public static Specification<JpaAction> hasTargetAssignedArtifact(final Long targetId, final String sha1Hash) {
return (actionRoot, query, criteriaBuilder) -> {
final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet);
final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules);
final ListJoin<JpaSoftwareModule, JpaArtifact> artifactsJoin = modulesJoin
.join(JpaSoftwareModule_.artifacts);
return criteriaBuilder.and(criteriaBuilder.equal(artifactsJoin.get(JpaArtifact_.sha1Hash), sha1Hash),
criteriaBuilder.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.id), targetId));
};
}