Найти элементы определенного типа в едином типе наследования таблиц - PullRequest
0 голосов
/ 08 ноября 2019

Представьте себе следующую иерархию сущностей:

@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;
}

Я пытаюсь создать пользовательский интерфейс для поиска EntityUsingResource s и могу фильтровать элементы, которые имеют ресурсы с определенным свойством.

Таким образом, в поле поиска в графическом интерфейсе можно ввести значение, скажем, property4, и оно отфильтровывает все EntityUsingResource с ресурсом типа Resource4, свойство которого4 которого равно введенному вами.

До сих пор мне удавалось сделать это, используя этот критерий API с использованием спецификаций пружин:

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 + "%");
}

Я использую эти спецификации с утилитой класса Спецификации пружин следующим образом: где (withResource2HavingProperty2Like (property2)) .and(withResource4HavingProperty4Like (property4));

которые я затем передаю JpaRepository и более или менее возвращаю результат в графический интерфейс.

Это создает следующий SQL при поиске 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 ...;

Проблема в том, что этот запрос выдает много дубликатов. Я попытался использовать различные, которые могли бы решить проблему, но возникает другая. В сущности EntityUsingResource у меня есть столбец, который имеет тип json, поэтому использование различных не будет работать, поскольку база данных не может сравнивать значения json.

Как написать запрос, который также фильтрует длятип Resource используя критерии api?

Заранее спасибо: -)

1 Ответ

0 голосов
/ 11 ноября 2019

Если ваша цель - создать эффективный запрос, а не использовать API критериев, вы можете использовать FluentJPA :

(аргумент property2 автоматически захватывается и передается в качестве параметра)

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();
}

, который выдает следующий 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 ) ,  '%' )  ))) )

Поддержка наследования JPA задокументирована здесь .

...