Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Рекурсия в persistence


Автор: Ортхэннер 15.9.2008, 08:48
Имеем следующий класс:
Код
@Entity
public class Bean inplements Serializable {
  protected int id;
  @Id @GeneratedValue(strategy=GenerationType.AUTO) public int getId() {
   return id;
  }
  public void setId(int id) {
   this.id = id;
  }
  protected String title;
  @Column(nullable=false) public String getTitle() {
   return title;
  }
  public void setTitle(String title) {
   this.title = title;
  }
  protected Bean parent;
  @ManyToOne public Bean getParent() {
   return parent;
  }
  public void setParent(Bean p) {
   parent = p;
  }
  protected java.util.List<Bean> children;
  @OneToMany(mappedBy="parent") public java.util.List<Bean> getChildren() {
   return children;
  }
  public void setChildren(java.util.List<Bean> children) {
   this.children = children;
  }
}


Можно ли одним запросом "выдернуть" всех потомков исходного объекта вне зависимости от поколения? (т. е. чтобы не только
Код

b.parent=?1

но и
Код

b.parent.parent=?1

и т. д.)

Автор: Plamenk 15.9.2008, 09:00
Средствами Oracle можно. См. "connect by prior". 

Но судя по коду ты работаешь с объектами не БД... так что этот способ здесь наверное не поможет.

Автор: powerOn 15.9.2008, 09:03
Если ты вытянешь корневой объект (т.е. предка для тех потомков которые ищешь), то связанные объекты будут загружены либо автоматом (для односсылочных связей), либо лениво. Фактически делать ничего не нужно, только грамотно ссылки перебрать.

Автор: Ортхэннер 15.9.2008, 10:08
Да знаю я, что они лениво будут загружены! Только дело-то в том как раз, что мне не надо, чтобы они грузились! Иначе приложение память будет кушать только в путь, да и медленнее обходить дерево таким образом, нежели на автомате. Ну ты сам посуди - это ж надо всё дерево будет в память загрузить. Куда это годится?

2 Plamenk: это и правда не оракул. Это Java Persistence. То есть БД там есть, но спрятана достаточно глубоко, и добраться до неё... можно, но, опять же, где гарантия, что это будет Oracle? у меня на машине вообще Java DB стоит... а надо, чтобы и с мусклом работало, и с Postgres... так что SQL-запросы если и можно использовать, то очень осторожно.

Автор: tux 15.9.2008, 10:33
Цитата(Ортхэннер @  15.9.2008,  08:48 Найти цитируемый пост)
Можно ли одним запросом "выдернуть" всех потомков исходного объекта вне зависимости от поколения?

Цитата(Ортхэннер @  15.9.2008,  10:08 Найти цитируемый пост)
Только дело-то в том как раз, что мне не надо, чтобы они грузились!

Мне кажется или у вас что-то здесь не сходится? 

Цитата(Ортхэннер @  15.9.2008,  10:08 Найти цитируемый пост)
Иначе приложение память будет кушать только в путь

Если надо загрузить всех потомков, тогда будет кушать, если не надо, тогда не будет. По-моему, все просто. Не вижу проблем в обходе дерева, причем тут скорость не понятно. Плоский массив и дерево память будут расходовать одинаково - объекты одни и те же. Причем тут проблемы с памятью не ясно. Если нужно загрузить сразу все, просто поставь lazy=false.

Автор: Platon 15.9.2008, 10:53
Уважаемые, если выйти из тени JPA, то вот статья для размышления http://www.getinfo.ru/article610.html

Автор: powerOn 15.9.2008, 14:36
Цитата(tux @  15.9.2008,  11:33 Найти цитируемый пост)
Если нужно загрузить сразу все, просто поставь lazy=false. 

В JPA это будет как fetch=FetchType.EAGER в аннотации типа связи.

Автор: Ортхэннер 16.9.2008, 02:09
Цитата(tux @  15.9.2008,  17:33 Найти цитируемый пост)
Мне кажется или у вас что-то здесь не сходится? 

Сорри, неправильно выразился. Надо получить количество потомков объекта. Но всех, а не только непосредственных.
А скорость здесь при том, что обход дерева - задача более трудоёмкая, нежели перебор массива. К тому же рекурсия сама по себе склонна потреблять больше памяти, чем итерационные алгоритмы, - за счёт стека.

Автор: Platon 16.9.2008, 08:08
Цитата(Ортхэннер @  16.9.2008,  03:09 Найти цитируемый пост)
К тому же рекурсия сама по себе склонна потреблять больше памяти, чем итерационные алгоритмы, - за счёт стека. 

Уважаемый пугает своей темнтой. Да ресурсов кушает больше, но от этого мало кто отказывался от ОО подхода или рекурсии.

Автор: Ортхэннер 17.9.2008, 07:22
Уважаемый пугает своей наивностью. Всему своё место, знаете ли. Мне просто реально памяти не хватит - всё дерево обойти в нескольких потоках одновременно. StackOverflowError - видели такое?

Автор: Platon 17.9.2008, 07:39
Какой же такой гигантской глубины у вас дерево, если стека не хватает?

