Reputation: 625
Imagine the following Entity hierarchy:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", columnDefinition = "varchar(60)")
abstract class Resource {
}
@Entity
@DiscriminatorValue("resource1")
class Resource1 extends Resource {
@Embedded
private Property property1;
}
@Entity
@DiscriminatorValue("resource2")
class Resource2 extends Resource {
@Embedded
private Property property2;
}
@Entity
@DiscriminatorValue("resource3")
class Resource3 extends Resource {
@Embedded
private Property property3;
}
@Entity
@DiscriminatorValue("resource4")
class Resource4 extends Resource {
@Embedded
private Property property4;
}
@Entity
class EntityUsingResource {
@OneToMany(...)
@JoinColumn(...)
private List<Resource> resources;
}
I am trying to create a UI to search for EntityUsingResource
s and being able to filter for elements that have resources with a specific property.
So in the search field in the GUI you can type a value for lets say property4
and it filters all EntityUsingResource
s which have a resource of type Resource4
whos property4 equals the one you typed in.
So far I managed to do this by using this criteria api using springs specifications:
public static Specification<EntityUsingResource>
withResource4HavingProperty4Like(String property4) {
Join<EntityUsingResource, Resource> join =
root.join(EntityUsingResource_.resources, JoinType.INNER);
Join<EntityUsingResource, Resource4> treatedJoin =
cb.treat(join, Resource4.class);
return cb.like(
treatedJoin.get(Resource4_.property4).get(Property_.value),
"%" + property4 + "%");
}
public static Specification<EntityUsingResource>
withResource2HavingProperty2Like(String property2) {
Join<EntityUsingResource, Resource> join =
root.join(EntityUsingResource_.resources, JoinType.INNER);
Join<EntityUsingResource, Resource2> treatedJoin =
cb.treat(join, Resource2.class);
return cb.like(
treatedJoin.get(Resource2_.property2).get(Property_.value),
"%" + property2 + "%");
}
I use those specifications with springs Specifications utility class as follows: where( withResource2HavingProperty2Like(property2) ).and( withResource4HavingProperty4Like(property4) );
Which I then pass to the JpaRepository
and more or less return the result to the gui.
This creates the following SQL when searching for property1:
select
entity_using_resource0.id as entityId
from
entity_using_resource entity_using_resource0
inner join resource resourceas1_ on
entity_using_resource0.id=resourceas1_.entity_using_resource_id
inner join resource resourceas2_ on
entity_using_resource0.id=resourceas2_.entity_using_resource_id
inner join resource resourceas3_ on
entity_using_resource0.id=resourceas3_.entity_using_resource_id
inner join resource resourceas4_ on
entity_using_resource0.id=resourceas4_.entity_using_resource_id
where (resourceas4_.property1 like 'property1')
and (resourceas2_.property1 like '%property1%') limit ...;
The problem is, this query produces a lot of duplicates. I tried using distinct which would solve the problem, but it raises another. In the EntityUsingResource
entity I have a column which is of type json so using distinct won't work, as the database can't compare json values.
How would one write a query that also filters for the type of Resource
using the criteria api?
Thanks in advance :-)
Upvotes: 0
Views: 980
Reputation: 1741
If your goal is creating an effective query rather than using criteria API, you may use FluentJPA:
(property2
argument is automatically captured and passed as a parameter)
public List<EntityUsingResource > findEntitiesUsingResource2(String property2) {
FluentQuery query = FluentJPA.SQL((EntityUsingResource entity) -> {
List<Long> resourceIds = subQuery((Resource res) -> {
SELECT(res.getEntityUsingResource().getId());
FROM(res);
WHERE(typeOf(res, Resource2.class) &&
((Resource2) res).getProperty2().getValue().matches("%" + property2 + "%"));
});
SELECT(entity);
FROM(entity);
WHERE(resourceIds.contains(entity.getId()));
});
return query.createQuery(em, EntityUsingResource.class).getResultList();
}
which produces the following SQL:
SELECT t0.*
FROM entity_using_resource t0
WHERE (t0.id IN (SELECT t1.entity_id
FROM resource t1
WHERE (t1.type = 'resource2' AND (t1.property2 LIKE CONCAT( CONCAT( '%' , ?1 ) , '%' ) ))) )
Support of JPA inheritance is documented here.
Upvotes: 1