| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java EE (J2EE) и Spring > Multi-tenancy in Hibernate |
| Автор: Samotnik 10.5.2011, 22:35 |
| Привет. Есть задача, реализовать Multi-tenancy в приложении, а точнее создать архитектуру БД. Если кто-то не знаком с этим понятием объясню на примере: главная сущность tenant - по сути пользователь, либо группа, остальные сущности вертятся вокруг нее, например сущности: Машина, Квартира, Женщина. Необходимо: если залогинился тенант А - показывать ему только его машины, квартиры, женщин, если тенант Б - только его женщин, машины квартиры и т.д. по аналогии Погуглив, https://encrypted.google.com/search?q=multi+tenancy+in+hibernate&ie=utf-8&oe=utf-8&aq=t Но все как бы в теории + единого метода так никто и не предложил, то памяти много отжирает,то с кэшем второго уровня проблемы. Есть 3 основных метода:
Первый и второй - тоже не ясны. Там предлагают создавать для каждого тенанта свою SessionFactory. Но ведь SessionFactory - это логическая работа с БД, а как физически будут записи храниться в одних и тех же сущностях ? Плюс вопрос, если будет 100 тенантов, то придется создавать 100 SessionFactory ? В общем, если кто знает еще какие- нибудь способы, подскажите пожалуйста, либо если кто-нибудь применял или разбирается в тех, что я выложил, объясните пожалуйста преимущества их. |
| Автор: Старовъръ 10.5.2011, 23:28 |
| Ну фильтры в Хибе не на SessionFactory устанавливаются, а на Session. Вообще, глянь на Spring Security ACL или на другие реализации ACL, может тебе это поможет. По поводу пунктов - лично для меня первые два просто ересь |
| Автор: dobrolub 11.5.2011, 00:09 |
| С фильтром меньше кода... В javaee 7 народ фокусируется на multi-tenancy и cloud, поэтому наверно подход #3 более интересен. |
| Автор: carper 11.5.2011, 08:38 | ||
Ну, в том виде в каком обычно это приводят в примерах особого преимущества не просматривается, в реальности же можно переопределить метод openSession() и осуществлять централизованное управление безопасностью, возвращая сессию с уже установленными фильтрами. P.S. IMHO ваш пример, не очень хорош, т.к. прежде чем выносить управление безопасностью за пределы собственно базы данных надо очень и очень подумать, а стоит ли создавать столь лакомый уязвимый пункт, как незащищенная СУБД, полагающаяся полностью на то, что доступ к ней будет только из вашего приложения. Не говоря уж о том, что понятие представления (разумеется, это только небольшой кусочек системы безопасности) существует практически в любой СУБД, легко тюнится админом СУБД (в отличие от непредсказуемых запросов Hibernate) и очень удобно в использовании в том же Hibernate. Между прочим, и метод 2, при передаче заботы о безопасности на уровень СУБД, тогда будет выглядеть отнюдь не ересью, а вполне разумным подходом. |
| Автор: Samotnik 11.5.2011, 14:22 | ||||
не понимаю выгоду, если будет 100 тенантов, то ведь и будет 100 схем, а сто схем, это стопроцентные проблемы с кэшем второго уровня, памятью и прочим...
Вот это тоже не ясно. Вы предлагаете всю схему управления тенантами вынести на уровень БД ? |
| Автор: MisterCleric 11.5.2011, 14:43 |
| Привет. У меня такая вещь реализована через розграничение прав доступа к данным на уровне БД. При каждом request я делаю insert в некую сессионную временную таблицу информации о залогиненном пользователе. А данные вычитываю с помощью View, где уже и накладывается рестрикшн на данные, которые возвращать. Я так думаю, решение, которое http://habrahabr.ru/blogs/java/113721/ представлено вполне годится. Могу смело сказать, что мое решение соответсвует данным инструкциям. Разве что у меня нет привязки к конкртеному фильтру, так как на уровне представлений я могу какие-угодно понавешивать фильтрации в зависимости от прав пользователя, его групп, организаций, в которые он входит... |
| Автор: Samotnik 11.5.2011, 14:57 |
| MisterCleric, спасибо, почитал коменты, что-то понравилось, что-то нет. Но вопрос так и остался открытым, использовать Partitioning хорошо или плохо ? |
| Автор: MisterCleric 11.5.2011, 15:32 | ||
А зачем? ведь 100 - это не миллион... Да и не спасет оно тебя. Потому как "партиции" изначально предопределяются на уровне архитектуры БД. Т.е. "секьюрити" на них не постоишь. Ты попробуй сначала с простыми индексами, а потом, когда набется несколько миллионов записей, то и будешь и уже делить. |
| Автор: Samotnik 11.5.2011, 15:48 |
| MisterCleric, ничего не понял, можешь по-понятнее немного объяснить |
| Автор: MisterCleric 11.5.2011, 16:32 | ||
Давай по очереди: что именно не понятно? Если вообще, то партиции нужны при большом количестве данных, а так вообще обходятся обычными индексами по внешним ключам. Если по поводу того, как они создаются, то тогда это к твоему вендору СУБД. |
| Автор: carper 11.5.2011, 17:07 | ||||
Я разве сказал, что партиции надо пихать везде? Просто само их применение в определенных случаях обретает смысл. И не надо для 1000 схем создавать 1000 партиций, вы ведь безопасность настраивать не будете для всех 1000 пользователей по отдельности, а будете действовать через роли. Вот тут и можно эффективно выделить несколько групп ролей и осуществить партиционирование уже по ним. И вообще, не видя конкретной архитектуры и нагрузки на базу странно рассуждать о партиционировании более конкретно.
1. Только тесно завязанную на безопасность (имеется в виду не только защита от злоумышленников, но и логическая целостность критически важных данных). 2. Широко использовать view и, если у вас нет требования к независимости от вендера, другие возможности СУБД, предназначенные для таких вещей, например Oracle Label Security. 3. Остальное храните себе на ApplicationServer и используйте хоть запросы по id, хоть фильтры, когда последние будут полезны я вроде как уже написал. И вообще, не надо на сервере приложений пытаться моделировать тригеры, представления, foreign key, генераторы id - почти во всех, даже бесплатных базах, все это есть, работает эффективней и гораздо более отлажено и протестировано, позволяет задействовать админов СУБД, более профессионально решающих некоторые вопросы чем разработчики под JAVA. Уходя от вышеизложенного вы получите лишние 0,5% доп. свободы от вендора и лишние 50% гемороя по изобретению велосипеда и разыскивая знатоков Hibernate там, где даже JAVA знать не надо. |
| Автор: Samotnik 11.5.2011, 17:35 | ||||||
отвечу вопросом на вопрос. http://www.lunatech-research.com/archives/2011/03/04/play-framework-writing-multitenancy-application-hibernate-filters считается нормальным или нет ? Т.е. создать package-info.java в пакете с entity классами, там, в нём:
Затем, в каждый entity класс добавить поле tenant_id (из entity класса Tenant, по связи @ManyToOne), и в каждом классе применять фильтр:
ну и в сессию естессно добавить
|
| Автор: MisterCleric 11.5.2011, 17:40 |
| Почему нельзя это все вынести на уровень представлений в БД? Будет меньше компилированного кода на java. А не дай бог придется к каким-то сущностям добавить еще фильтров? Ну, в общем, дело мастера боится: раз такой способ есть, значит он имеет право на жизнь. Не бойся и эксперементируй |
| Автор: Samotnik 11.5.2011, 18:02 | ||
не понимаю, о чем речь и как это сделать ? |
| Автор: MisterCleric 11.5.2011, 18:45 | ||||||
Где: 1. Usersdepartmentses отделы, на которые есть право доступа у пользователя 2. usersession сессионная временная таблица, куда я укладываю перед выборкой залогиненного пользователя 3. Agents - таблица, на которую мне надо ограничить права доступа. и того у меня при отукрытии HibernateSession вызывается такое(псевдо код):
А потом просто делаю выборку:
|
| Автор: dobrolub 11.5.2011, 19:24 |
| Views построенные с использованием динамического аттрибута - хорошая идея. Я такую видел в работе, отлично работает. Дополнительный критерий к вопросу выбора: будут ли запросы аггрегативного характера, охватывающие всю базу? Если да - то, выполнить такой запрос по нескольким схемам потребует специального подхода. |
| Автор: Samotnik 11.5.2011, 19:50 |
| MisterCleric, впечатляет, но есть вопросы 1. Я так понимаю tenant - это user ? Если да, то содержится ли поле user в остальных сущностях ? Если да, то по связи или нет ? 2. Зачем временная таблица с именем залогиненного пользователя? 3. Что такое "отделы, на которые есть право доступа у пользователя" ? 4. Где и когда этот sql код исполнять ? |
| Автор: MisterCleric 12.5.2011, 10:18 | ||||||||||
| Привет. Давай попытаюсь ответить:
Это просто привел тебе пример моего кода. Я его не адаптировал под тебя. Зачем этот твой tenant держать во всех сущностях? Ведь в итоге у теб все-равно будет дерево объектов. И понятно, что всегда можно по внешним ключам добраться до вершины пирамиды - tenant. Что легко можно скрыть в таких вот View.
Потому как каждый пользователь ложа в такую таблицу записи видит только свои. И мне, что понимать на уровне БД, какой пользователь выполняет запросы, достаточно написать так:
И сразу же встречный вопрос: как ты будешь понимать какому tenant принадлежит пользователь, который выполняет действие?
Это такое разграничение прав доступа к данным в моей архитектуре. Суть сводиться к чему: контора, использующая мое приложение, имеет множество отделов по всей стране. Так вот необходимо показывать сделки пользователям строго по их региональной принадлежности. Плюс "объем региональности" растет с приближением к уровню столицы.
Мой EntityManager имеет TransactionScope. Я оборачиваю весь цикл запроса от пользователя в транзакцию. Т.е. у меня это делает http://seamframework.org/. И в фильтре перед вызовом бизнес логики у меня стоит вызов INSERT во временную таблицу. Поскольку все дальнейшие действия с БД выполняются в одной БД сессии, то и содержимое этой таблицы доступно везде и оно однозначно. Удачки тебе |
| Автор: Samotnik 15.5.2011, 21:59 |
| MisterCleric, спасибо. В итоге я добавил во все сущности tenant_id, и при занесении записи в БД, я заношу id того тенанта к которому относится эта запись. Ну а дальше дело техники, создал хибер фильтр, в который в сессию ложу критерий tenant_id. Этот способ нормальный ? |
| Автор: MisterCleric 16.5.2011, 10:34 | ||
Да, поскольку его тоже приводят в пример по данной теме |