WedX - журнал о программировании и компьютерных науках

Найти элементы определенного типа в типе наследования одной таблицы

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

@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 и иметь возможность фильтровать элементы, у которых есть ресурсы с определенным свойством.

Таким образом, в поле поиска в графическом интерфейсе вы можете ввести значение, скажем, 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 + "%");
}

Я использую эти спецификации с служебным классом Springs Specifications следующим образом: где (withResource2HavingProperty2Like (property2)).

Который я затем передаю в 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 ...;

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

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

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


Ответы:


1

Если вашей целью является создание эффективного запроса, а не использование 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 задокументирована здесь .

10.11.2019
  • Я отмечу это как решение, хотя я еще не пробовал. Я дам отзыв, как только я попробовал это. Спасибо за вашу помощь! 18.11.2019
  • Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]