Здравствуйте, 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, похоже, можно использовать только классы объявленные в маппинге.
Как раз наоборот, можно таким образом классы без маппинга создавать.
Здравствуйте, 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.
Условие задачи: переходим с 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 вот такой маркер есть.
Здравствуйте, A13x, Вы писали:
A>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData? A>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть.
Что останавливает от использования Hibernate?
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, A13x, Вы писали:
A>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData? A>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть. B>Что останавливает от использования Hibernate?
Возможный переход в дальнейшем на Google App Engine.
Здравствуйте, A13x, Вы писали:
A>Возможный переход в дальнейшем на Google App Engine.
Тогда или писать NamedQuery или попробовать прикрутить @SecondaryTables
Здравствуйте, A13x, Вы писали:
A>>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData? A>>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть. B>>Что останавливает от использования Hibernate?
A>Возможный переход в дальнейшем на Google App Engine.
Я бы даже не лелеял мысль сделать переносимое приложение на JPA. Смена имплементации весьма трудоемкая штука — есть опыт.
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, A13x, Вы писали:
A>>Возможный переход в дальнейшем на Google App Engine. B>Тогда или писать NamedQuery или попробовать прикрутить @SecondaryTables
Пока как-то не особо получилось, запросы, составленные "силой мысли" не работают.
Покопался в интернете, но пока не нашел эквивалентного примера.
Я, конечно, буду еще копать в эту сторону, но возможно кто-то может поделится готовым примерчиком или личным опытом реализации похожей штуки на JPA?
Здравствуйте, GarryIV, Вы писали:
GIV>Здравствуйте, A13x, Вы писали:
A>>>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData? A>>>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть. B>>>Что останавливает от использования Hibernate?
A>>Возможный переход в дальнейшем на Google App Engine.
GIV>Я бы даже не лелеял мысль сделать переносимое приложение на JPA. Смена имплементации весьма трудоемкая штука — есть опыт.
Здравствуйте, 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
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; // друзья этого пользователя
//...
}
Здравствуйте, bl-blx, Вы писали:
BB>... BB>Как раз наоборот, можно таким образом классы без маппинга создавать.
Я был неправ, сорри. Все заработало, спасибо огромное!
Жаль, что похоже в таком варианте кэш хибернейта для SocialProfileData работать не будет.
Впрочем, наверное даже для этого есть какой-нибудь хитрый финт.
Еще раз спасибо!
А запрос вполне себе рабочий, только вместо '=' надо написать '<>'
Здравствуйте, A13x, Вы писали:
A>>>>>но вот как быть с запросами, которые должны вернуть набор из SocialProfileData? A>>>>>В JPA не нашел маркера для вычислимого поля, может плохо искал? В hibernate вот такой маркер есть. B>>>>Что останавливает от использования Hibernate?
A>>>Возможный переход в дальнейшем на Google App Engine.
GIV>>Я бы даже не лелеял мысль сделать переносимое приложение на JPA. Смена имплементации весьма трудоемкая штука — есть опыт.
A>А что за проблемы были, если не секрет?
Ничего такого глобального, но мелочей море.
* Результат запроса был List<List<Object>> стал List<Object[]>
* Были параметры #param стали :param
* Другая внутренняя структура имплементации — то что было оптимально, стало не очень.
* Местами было использование классов имплементации напрямую (хендлеры всякие).
и тд и тп
Меняли Toplink на Hibernate, кстати. Заняло полгода работы нескольких человек. Это грязное время, но все таки.