Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Design, Quality, Testing > Нужен совет по архитектуре


Автор: User1302 1.11.2011, 12:11
Здравствуйте, нужен совет по архитектуре.

Попробую максимально упростить.
Программа использует средства и компоненты: Swing, Hibernate и базу данных.

Есть интересующий нас бизнес-обьект. Он представлен сохраняемым (Persistent) классом, пусть PObject. В программе есть 2 два независимых друг от друга компонента Jcomponent, кот. определенным образом позволяют работать с одними и теми же экземплярами PObject.

Для простоты допустим, что эти два компонента - содержат в себе списки JList, заключающие в себе экземпляры PObject.
Каждый из компонентов должен уметь обновлять свой список из базы данных самостоятельно, так как по идее, эти компоненты ничего друг о друге не знают.
Для этого берется Hibernate сессия Session, достается список List из базы, обновляется список JList компонента.

Возникает необходимость перетаскивания объектов PObject из первого компонента во второй, и наоборот. При перетаскивании появление экземпляра PObject в Target-компоненте должно происходить в случае его отсутствия в Target-компоненте. Если использовать отдельные сессии Hibernate для каждого компонента возникает ситуация когда один и тот же бизнес-обьект, кот. сохранен в базе данных, будет представлен разными экземплярами PObject.
Получается, при перетаскивании мне нужно проверять наличие в Target-компоненте именно бизнес-обьекта (например, сравнивать Id), а не просто приравнивать java-обьекты оператором "==".

Писать вот такие костыли для проверки Id-шников объектов неправильно.

Вижу два выхода:
1) Доставать обьекты из одной сессии Hibernate. В этом случае нужно что ли сделать одну сессию на все приложение и ею пользоваться.
2) Сделать кеш второго уровня, например EhCache прикрутить, чтоб из разных сессий дергать одни и те же объекты.

Не знаю, по какому пути пойти.
При переносе ПО на сервер, видимо, нужно будет использовать transaction-manager для определения контекста пользователя и предоставления ему Session.

Сейчас hibernate настроен на получение Thread-зависимой сессии, то есть, для каждого потока выделяется сессия.
Ничего подобного еще не писал, так что, в принципе, вопрос этот - вопрос новичка.

Хочется избрать правильный путь для как можно более легкого рефакторинга в дальнейшем.

Исходить ли из того, что на каждого пользователя должна быть одна своя сессия? А поскольку приложение пока однопользовательское, то, выходит, для всего приложения будет одна сессия. Или положиться на кеш второго уровня и создавать сессии когда вздумается?

В каком направлении двигаться? 

Спасибо!

Автор: LSD 1.11.2011, 15:08
Цитата(User1302 @  1.11.2011,  13:11 Найти цитируемый пост)
Получается, при перетаскивании мне нужно проверять наличие в Target-компоненте именно бизнес-обьекта (например, сравнивать Id), а не просто приравнивать java-обьекты оператором "==".

Ну воообще-то в Java по == принято сравнивать только примитивные типа и enum-ы. Все остальные объекты принято сравнивать через equals(), в котором уже надо реализовать правильную логику сравнения. Есть специальные случаи когда объекты сравниваются через ==, но это исключение.


По идее логика должна быть такой:
- ищем объект с данным ID в текущем списке
- если нашли мержим содержимое с текущим объектом
- если не нашли пытаемся смержить содержимое с базой
- если не получилось смержить с базой добавляем в базу

Автор: User1302 1.11.2011, 17:22
Про "==" знаю - для ссылочных типов будет проверять, указывают ли ссылки на одну область памяти.
В equals() нужно будет реализовывать сравнение Id объектов.

Цитата

По идее логика должна быть такой:
- ищем объект с данным ID в текущем списке
- если нашли мержим содержимое с текущим объектом
- если не нашли пытаемся смержить содержимое с базой
- если не получилось смержить с базой добавляем в базу 


Меня как раз интересовало мнение общественности по поводу таких телодвижений, как "мержить".
Как раз насчет этого у меня были сомнения, нужно ли такой огород городить. Если такое кто-то делает, возможно, стоит подумать над этим. Спасибо!

