Hibernate Custom Field Support - технически статии и рецензии от Enterra

При внедряване на бизнес приложения на корпоративно ниво (Enterprise Scale), много често клиентът изисква да внедри поддръжка за разширяемостта на обектния модел на приложението, без да променя изходния код на системата. Използването на разширяем обектен модел (Extensible Domain Model) позволява без допълнителни усилия и разходи за разработване на нова функционалност:

1) използвайте приложението за по-дълъг период 2) модифицирайте процеса на системата с течение на времето и при промяна на външните обстоятелства 3) „настройте“ приложението към спецификата на предприятието, в което се внедрява.

Най-лесният и евтин начин за постигане на необходимата функционалност е внедряването на Extensible Business Entities в приложението с поддръжка на персонализирани полета (Custom Fields).

Какво е "персонализирано поле"?

Какво е "персонализирано поле" и какво дава на крайния потребител на системата? „Персонализирано поле“ е атрибут на обект, който не е създаден от разработчика на системата на етапа на разработка на системата, но е добавен от системния потребител към този обект по време на работа на системата, без да се правят промени в оригиналния код на приложението.

Как може да се изисква такава функционалност?

В този случай е по-добре да позволите на потребителя (или администратора) на системата да създаде атрибутите, които са необходими за работата на мениджърите по продажбите на определена организация.

Формулиране на проблема

По време на изпълнението на проекта Enterra CRM клиентът постави задачата да поддържа функцията Custom Fields в приложението в следната формулировка:

„Системният администратор трябва да може да създава/изтриваперсонализирано поле без необходимост от рестартиране на системата.

Рамката Hibernate 3.0 беше използвана за разработване на задния край на системата. Това обстоятелство (технологично ограничение) стана ключово, като се има предвид, че това изискване беше изпълнено.

Техническо решение

В този раздел ще дадем само ключовите точки за внедряване, като вземем предвид особеностите на използването на рамката Hibernate.

Тази демонстрационна реализация е разработена с помощта на: 1) JDK 1.5; 2) рамка на Hibernate 3.2.0; 3) MySQL 4.1.

Ограничения

За простота няма да използваме Hibernate EntityManager, Hibernate Annotations. Картографирането на постоянни обекти ще бъде изградено на базата на xml файлове за картографиране. Освен това си струва да се отбележи, че когато използвате Hibernate Annotations, горната реализация няма да работи, защото. той разчита на управление на файлове за картографиране на xml.

Нашата задача е да внедрим механизъм, който ви позволява да създавате/изтривате персонализирано поле в реално време, без да рестартирате приложението, да добавите някаква стойност към него и да се уверите, че тази стойност се отразява в базата данни на приложението. Освен това трябва да сме сигурни, че можем да използваме това потребителско поле в заявки.

модел на домейн

Нека започнем с факта, че се нуждаем от клас бизнес субект, върху който ще експериментираме. Нека да е клас Контакт. Той ще има две постоянни полета: id и name. Но освен тези постоянни и неизменни полета, този клас трябва да предостави някаква конструкция за съхраняване на персонализирани стойности на полета. Идеалната конструкция за тази цел е Map. Нека създадем общ базов клас за всички бизнес субекти, които поддържатпотребителски полета - CustomizableEntity, който съдържа Map CustomProperties за работа с персонализирани полета:

Списък 1 - Базов клас CustomizableEntity

Нека наследим нашия клас Contact от този базов клас:

Списък 2 - Класът Contact, наследен от CustomizableEntity.

Не забравяйте за файла за картографиране за този клас:

Списък 3 - Картографиране на класа Contact. Това, на което трябва да обърнете внимание е, че свойствата на id и name са форматирани като всички обикновени свойства, но за customProperties ние използваме етикета. Документацията на Hibernate 3.2.0GA ни казва, че целта на динамичния компонент е:

„Семантиката на картографиране е идентична с . Предимството на този вид картографиране е възможността да се определят действителните свойства на bean-а по време на внедряване, само чрез редактиране на документа за картографиране. Възможно е и манипулиране по време на изпълнение на документа за картографиране, като се използва анализатор на DOM. Дори по-добре, можете да получите достъп (и да промените) метамодела за конфигуриране на Hibernate чрез обекта за конфигурация.

Всъщност, въз основа на тази разпоредба от документацията на Hibernate, ние ще изградим механизма за тази функция.

HibernateUtil и hibernate.cfg.xml