Автор: Ортхэннер 18.9.2008, 07:20
Да не в глубине дело, вернее, не только в ней... кстати она не фиксирована, в общем случае - от 4 и выше. Там на последних двух уровнях у каждого узла уйма потомков получается. :-\ А поскольку крутится всё это не само по себе, а в компании много чего ещё - доступный объём памяти оказывается весьма ограничен. Плюс каждый поток работает с базой самостоятельно (пул, конечно, есть, но от этого не легче - это ж пул соединений с БД, а когда данные уже извлечены...), то есть при подключении десяти клиентов в памяти окажется десять "деревьев". И тут реально одно из двух - или OutOfMemoryError, или StackOverflowError, смотря по тому, хватит памяти на загрузку последней копии базы или нет. В любом случае дёргать объекты получается некошерно. Можно, конечно, делать выборку чисто ID... в этом случае памяти хватит. Но, откровенно говоря, хотелось бы всё-таки переложить эту работу на сервер приложений, а не трахаться самому... вот и интересуюсь - есть способ?

Автор: Platon 18.9.2008, 07:49
Цитата(Ортхэннер @  18.9.2008,  08:20 Найти цитируемый пост)
в общем случае - от 4 и выше

ну-ну, от такой рекурсии просто почти стопроцентное попадание на переполнение стека.

Добавлено через 7 минут и 50 секунд
всё, больше в занозу не лезу. smile 

Автор: Ортхэннер 18.9.2008, 11:02
Платон, хочешь верь, хочешь нет, но такая ситуация реально была. Ты знаешь, сколько объектов находится на одном уровне иерархии? или размер в памяти одного объекта? или объём доступной памяти? нет?
Тогда хватит иронизировать. Нечего посоветовать по сабжу, лучше и правда молчи.

Автор: Hroft 22.9.2008, 17:00
Мне кажется, Platon всего лишь хотел сказать, что у вас принципиально что-то неправильно продумано. Он хочет вам помочь, а не выслушивать "да я сам все знаю, все умею, и вы ни фига не представляете, что у меня тут за МЕГАЗАДАЧА". Нужно как-то уважительнее относиться друг к другу.
Да, ситуации бывают разные. Но за 7 лет программирования я не видел ни одного StackOverflowError, вызванного сложностью задачи. Зато видел предостаточно вызванных... ммм... некорректным кодом.
Даже если рекурсия глубокая, кто заставляет хранить данные в автопеременных? Для больших объемов данных есть куча, может быть, полезно будет свой менеджер памяти небольшой сваять, хотя, опять же, в яве все это смотрится более чем подозрительно.

Автор: powerOn 22.9.2008, 20:15
Я так понимаю что задача сводится к нахождению всех листовых узлов дерева объектов. А может попробовать грамотно запрос на JPA написать, который их находить будет?

Автор: Ортхэннер 23.9.2008, 02:05
Hroft:
В Java менеджер памяти заменить, к сожалению, впрямую нельзя (ограничения безопасности). Есть объекты-ссылки, позволяющие в какой-то степени разрулить ситуацию, когда памяти не хватает, но если полагаться на fetch=FetchType.EAGER в JPA, то пристроить эти объекты оказывается уже некуда. Может быть (и даже более вероятно), не StackOverflow, а OutOfMemory, но... с точки зрения результата - невелика разница. :( Мне памяти-то доступно метров, может, двадцать. А когда на листьях оказываются объекты размерчиком эдак по килобайту, и их несколько тысяч... а теперь представьте, что эту же структуру из базы дёргают ещё два-три хотя бы потока (а их будет не два-три)... и всё, "приплыли", как говорится.
Веб-приложение, чтоб его. :-\

powerOn, типа того, но не совсем. Мне бы количество узлов в конкретной ветви дерева найти. Листья-то что - запрос вроде вот этого, и всё:
Код

SELECT COUNT(b) FROM Bean b WHERE b.parent IS NOT NULL AND b.children IS EMPTY

Можно, конечно, извратиться и использовать вложенный запрос, но зачем? - и так сработает.
А вот как посчитать количество вообще всех потомков конкретного узла - это вопрос...

Автор: Hroft 23.9.2008, 10:57
Не знаю, насколько здравым будет предложение, но вот оно.
А что, если при помощи вообще сторонней программки просто создать сущность, хранящую необходимую информацию? Тупо написать обычную программу, которая с какой-то периодичностью будет обновлять эти данные. В результате при работе ограниченной по памяти (и времени, насколько я понимаю) программы вообще не будет ничего ни загружаться, ни считаться. На память сторонней программы, которая уж точно будет работать в один поток и иметь больше 20 мегов памяти в своем распоряжении, нам, по сути, наплевать.
Пойдет так?

Автор: Platon 23.9.2008, 11:08
Цитата(Hroft @  23.9.2008,  11:57 Найти цитируемый пост)
Не знаю, насколько здравым будет предложение, но вот оно.

По мне так очень здравое, и вообще можно сделать в логику программы инкремент в этой таблице. Получится запросов всего 4+, и памяти минимум.

Автор: Ортхэннер 25.9.2008, 02:29
Действительно, мысль интересная. В принципе, попробовать ничто не мешает, хотя при этом в промежутке между обновлениями индекса пользователь будет получать недостоверную информацию (кстати, проверено - часто такую прогу не позапускаешь, во время анализа загрузка процессора растёт очень сильно, а снижать приоритет потоку - так он работать будет дольше желаемого). Ещё можно всё-таки сделать рекурсию в самой программе, но дёргать только ID объектов базы, а не сами объекты. Возни чуть больше, но зато в память укладываюсь, особенно если обернуть их в PhantomReference. Плюс - ID выбирается шустрее, чем сам объект, что тоже очень приятно. А количество прямых потомков узла при этом определяется автоматически - по длине результирующего списка. Не так уж сложно. "Дёрнуть" из базы после этого связанную сущность по какому-либо критерию тоже не проблема, QL-запросам можно же коллекции в качестве параметров передавать?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)