Теперь по поводу моего проекта..
Сделать, как в варианте 1), то есть одну сессию на все приложение - не очень удобно в моем случае. Поясню, почему.
Представим, что в копмоненте1 (помните, я говорил о двух компонентах в начале поста?..) из объектов PObject строится некая сложная структура. В этой же базе хранятся всякие там "restrictions", кот. позволяют принимать решение, грузить следующий обьект из базы, или нет. То есть загрузка данных из базы в этом случае - некоторый длительный итерационный процесс.
В свою очередь, компонент под номером 2 простым бесхитростным образом вытягивает список объектов и это у него получается довольно-таки быстро. Мне не хочется ждать, пока компонент1 построит свою структуру и отпустит сессию.
Очевидно, параллельно к базе могут обращаться два компонента и это нужно делать в разных потоках.
Выходит, что на каждый поток нужна своя сессия, ведь объект Session не является потокобезопасным.

Я никогда не работал с cache, поэтому насчет следующего могу ошибаться:
В идеале я хочу, чтоб разные сессии возвращали мне один и тот же обьект PObject, если он уже где-то используется (где-то есть на него ссылки).
Возможно, с этим мне поможет кеш второго уровня? Или я ошибаюсь?

Даже если не ошибаюсь, вырисовывается другой вопрос:
Стоит ли завязывать всю логику приложения на "какой-то там кеш", который вообще прикручивается через конфигурационные файлы и выступает в роли просто "примочки", кот. позволяет ускорить работу приложения и которого может и не быть, или который может быть заменен другим поставщиком кеша, который, к тому же, может работать не так, как нужно.??
Поправьте меня, если я ошибаюсь.

Интересно, как люди это делают, боюсь наламать дров.

Автор: LSD 2.11.2011, 11:42
Цитата(User1302 @  1.11.2011,  18:22 Найти цитируемый пост)
Меня как раз интересовало мнение общественности по поводу таких телодвижений, как "мержить".

У тебя есть два места в памяти где хранятся сущности, причем они могут независимо модифицироваться, и плюс ко всему этому есть база. Которая вроде того как source of truth. Естественно тут придется повертеться, чтобы это все заработало. Можно упростить схему, но для этого надо где-то отказаться от изменений. Или не держать изменений в памяти, сразу писать их в базу.

Автор: User1302 2.11.2011, 12:15
Цитата(LSD @  2.11.2011,  11:42 Найти цитируемый пост)
У тебя есть два места в памяти где хранятся сущности, причем они могут независимо модифицироваться, и плюс ко всему этому есть база. Которая вроде того как source of truth. Естественно тут придется повертеться, чтобы это все заработало. Можно упростить схему, но для этого надо где-то отказаться от изменений. Или не держать изменений в памяти, сразу писать их в базу.

Когда придумывал приложение, представлял себе, что места, где хранятся сущности (я так понимаю, Вы про мои "компоненты" говорите), могут добавляться во время постепенного расширения приложения, и в каждом таком месте (компоненте) возможно изменение объекта. При изменении объект бросается во "flush-очередь", содержимое которой сбрасывается в БД при нажатии кнопочки с пиктограммой дискетки "сохранить".

Тема, в общем, остается открытой, а именно, вопрос о том, стоит ли заморачиваться с тем, чтоб для всего приложения мог существовать только один экземпляр обьекта PObject, то есть одна запись в таблице = одному Java-объекту, с которым работают множество потоков?
Или же смотреть в сторону "мержить"?

LSD, спасибо!

Автор: Skipy 2.11.2011, 16:57
Вопрос на засыпку. А зачем использовать разные сессии для компонент? Сессия в них должна поставляться извне, через dependency injection. Вот и поставляйте один экземпляр, тогда перетаскиваемые объекты будут в одной сессии. А компоненты по-прежнему ничего не будут знать друг о друге.

Автор: User1302 2.11.2011, 17:43
Цитата(Skipy @  2.11.2011,  16:57 Найти цитируемый пост)
Вопрос на засыпку. А зачем использовать разные сессии для компонент?

У компонент разное время обновлений из БД. Например, у первого - от 10сек до 20сек. У второго - миллисекунды.
Нужно пока строится структура в первом компоненте, беспрепятственно строить/обновлять второй компонент.

Автор: User1302 3.11.2011, 18:15
Решение, конечно, напрашивается само:
Использовать разные сессии, разные java-объекты для одного бизнес-объекта, сравнивать эти объекты по полю Id, и так работать.

Неужто никто не сталкивался с подобным?
Может быть, я не совсем понятно объяснил?

Хотелось услышать побольше мнений..

Автор: lazycat 3.11.2011, 19:36
IMHO для того, чтобы принять конкретное решение, надо еще очень много чего уточнить, но "навскидку" я бы предпочел подход, при котором каждый PObject хранился бы в памяти только в одном экземпляре. В противном случае, мне кажется, на каждом этапе разработки будут возникать все новые и новые проблемы с синхронизацией.

