[JPA] calculated boolean field
От: A13x США  
Дата: 21.12.10 09:56
Оценка:
Условие задачи: переходим с plain JDBC на JPA.
Вопрос: Как эффективно реализовать запросы на JPA с вычисляемым полем?

Далее подробнее:

Есть таблица пользователей и отношения дружбы между ними:

create table PROFILE (
    ID integer,
    NAME varchar(64) not null
);

alter table PROFILE add constraint IDX_PROFILE_ID primary key (ID);

create table FRIEND (
    FROM_ID integer not null,
    TO_ID integer not null
);

alter table FRIEND add constraint IDX_FRIENDS_IDS primary key (FROM_ID, TO_ID);
alter table FRIEND add constraint FK_FRIENDS_FROM_ID foreign key (FROM_ID) references PROFILE(ID);
alter table FRIEND add constraint FK_FRIENDS_TO_ID foreign key (TO_ID) references PROFILE(ID);


Один из часто используемых запросов к базе — выборка пользователей для конкретного пользователя A из таблицы PROFILE с добавленным вычисляемым булевым полем является ли этот человек другом пользователю A.
Для пущей ясности вот вариант такого параметризованного SQL запроса (параметр — :ID) —

select P.NAME, exists(select * from FRIEND where FRIEND.FROM_ID = :ID and FRIEND.TO_ID = P.ID) as IS_FRIEND
from PROFILE as P
where P.ID != :ID;


который заполняет набор из объектов вида:


public class SocialProfileData {
    private String name;
    private boolean isFriend;

//+ getters/setters
}



Вопрос: как эффективно смоделировать это отношение в JPA?

Понятно, что доменный класс следует оформить в виде
public class ProfileData {
    private int id;
    private String username;
    private Set<ProfileData> friends;

//+ getters/setters
}


но вот как быть с запросами, которые должны вернуть набор из SocialProfileData?
В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть.
Re: [JPA] calculated boolean field
От: Blazkowicz Россия  
Дата: 21.12.10 10:03
Оценка:
Здравствуйте, A13x, Вы писали:

A>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData?

A>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть.
Что останавливает от использования Hibernate?
Re[2]: [JPA] calculated boolean field
От: A13x США  
Дата: 21.12.10 10:04
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, A13x, Вы писали:


A>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData?

A>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть.
B>Что останавливает от использования Hibernate?

Возможный переход в дальнейшем на Google App Engine.
Re[3]: [JPA] calculated boolean field
От: Blazkowicz Россия  
Дата: 21.12.10 10:21
Оценка:
Здравствуйте, A13x, Вы писали:

A>Возможный переход в дальнейшем на Google App Engine.

Тогда или писать NamedQuery или попробовать прикрутить @SecondaryTables
Re[3]: [JPA] calculated boolean field
От: GarryIV  
Дата: 21.12.10 10:30
Оценка:
Здравствуйте, A13x, Вы писали:

A>>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData?

A>>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть.
B>>Что останавливает от использования Hibernate?

A>Возможный переход в дальнейшем на Google App Engine.


Я бы даже не лелеял мысль сделать переносимое приложение на JPA. Смена имплементации весьма трудоемкая штука — есть опыт.
WBR, Igor Evgrafov
Re[4]: [JPA] calculated boolean field
От: A13x США  
Дата: 21.12.10 10:51
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, A13x, Вы писали:


A>>Возможный переход в дальнейшем на Google App Engine.

B>Тогда или писать NamedQuery или попробовать прикрутить @SecondaryTables

Пока как-то не особо получилось, запросы, составленные "силой мысли" не работают.
Покопался в интернете, но пока не нашел эквивалентного примера.
Я, конечно, буду еще копать в эту сторону, но возможно кто-то может поделится готовым примерчиком или личным опытом реализации похожей штуки на JPA?
Re[4]: [JPA] calculated boolean field
От: A13x США  
Дата: 21.12.10 10:51
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>Здравствуйте, A13x, Вы писали:


A>>>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData?

A>>>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть.
B>>>Что останавливает от использования Hibernate?

A>>Возможный переход в дальнейшем на Google App Engine.


GIV>Я бы даже не лелеял мысль сделать переносимое приложение на JPA. Смена имплементации весьма трудоемкая штука — есть опыт.


А что за проблемы были, если не секрет?
Re: [JPA] calculated boolean field
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 21.12.10 10:58
Оценка:
Здравствуйте, A13x, Вы писали:

A>Один из часто используемых запросов к базе — выборка пользователей для конкретного пользователя A из таблицы PROFILE с добавленным вычисляемым булевым полем является ли этот человек другом пользователю A.

A>Для пущей ясности вот вариант такого параметризованного SQL запроса (параметр — :ID) -

A>
A>select P.NAME, exists(select * from FRIEND where FRIEND.FROM_ID = :ID and FRIEND.TO_ID = P.ID) as IS_FRIEND
A>from PROFILE as P
A>where P.ID != :ID;
A>


A>который заполняет набор из объектов вида:


A>
A>public class SocialProfileData {
A>    private String name;
A>    private boolean isFriend;

A>//+ getters/setters
A>}
A>



A>Вопрос: как эффективно смоделировать это отношение в JPA?


Я правильно понял суть запроса (не вникая в его эффективность) — "выбрать все профили, кроме А, отметив флажком друзей A"?
Тогда JPA 2.0 аналог в черновом варианте:

SELECT NEW SocialProfileData(
    p.name, 
    CASE 
        WHEN EXISTS (SELECT q.id FROM profile q WHERE p MEMBER OF q.friends)) 
            THEN TRUE
        ELSE FALSE
    END
    )
