| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > 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-зависимой сессии, то есть, для каждого потока выделяется сессия. Ничего подобного еще не писал, так что, в принципе, вопрос этот - вопрос новичка. Хочется избрать правильный путь для как можно более легкого рефакторинга в дальнейшем. Исходить ли из того, что на каждого пользователя должна быть одна своя сессия? А поскольку приложение пока однопользовательское, то, выходит, для всего приложения будет одна сессия. Или положиться на кеш второго уровня и создавать сессии когда вздумается? В каком направлении двигаться? Спасибо! |
| Автор: User1302 1.11.2011, 17:22 | ||
| Про "==" знаю - для ссылочных типов будет проверять, указывают ли ссылки на одну область памяти. В equals() нужно будет реализовывать сравнение Id объектов.
Меня как раз интересовало мнение общественности по поводу таких телодвижений, как "мержить". Как раз насчет этого у меня были сомнения, нужно ли такой огород городить. Если такое кто-то делает, возможно, стоит подумать над этим. Спасибо! Теперь по поводу моего проекта.. Сделать, как в варианте 1), то есть одну сессию на все приложение - не очень удобно в моем случае. Поясню, почему. Представим, что в копмоненте1 (помните, я говорил о двух компонентах в начале поста?..) из объектов PObject строится некая сложная структура. В этой же базе хранятся всякие там "restrictions", кот. позволяют принимать решение, грузить следующий обьект из базы, или нет. То есть загрузка данных из базы в этом случае - некоторый длительный итерационный процесс. В свою очередь, компонент под номером 2 простым бесхитростным образом вытягивает список объектов и это у него получается довольно-таки быстро. Мне не хочется ждать, пока компонент1 построит свою структуру и отпустит сессию. Очевидно, параллельно к базе могут обращаться два компонента и это нужно делать в разных потоках. Выходит, что на каждый поток нужна своя сессия, ведь объект Session не является потокобезопасным. Я никогда не работал с cache, поэтому насчет следующего могу ошибаться: В идеале я хочу, чтоб разные сессии возвращали мне один и тот же обьект PObject, если он уже где-то используется (где-то есть на него ссылки). Возможно, с этим мне поможет кеш второго уровня? Или я ошибаюсь? Даже если не ошибаюсь, вырисовывается другой вопрос: Стоит ли завязывать всю логику приложения на "какой-то там кеш", который вообще прикручивается через конфигурационные файлы и выступает в роли просто "примочки", кот. позволяет ускорить работу приложения и которого может и не быть, или который может быть заменен другим поставщиком кеша, который, к тому же, может работать не так, как нужно.?? Поправьте меня, если я ошибаюсь. Интересно, как люди это делают, боюсь наламать дров. |
| Автор: LSD 2.11.2011, 11:42 | ||
У тебя есть два места в памяти где хранятся сущности, причем они могут независимо модифицироваться, и плюс ко всему этому есть база. Которая вроде того как source of truth. Естественно тут придется повертеться, чтобы это все заработало. Можно упростить схему, но для этого надо где-то отказаться от изменений. Или не держать изменений в памяти, сразу писать их в базу. |
| Автор: User1302 2.11.2011, 12:15 | ||
Когда придумывал приложение, представлял себе, что места, где хранятся сущности (я так понимаю, Вы про мои "компоненты" говорите), могут добавляться во время постепенного расширения приложения, и в каждом таком месте (компоненте) возможно изменение объекта. При изменении объект бросается во "flush-очередь", содержимое которой сбрасывается в БД при нажатии кнопочки с пиктограммой дискетки "сохранить". Тема, в общем, остается открытой, а именно, вопрос о том, стоит ли заморачиваться с тем, чтоб для всего приложения мог существовать только один экземпляр обьекта PObject, то есть одна запись в таблице = одному Java-объекту, с которым работают множество потоков? Или же смотреть в сторону "мержить"? LSD, спасибо! |
| Автор: Skipy 2.11.2011, 16:57 |
| Вопрос на засыпку. А зачем использовать разные сессии для компонент? Сессия в них должна поставляться извне, через dependency injection. Вот и поставляйте один экземпляр, тогда перетаскиваемые объекты будут в одной сессии. А компоненты по-прежнему ничего не будут знать друг о друге. |
| Автор: User1302 2.11.2011, 17:43 | ||
У компонент разное время обновлений из БД. Например, у первого - от 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 | ||||
1. У хибера сессия не синхронизирована. 2. Это противоречит рекомендациям хибернейта:
|
| Автор: User1302 10.11.2011, 11:41 | ||||
Что касается кэша 2-го уровня: насколько понял по документации, кэш не хранит "инстансы" объектов, а хранит объекты в разобранном виде. Это значит, что в контексте сессии всегда создается новый экземпляр объекта. Так что, не выйдет воспользоваться кэшем 2-го уровня для получения одного и того же экземпляра PObject через разные сессии (см. мое первое сообщение, там описано зачем). Дальше, насчет моего вот этого:
Если даже такое сделать, - это будет один большой костыль. При расширении системы в дальнейшем всегда нужно будет иметь ввиду, что мы работаем через эту "изуродованную" сессию, привязывать к ней весь код. Вывод: нужно выработать подход, как сделать так, чтоб разные инстансы одного бизнес объекта мирно сосуществовали в одном приложении, подумать над тем, чтоб меньше использовать памяти. Думаю, тему можно закрыть. Всем спасибо за помощь! |
| Автор: LSD 11.11.2011, 12:02 | ||
Кстати, да |
| Автор: 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шаг к решению проблемы |
| Автор: COVD 23.11.2011, 20:06 | ||
| Мне кажется, все эти сложности от того, что на хибернет здесь возлагается слишком много. Я бы начал с того, что определил модель приложения, в которой хранятся обьекты.
Обьекты в модель могут попасть из разных мест. Пользователь может создать обьект через ГУИ. Приложение может прочитать сериализованные обьекты из файла. Наконец, приложение может загрузить обьекты из базы данных. И вот в этом случае хибернет может быть использован как маппер, как парсер. Больше от него ничего не надо. Таким образом, уникальные обьекты хранятся в модели - синглтоне и доступны по ключу. Далее, если пользователь может открывать одновременно несколько view, которые отображают одни и те же данные, то это другая задача, которая никак не связана со способом сериализации обьектов. |