Автор: User1302 4.11.2011, 13:07
Что если сделать так:
1) Написать кэш для persistent объектов на фантомных ссылках.
2) Дописать Hibernate таким образом, чтобы сессия искала объект по Id в кэше.
    - Если находит объект в кэше - изменяет его в соответствии с данными полей, кот. вытянут из БД, возвращает объект из кеша.
    - Если не находит объект в кэше - создает новый по стандартной схеме Hibernate, возвращает новый обьект.
Тогда любая сессия будет возвращать один и тот же экземпляр, если на него уже есть где-либо в приложении ссылки.

О трудностях этой задачи еще не думал, нужно поковырять Хибер.

Есть мысли?

Автор: LSD 7.11.2011, 15:02
Цитата(Skipy @  2.11.2011,  17:57 Найти цитируемый пост)
Сессия в них должна поставляться извне, через dependency injection. Вот и поставляйте один экземпляр, тогда перетаскиваемые объекты будут в одной сессии.

1. У хибера сессия не синхронизирована.
2. Это противоречит рекомендациям хибернейта:
Цитата
A Session is an inexpensive, non-threadsafe object that should be used once and then discarded for: a single request, a conversation or a single unit of work.

Автор: User1302 10.11.2011, 11:41
Цитата(LSD @  7.11.2011,  15:02 Найти цитируемый пост)
1. У хибера сессия не синхронизирована.
2. Это противоречит рекомендациям хибернейта:


Что касается кэша 2-го уровня: насколько понял по документации, кэш не хранит "инстансы" объектов, а хранит объекты в разобранном виде.
Это значит, что в контексте сессии всегда создается новый экземпляр объекта. Так что, не выйдет воспользоваться кэшем 2-го уровня для получения одного и того же экземпляра PObject через разные сессии (см. мое первое сообщение, там описано зачем).

Дальше, насчет моего вот этого:
Цитата

2) Дописать Hibernate таким образом, чтобы сессия искала объект по Id в кэше.
    - Если находит объект в кэше - изменяет его в соответствии с данными полей, кот. вытянут из БД, возвращает объект из кеша.
    - Если не находит объект в кэше - создает новый по стандартной схеме Hibernate, возвращает новый объект.


Если даже такое сделать, - это будет один большой костыль.
При расширении системы в дальнейшем всегда нужно будет иметь ввиду, что мы работаем через эту "изуродованную" сессию, привязывать к ней весь код.
Вывод: нужно выработать подход, как сделать так, чтоб разные инстансы одного бизнес объекта мирно сосуществовали в одном приложении, подумать над тем, чтоб меньше использовать памяти.

Думаю, тему можно закрыть.

Всем спасибо за помощь!

Автор: LSD 11.11.2011, 12:02
Цитата(User1302 @  10.11.2011,  12:41 Найти цитируемый пост)
Что касается кэша 2-го уровня: насколько понял по документации, кэш не хранит "инстансы" объектов, а хранит объекты в разобранном виде.

Кстати, да smile 

Автор: User1302 18.11.2011, 17:54
Может быть, вот эта ссылка будет кому интересна:
https://forum.hibernate.org/viewtopic.php?f=1&t=965080

Что касается приложения - подход пока не определен. Колеблюсь то в одну, то в другую сторону каждый день буквально.

Автор: User1302 18.11.2011, 19:52
Такая-себе "квинтэссенция":
http://blog.schauderhaft.de/2008/09/28/hibernate-sessions-in-two-tier-rich-client-applications/
+1шаг к решению проблемы smile 

Автор: COVD 23.11.2011, 20:06
Мне кажется, все эти сложности от того, что на хибернет здесь возлагается слишком много.

Я бы начал с того, что определил модель приложения, в которой хранятся обьекты.

Код

static Map map = new HashMap();



Обьекты в модель могут попасть из разных мест. Пользователь может создать обьект через ГУИ. Приложение может прочитать сериализованные обьекты из файла. Наконец, приложение может загрузить обьекты из базы данных. И вот в этом случае хибернет может быть использован как маппер, как парсер. Больше от него ничего не надо.

Таким образом, уникальные обьекты хранятся в модели - синглтоне и доступны по ключу. Далее, если пользователь может открывать одновременно несколько view, которые отображают одни и те же данные, то это другая задача, которая никак не связана со способом сериализации обьектов.

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