Непонятное поведение JPA/Hibernate
при втором запуске в children попадают Child с 'category_1'?а при первом в children попадают Child с 'category_2'?
Может кто подскажет другой метод фильтра children?jdbc
А вообще, ты выбираешь объекты Parent по некоторому критерию, тебе их возвращают. При этом фильтровать в получившихся объектах дочернюю коллекцию никто не обещал. Добро пожаловать в ORM, мир протекающих абстракций, vietnam of computer science, все такое.
Нет, при первом выборе все ок, children подставляется по нужно категории.
Я понимаю, что подход слегка нестандартный, но в контексте задачи он представлял самое локаничное и простое решение.
Если сделать show_sql=true, то видно что при запросе 1 - он делает 2 запроса в БД, выбирая первым Parent, вторым Child. При втором тоже самое. То есть влогах 4 записи, но при втором все равно отдает кеш первого запроса
выбирая первым Parent, вторым ChildВо втором запросе есть фильтр c.category = :category ?
То есть влогах 4 записи, но при втором все равно отдает кеш первого запросаА что ты хотел, может ты уже сам поменял содержание листа List<Child> children, хибернейт же об этом не знает, он просто отдает тебе экзеплеры Parent из Identity Map.
Мне удивительно почему от первый раз накладывает фильтр на children. Спасибо за хороший пример, демонстрирующий дырявость абстракции ORM.
Запросы асболютно одинаковые, различается только category.
А что именно ты хочешь выбрать? Твой запрос с точки зрения hibernate не очень правильный - т.е. возможно на самом деле ты хочешь сделать select children join Parent?
по-моему аболютно понятное поведение, если вспомнить про session cache и как он работает.
если вспомнить про session cache и как он работаетчеловеческое мышление надо экономить
человеческое мышление надо экономитья вижу, что ты успешно его наэкономил уже.
да, а твоих успехов не видно
Почему при втором запуске в children попадают Child с 'category_1'?Ничего не кешируется. Поведение by design. Как сказали выше, твой запрос означает выбрать всех парентов, у которых есть хотя бы один чайлд удовлетворяющий запросу. И полученные паренты - это ентити, то есть отображение на всю реляцию целиком, без твоих условий на категорию.
Я понимаю что они кешируются, но зачем?
Может кто подскажет другой метод фильтра children?
Вот тут объяснено подробно с двумя вариантами решений.
Поведение by design.Почему нет симметрии относительно category_1 и category_2? Причем симметрию нарушает только порядок выполнения запросов. Почему получатся разные результаты, если запросы выполнять вот в таком порядке
1. category = 'category_1'
2. category = 'category_2'
или в таком
1. category = 'category_2'
2. category = 'category_1'
?
Согласись, довольно странный design.
Нет, при первом выборе все ок, children подставляется по нужно категории.Есть подозрение, что ты обманываешь, при симметрично наполнении базы, запросы будут выдавать аналогичные результаты.
Я не обманываю Это точно.
минимальный тестовый пример. Описанное тобой поведение не воспроизвелось. Что я сделал не так?
Упоролся и набросал
Это какое-то странное заключение. Вообще, афаир, хибернейт при лукапах кроме всего прочего вытаскивает объекты из своего сессионного кеша, то есть при двух квирях одного и того же парента у тебя не то, что одинаковое содержимое вернется, у тебя одинаковые инстансы парента вернутся. Они различаться в принципе не могут.
Query by category_1 returns:[Parent{id=1, children=[Child{id=1, parent.id=1, category='category_1'}, Child{id=2, parent.id=1, category='category_2'}]}]
Query by category_2 returns:[Parent{id=1, children=[Child{id=1, parent.id=1, category='category_1'}, Child{id=2, parent.id=1, category='category_2'}]}]
Или я чего-то не вижу?
Должно быть вот так:
Query by category_1 returns:[Parent{id=1, children=[Child{id=1, parent.id=1, category='category_1'}]}]
Query by category_2 returns:[Parent{id=1, children=[Child{id=2, parent.id=1, category='category_2'}]}]
Не было, да. И не должно было быть, если я правильно понимаю, как работает JPA/Hibernate. А ты описал поведение, когда она то есть, то нет. Или я тебя не понял?
По твоим результатам ни в первом ни во втором случаи фильтрации по category не было.Ты тут всех запутал, написав:
Нет, при первом выборе все ок, children подставляется по нужно категории.
Короче, Хиберней вполне ожидаемо себя ведет, просто вот это условие "c.category = :category" означает EXISTS. Это не совсем очевидно, но прежде чем написать такое выражение нужно было поинтересоваться что оно значит, у меня бы такой вопрос явно возник.
Делаю запрос первый раз с параметром category_1, в результате я получаю нужный Parent и с Children заполненный правильно, то есть там все Child, у которых category = category_1.
В этой же сессии я делаю второй запрос с параметром category_2, в результате получаю children в которых присутствуют только Child, у которых category = category_1 (ПОЧЕМУ?).
Вроде доступно написано.
когда я пишу join fetch - я подразумеваю что мне нужно зафетчить то, что я скажу, то есть фильтрованные по category, а этого не происходит.
Вот и напрашивается почему вроде логичное выражение выводи нелогичный результат.
Делаю запрос первый раз с параметром category_1, в результате я получаю нужный Parent и с Children заполненный правильно, то есть там все Child, у которых category = category_1.добавь в свою базу данные так, чтобы у этого Parant было два Child c category_1 и category_2. И твой фильтр не будет работать и в ПЕРВОМ запросе.
когда я пишу join fetch - я подразумеваю что мне нужно зафетчить то, что я скажу, то есть фильтрованные по category, а этого не происходит.join fetch - просто говорит заселектить в том же запросе еще и ту коллекцию чайлдов. Про фильтрацию - это ты сам додумал.
Разве не логично, если я пишу join fetch p.children c - мне позволяется так писать. Зачем я в блоке where могу использовать "c" для фильтрации. По вашему для чего это позволяют делать?
С самого начала эти данные присутствовали.
Накидал проект, который воспроизводит поведение, и выложил его на GitHub.
A fetch join does not usually need to assign an alias, because the associated objects should not be used in the where clause (or any other clause).
Сочувствую.
Воркэраунды - юзать фильтры (@Where выключать кеш для кривой квири, сливать кеш после нее.
http://java-persistence-performance.blogspot.com/2012/04/obj...
Если не юзать inner, то при запросах ответ будет одинаковый и филтрации по category ни в первом ни во втором случае не будет.
У меня ответ всегда одинаковый (обе квири возвращают один и тот же инстанс Парента и если есть fetch то в чилдренах одна категория. Если fetch убрать - то категорий две.
Если не юзать inner, то при запросах ответ будет одинаковый и филтрации по category ни в первом ни во втором случае не будет.У тебя выборка select Parent, и результаты выборки не Child, а Parent. Фильтрация тут не имеет особого смысла, т.к. семантика parent.getChildren - вернуть всю коллекцию целиком. Тебе нужно переделать запрос, например, сделать его many-to-one, а не one-to-many
Ошибся. Извиняюсь.
Понятно, конечно, что у фреймоврка нет вариантов эффективно прожевать такой запрос... С другой стороны могли бы просто автоматом кеш отключать для таких запросов. Или хотя бы ошибку выдавать... А такое сломанное поведение замалчивать совсем нехорошо...
Оставить комментарий
t1h0n0ff
Допустим есть 2 сущностиДопустим в одной транзакции я делаю запрос дважды (jpql):
1 запуск: id = 3, category = 'category_1'
2 запуск: id = 3, category = 'category_2'
Почему при втором запуске в children попадают Child с 'category_1'?
Я понимаю что они кешируются, но зачем?
Может кто подскажет другой метод фильтра children?
Использовался hibernate-entitymanager/4.3.0.Final
Спасибо.