FROM profile p
WHERE p.id = :id
El pueblo unido jamás será vencido.
Re[2]: [JPA] calculated boolean field
От: A13x США  
Дата: 21.12.10 15:56
Оценка:
Здравствуйте, bl-blx, Вы писали:

BB>...

BB>
BB>SELECT NEW SocialProfileData(
BB>    p.name, 
BB>    CASE 
BB>        WHEN EXISTS (SELECT q.id FROM profile q WHERE p MEMBER OF q.friends)) 
BB>            THEN TRUE
BB>        ELSE FALSE
BB>    END
BB>    )
BB>FROM profile p
BB>WHERE p.id = :id
BB>


Это, увы, не работает, т.к. SocialProfileData не фигурирует среди классов в JPA маппинге (и кажется, что не должен), а в JPQL, похоже, можно использовать только классы объявленные в маппинге.

Поправьте если я ошибаюсь.

Кстати, я в первоначальном сообщении был неправ высказав предположение о виде доменного класса в JPA. Там нужно использовать отношение многие-ко-многим и ввести еще одно поле и маппинг для него.

Примерно так:


public class ProfileData {
    private int id;
    private String username;
    private Set<ProfileData> referalFriends; // те, у кого этот пользователь в друзьях
    private Set<ProfileData> friends; // друзья этого пользователя
//...
}



кусок маппинга:

<entity name="ProfileData" class="domain.ProfileData">
    <table name="PROFILE_DATA"/>
    <attributes>
        <id name="id">
            <column name="ID"/>
            <generated-value strategy="TABLE"/>
        </id>
        <basic name="username">
            <column name="USERNAME" nullable="false" length="64"/>
        </basic>
        <many-to-many name="referalFriends" fetch="EAGER">
            <join-table name="FRIEND">
                <join-column name="TO_ID" referenced-column-name="ID"/>
                <inverse-join-column name="FROM_ID" referenced-column-name="ID"/>
            </join-table>
        </many-to-many>
        <many-to-many name="friends" fetch="EAGER" mapped-by="referalFriends"/>
    </attributes>
</entity>
Re[3]: [JPA] calculated boolean field
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 21.12.10 16:07
Оценка: 6 (1)
Здравствуйте, A13x, Вы писали:

A>Здравствуйте, bl-blx, Вы писали:


BB>>
BB>>SELECT NEW SocialProfileData(
BB>>    p.name, 
BB>>    CASE 
BB>>        WHEN EXISTS (SELECT q.id FROM profile q WHERE p MEMBER OF q.friends)) 
BB>>            THEN TRUE
BB>>        ELSE FALSE
BB>>    END
BB>>    )
BB>>FROM profile p
BB>>WHERE p.id = :id
BB>>

Вообще, какую-то фигню я написал. Просто под рукой нет рабочей конфигурации, чтобы проверить.
Но идея прежняя — использовать constructor expression и case. Eсть сомнения насчет возможности использовать подзапрос в case.

A>Это, увы, не работает, т.к. SocialProfileData не фигурирует среди классов в JPA маппинге (и кажется, что не должен), а в JPQL, похоже, можно использовать только классы объявленные в маппинге.


Как раз наоборот, можно таким образом классы без маппинга создавать.
El pueblo unido jamás será vencido.
Re[4]: [JPA] calculated boolean field
От: A13x США  
Дата: 21.12.10 16:20
Оценка:
Здравствуйте, bl-blx, Вы писали:

BB>...

BB>Как раз наоборот, можно таким образом классы без маппинга создавать.

Я был неправ, сорри. Все заработало, спасибо огромное!
Жаль, что похоже в таком варианте кэш хибернейта для SocialProfileData работать не будет.
Впрочем, наверное даже для этого есть какой-нибудь хитрый финт.

Еще раз спасибо!

А запрос вполне себе рабочий, только вместо '=' надо написать '<>'
Re[5]: [JPA] calculated boolean field
От: GarryIV  
Дата: 21.12.10 16:31
Оценка:
Здравствуйте, A13x, Вы писали:

A>>>>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData?

A>>>>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть.
B>>>>Что останавливает от использования Hibernate?

A>>>Возможный переход в дальнейшем на Google App Engine.


GIV>>Я бы даже не лелеял мысль сделать переносимое приложение на JPA. Смена имплементации весьма трудоемкая штука — есть опыт.


A>А что за проблемы были, если не секрет?


Ничего такого глобального, но мелочей море.
* Результат запроса был List<List<Object>> стал List<Object[]>
* Были параметры #param стали :param
* Другая внутренняя структура имплементации — то что было оптимально, стало не очень.
* Местами было использование классов имплементации напрямую (хендлеры всякие).
и тд и тп

Меняли Toplink на Hibernate, кстати. Заняло полгода работы нескольких человек. Это грязное время, но все таки.

Иногда на версию выше перейти не так-то просто...
WBR, Igor Evgrafov
Re[5]: [JPA] calculated boolean field
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 21.12.10 20:51
Оценка: 3 (1)
Здравствуйте, A13x, Вы писали:

A>Здравствуйте, bl-blx, Вы писали:


A>Я был неправ, сорри. Все заработало, спасибо огромное!


А я был прав — фигня получилась.
Исправленный вариант:

SELECT NEW samples.model.SocialProfileData(
    p.name,
    CASE 
        WHEN EXISTS (SELECT q.id FROM Profile q WHERE q.id = :id AND p MEMBER OF q.friends) 
            THEN TRUE
        ELSE FALSE
    END
    )
FROM Profile p
WHERE p.id <> :id


Ну, а при наличии обратной связи referalFriends, возможно, попроще будет как сам JPQL так и сгенерированный SQL.

A>Жаль, что похоже в таком варианте кэш хибернейта для SocialProfileData работать не будет.


Не будет. Кэш только для управляемых сущностей, у которых есть id.
El pueblo unido jamás será vencido.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.