След като сме избрали домейн модела на нашето приложение, трябва да създадем необходимите условия за функционирането на Hibernate framework. За да направите това, трябва да създадете конфигурационен файл hibernate.cfg.xml и клас за работа с основните функции на Hibernate.

Листинг 4 е конфигурационният файл на Hibernate.

Файлът hibernate.cfg.xml не съдържа нищо забележително, освен може би реда:

Списък 5 - използване на автоматична актуализация.

По-късно ще се спра на нейното назначение и ще кажа как може да се мине без нея. Има няколко реализации на класа HibernateUtil. Нашата реализация ще бъде малко по-различна от познатите ми именно защото ще трябва да променим конфигурацията на Hibernate в процеса на работа.

Листинг 6 - Клас HibernateUtils. Наред с обичайните методи като getCurrentSession(), getConfiguration(), които са необходими за нормалната работа на приложение, базирано на Hibernate, ние сме внедрили методи като: reset() и getClassMapping(Class entityClass). В метода getConfiguration() ние конфигурираме Hibernate и добавяме класа Contact към конфигурацията. Методът reset() се използва за затваряне на всички ресурси, използвани от Hibernate, и нулиране на всички негови настройки:

Списък 7 - метод reset() Методът getClassMapping(Class entityClass) връща обект PersistentClass, който съдържа пълна информация за картографирането на предадения му обект. Именно манипулациите с обекта PersistentClass правят възможно модифицирането на състава на атрибутите на класа на обекта в режим на изпълнение.

Списък 8 - метод getClassMapping(Class entityClass).

Картографски манипулации

След като имаме класа на бизнес обект (Контакт) и основния клас за взаимодействие с Hibernate, можем да започнем. Можем да създаваме и запазваме екземпляри на класа Contact. Можем дори да поставим някои данни в customProperties на нашата карта, но трябва да сме наясно, че тези данни (съхранени в customProperties на картата) няма да се съхраняват в базата данни. За да се запазят, трябва да предоставим механизъм за създаване на персонализирани полета в нашия клас и Hibernate да се научи да работи с тях. За да можем да манипулираме картографирането на клас, трябва да създадем някакъв интерфейс, за да го направим. Нека го наречем CustomizableEntityManager. Неговото иметрябва да отразява целта на този интерфейс - управление на бизнес обект, неговото съдържание, неговите атрибути:

Списък 9 – Интерфейс CustomizableEntityManager Основните методи на този интерфейс естествено са: void addCustomField(Име на низ) и void removeCustomField(Име на низ). Което трябва да създаде и изтрие нашето потребителско поле в картографирането на съответния клас. Следното е изпълнението на този интерфейс:

Списък 10 – внедряване на интерфейса CustomizableEntityManager На първо място, трябва да се отбележи, че когато създаваме класа CustomizableEntityManager, ние указваме кой действителен клас бизнес обект ще управлява този мениджър. Този клас се предава като параметър на конструктора CustomizableEntityManager:

Списък 11 – Конструктор на клас CustomizableEntityManagerImpl Сега сме най-заинтересовани от имплементацията на метода void addCustomField(String name):

Списък 12 - Създаване на потребителско поле. Както можете да видите от реализацията, Hibernate предоставя големи възможности за работа със свойствата на постоянни обекти и показването им в базата данни. По същество този метод: 1) Създаваме обект от класа SimpleValue, който ни позволява да посочим как стойността на това персонализирано поле ще се съхранява в базата данни - в кое поле и в коя таблица на базата данни:

Листинг 13 - създаване на колона в таблица.

2) Създаваме свойство на постоянния обект и го добавяме към динамичния компонент (!), който планирахме да използваме за тази цел:

Листинг 14 - създаване на свойство на обект.

3) Последното нещо, което трябва да направим, е да накараме нашето приложение да направи съответните промени в xml файловете и да актуализира конфигурацията на Hibernate. Това се прави чрез метода updateMapping(); Задължителноза да се изясни целта на още две извиквания на метод, които бяха направени в кода, описан по-горе. Първият метод е getCustomProperties():

Списък 15 - Получаване на CustomProperties като компонент. Този метод намира и връща обекта Component, съответстващ на етикета в нашето картографиране на бизнес обект. Вторият метод е updateMapping():

Листинг 16 - метод updateMapping(). Той отговаря за запазването на модифицираното съпоставяне на класа на устойчивост и за нулиране на състоянието на конфигурацията на Hibernate, така че следващия път, когато бъде достъпен, направените от нас промени ще влязат в сила. Между другото, тук си струва да се върнем към реда:

Конфигурация на хибернация. Ако този ред не съществуваше, тогава ще трябва също да започнем да актуализираме схемата на базата данни с помощта на Hibernate. Но използването на посочената настройка ни позволява да не правим това.

Запазване на картографирането

Модификациите на картографирането, направени по време на изпълнение, не се записват сами в съответния xml файл за картографиране и за да влязат в сила промените при следващото стартиране на приложението, трябва ръчно (фалшиво) да запазим промените в съответния файл за картографиране. За това ще използваме класа MappingManager, чиято основна задача е да запази картографирането на посочения бизнес обект в неговия xml файл за картографиране:

Листинг 17 е помощна програма за актуализиране на картографирането на постоянен клас. Този клас прави буквално следното: 1) Намира и зарежда xml картографирането за посочения бизнес обект в обект на DOM документ за последващо манипулиране с него; 2) Намира елемент от дадения документ. Именно в него съхраняваме потребителски полета и променяме съдържанието му; 3) Премахнете (!) всички вложени елементи от този елемент; 4) За всекипостоянно свойство (Property), съдържащо се в нашия Компонент, отговорен за съхраняване на персонализирани полета, създаване на съответния елемент на документа и задаване на атрибутите на този елемент от съответния обект Property; 5) Запазете този новосъздаден файл за картографиране. Когато манипулираме XML, ние използваме (както можете да видите от кода) класа XMLUtil, който по принцип може да бъде имплементиран по всякакъв начин, стига правилно да зарежда и записва xml файла. Нашето внедряване е показано в следния списък:

Списък 18 е помощна програма за манипулиране на XML.

Тестване

Сега, когато целият работещ код, от който се нуждаем, е написан, можем да пишем тестове и да видим как работи всичко. Първият тест ще създаде персонализирано имейл поле, ще създаде и запише обект на клас контакт и ще зададе свойството му за имейл. Първо, нека да разгледаме таблицата с данни tbl_contact. Съдържа две полета: fld_id, fld_name. Тестовият код е по-долу:

Листинг 19 е тест за създаване на потребителско поле. Този метод прави следното: 1) Създава CustomizableEntityManager за класа Contact; 2) Създава ново потребителско поле в него, наречено имейл; 3) След това в транзакцията създайте нов контакт и задайте потребителското поле на „[имейл защитен]“; 4) Запазване на контакта; 5) Вземете стойността на персонализираното поле „имейл“. В резултат на изпълнението виждаме следното:

Списък 20 е резултатът от теста. В базата данни виждаме следния запис:

Списък 21 - Резултат в базата данни. Както виждаме, новото поле е създадено по време на изпълнение и стойността на това поле е запазена успешно. Вторият тест прави заявка към базата данни, използвайки новосъздаденото поле:

Списък 22 - Тест за персонализирано поле за заявка. Резултат от изпълнението:

Списък 23 - Резултатът от заявката. Както виждаме, потребителското поле, създадено от нашата технология, е много лесно за участие в заявки към база данни.

Доразвиване на идеята

Очевидно е, че изпълнението, дадено в тази статия, е доста примитивно. Той не отразява цялото разнообразие от опции, които възникват при работа с тази функционалност. Той обаче показва общия механизъм на действие на това решение върху предложената технологична платформа.

Това внедряване поддържа само типа String като персонализирани полета, но в реално приложение, изградено на базата на този подход (Enterra CRM), е внедрена пълна поддръжка за всички примитивни типове, както и типове обекти (препратки към бизнес обекти) и полета за събиране.

За да се поддържат персонализирани полета в частта за потребителския интерфейс, беше внедрена система от мета-дескриптори на потребителски полета, които бяха използвани от системата за генериране на потребителски интерфейс. Но механизмът на работа на този генератор е тема за отделна статия.

Заключение

В резултат на работата на екипа на проекта Enterra CRM беше разработена, тествана и внедрена на практика архитектурата на разширяем обектен модел, базиран на платформата Hibernate ORM, което позволи да се задоволят нуждите на клиента по отношение на персонализиране на приложението към нуждите на крайните потребители в режим на работа на системата, без да е необходимо да се правят промени в изходния код на приложението.

Този запис беше публикуван във вторник, 31 юли 2007 г. в 11:05 сутринта и се намира в Hibernate, Java.