УДК 519.6
Разработка кросс-платформенного, кросс-браузерного модулей ввода информации в базу данных
Д.Е. Белов, к. б. н.,
А.Ф. Шалин, гл. специалист по информационной безопасности И.Н. Воронкина, преподаватель, Ставропольский региональный колледж вычислительной техники и электроники
Технология объектно-реляционного связывания (ORM - object relation mapping), основанная на реализации интерфейса java persistence API (JPA) провайдером Hibernate, предусматривает разработку доменной модели классов для организации взаимодействия СУБД с java-программой. Причем, как было показано в предыдущих исследованиях, при организации такого взаимодействия необходимо учитывать, что объектная модель накладывает ряд ограничений на реляционную. А именно, в результате реализации подобной технологии мы имеем следующие побочные эффекты:
a) в объектной модели перестают работать основные концепции теории множеств;
b) класс должен являться проекцией отношения базы данных, то есть, описывая класс, мы должны на этапе написания программы задать все его поля и методы для извлечения, сохранения, обновления и удаления данных.
ORM предусматривает организацию связывания реляционной и объектной моделей следующим образом:
- Отношение > Контейнер объектов;
- Кортеж > Объект класса;
- Домен > Поле класса.
Положительным эффектом технологии ORM является возможность мягкой смены СУБД в рамках реализации JPA. Другими словами, именно это и обусловливает кросс-платформенность на уровне базы данных. Хотя для данного эффекта в английском языке есть более специализированный термин «Cross-database», однако, так как в русском аналоге «Кросс-база-данность» не получил широкого распространения, в своей работе под кросс-платформенностью мы подразумеваем не только возможность миграции между различными операционными системами, но и между системами управления базами данных.
Что касается кросс-браузерности, в это понятие мы вкладываем довольно стандартное значение, которое присуще большинству авторов. А именно, под кросс-браузерностью понимается свойство сайта отображаться и работать во всех популярных браузерах идентично, то есть способность отображать материал с одинаковой степенью читабельности. При разработке доменной модели классов было необходимо реализовать методы для поддержки CRUD (Create,
read, update and delete) - операций.
Так как реализация данных методов для каждого из классов является весьма рутинной и дорогой задачей, возникло естественное желание организовать обобщённые методы для большинства классов, которые позволят реализовать CRUD-операции. Для реализации данной идеи было принято решение использовать такие парадигмы языка java, как наследование и полиморфизм, так как интерфейс JPA поддерживает эту концепцию. Причем JPA позволят указать, как именно хранить данные наследуемых классов, например, если мы организуем иерархический справочник, то все данные разумно хранить в одной таблице в базе данных, однако классы в программе для доступа к каждому уровню иерархии необходимо реализовать отдельно.
Рисунок 1 - Графическое представление доменной модели классов.
Организация основных операций с базой предусматривала следующие функциональные возможности:
1) получение и сохранение ip-адреса пользователя при реализации любых операция с объектами БД;
2) получение и сохранение уникального идентификатора пользователя в системе;
3) получение и сохранение времени операции;
4) организация метода remove так, чтобы физически данные из БД не удалялись, а у пользователя складывалось впечатление об их удалении.
Для решения поставленных задач был организован основной класс -SuperAbstractBean, отвечающий за основную часть операций CRUD, кроме операции чтения. Для операции чтения возникла необходимость реализовывать более специализированные классы, например, такие как SuperReport для чтения информации об отчетах и SuperAbstractUnit для иерархического справочника местонахождения (рис.1).
При этом сами операции реализованы следующим образом:
1) Create:
@Transactional public void persist() {
if (this.entityManager == null) this.entityManager = entityManager();
if (WebApplication.get()!= null){
WebApplicationContext wc = ((WebApplicationContext) WebApplication.get().getContext()); setUserCreateID(WebApplication.get().getUser().getId()); setUserCreateIP(wc.getBrowser().getAddress()); setUserCreateTime(new Date());
}
this.entityManager.persist(this);
}
2) Update:
@Transactional
public SuperAbstractBean merge() { if (WebApplication.get()!= null) {
WebApplicationContext wc = ((WebApplicationContext) WebApplication.get().getContext());
if (this.entityManager == null) this.entityManager = entityManager(); this.setUserModifyID(WebApplication.get().getUser().getId()); this.setUserModifyIP(wc.getBrowser().getAddress()); this.setUserModifyTime(new Date());
SuperAbstractBean merged = this.entityManager.merge(this);
this.entityManager.flush();
return merged;
}
else {
if (this.entityManager == null) this.entityManager = entityManager(); SuperAbstractBean merged = this.entityManager.merge(this); this.entityManager.flush();
return merged;
}}
3) Remove:
@Transactional public void remove() {
if (WebApplication.get()!= null) {
WebApplicationContext wc = ((WebApplicationContext)
WebApplication.get().getContext());
if (this.entityManager == null) this.entityManager = entityManager(); if (this.entityManager.contains(this)) { this.setUserDeleteID(WebApplication.get().getUser().getId()); this.setUserDeleteIP(wc.getBrowser().getAddress()); this.setUserDeleteTime(new Date()); this.falseDelete = true;
}
else {
SuperAbstractBean attached = findSuperAbstractBean(this.id); this.setUserDeleteID(WebApplication.get().getUser().getId()); this.setUserDeleteIP(wc.getBrowser().getAddress()); this.setUserDeleteTime(new Date()); this.falseDelete = true;
}
}
else {
if (this.entityManager == null) this.entityManager = entityManager(); if (this.entityManager.contains(this)) { this.entityManager.remove(this);
}
else {
SuperAbstractBean attached =
SuperAbstractBean. findSuperAbstractBean(this.id); this.entityManager.remove(attached);
}
}Что касается операции чтения (Read), то в нашем случае она была достаточно специализированной, и её реализация зависела от того, что именно мы хотели показать пользователю. Например, в классе SuperReport, который является базовым для всех отчетов и в то же время наследником класса SuperAbstractBean, операция чтения была реализована следующим образом:
4) Read для выборки данных по соответствующей подотчетной единице и дате составления отчета:
@SuppressWarnings("unchecked")
public static List<? extends SuperReport> findAllReportsByRayonAndDate(Class<? extends SuperReport> report, ReportingDate dateBean) { if (WebApplication.get()!= null){
return entityManager().createQuery("SELECT o FROM " + report.getSimpleName() + " o WHERE o.reportingDateId = :repDate") .setParameter("repDate", dateBean)
.getResultList();
}
}
Такой подход к реализации классов доменной модели является достаточно универсальным и позволяет сократить затраты на написание кода для каждого класса. В результате появляется возможность работать практически со всеми классами в системе как с SuperAbstractBean, используя восходящее предобразование, а со всеми классами, предназначенными для хранения отчётной информации, как с SuperReport и т.д. (рис. 1).
Для подтверждения эффекта кросс-платформенности был проведен эксперимент, характеризующий работоспособность приложения при взаимодействии с системами управления базами данных Oracle, MySQL и FireBird.
При организации данного эксперимента основными проблемами миграции между различными СУБД были следующие:
1) длина названия полей и таблиц;
2) различия в генерации первичных ключей.
Рассмотрим каждую из этих проблем более подробно. Длина названия полей и таблиц в различных СУБД может существенно различаться, например:
1) Oracle - 30 знаков;
2) FireBird - 31 знак;
3) MySQL- 64 знака.
Из данной ситуации существует два выхода: первый наиболее простой - назвать поля и таблицы таким образом, чтобы они не превышали 30 знаков. Но что делать, если, например, мы уже попали в подобную ситуацию, мигрируя между MySQL и Oracle? В этой ситуации становится полезной аннотация @column, позволяющая реализовать новое имя в базе данных, не совпадающее с названием поля класса. Например, благодаря коду, представленному ниже, имя поля в базе данных будет version, без спецификатора, при использовании провайдера Hibernate, оно будет таким: Version_Super_Abstract_Bean. @Version
@Column(name = "version")
private Integer versionSuperAbstractBean;
Что касается механизмов генерации первичных ключей, то для получения автоинкрементного поля с шагом 1 в СУБД MySQL был применен следующий код:
@Id
@GeneratedValue(strategy = GenerationType. AUTO)
@Column(name = "id") private Long id; для СУБД Oracle такой:
@Id
@Column(name = "id") protected Long id;
однако пришлось написать дополнительный метод и класс.
При тестировании программы на Oracle наиболее стабильным показал себя следующий подход:
1) был написан класс Identifier, исходный код приведен ниже: package ru.sniizhk.f24a.mixedbean;
@Transactional
@Configurable
@RunWith(SpringJUnit4ClassRunner. class)
@ContextConfiguration(locations =
"classpath:/META-INF/spring/applicationContext.xml") public class Identifier {
public static final Logger log = Logger.getLogger(Identifier.class);
@PersistenceContext
transient EntityManager entityManager;
private Long nextId;
@Test
/** В переменной s передается название класса */ public void sqlCallfromDual(String s){
HibernateEntityManager hem = entityManager.unwrap(HibernateEntityManager.class);
Session session = hem.getSession();
List l = session.createSQLQuery("select SQ_" + s + "_ID.nextval as nextMyId from dual").list(); log.info(" session.createSQLQuery l = " + l.get(0)); this.nextId = Long.parseLong(l.get(0).toString());
}
public Long getNextID(){ return this.nextId;
}
}
2) В классе SuperAbstractBean был добавлен следующий метод: @PrePersist // Событие возникает перед операцией создания (Create) public void setNextId(){
if (this. id == null){
Identifier i = new Identifier();
if(this.getClass().getSimpleName().equals("Region") || this.getClass().getSimpleName().equals("Rayon") || this.getClass().getSimpleName().equals("Company")){ i.sqlCallfromDual("Unit");
} else {
i.sqlCallfromDual(this.getClass().getSimpleName());
}
setId(i.getNextID());
}
}
Что касается кросс-браузерности, для тестирования работоспособности приложения использовались следующие просмотрщики:
1) Mozilla Firefox. Версия 11.0;
2) Google Chrom. Версия 19.0;
3) Internet Explorer. Версия 9.0;
4) Opera. Версия 12.11;
5) Safari. Версия 5.1.
Для решения проблемы одинакового отображения информации в браузерах различных производителей использовались open source, библиотеки, позволяющие генерировать java script и HTML на стороне клиента.
Литература:
1. Абонеев, В.В. Методика оценки мясной продуктивности овец /В.В. Абонеев, Ю.Д. Квитко, И.И. Селькин и др. //Ставрополь, 2009. -34с.
2. Абонеев, В.В. Стратегия развития овцеводства в Российской Федерации /В.В. Абонеев //Достижения науки и техники АПК. -2008.-№10. -С.37-39.
3. Абонеев, В.В. Приемы и методы повышения конкурентоспособности товарного овцеводства /В.В.Абонеев, Л.Н.Скорых, Д.В. Абонеев //ГНУ СНИИЖК, Ставрополь. -2011. -337с.
4. Абонеев, В.В. Перспективные направления селекции овец в условиях рыночной экономики /В.В.Абонеев, А.Н. Соколов //Овцы, козы, шерстяное дело. -2007. -№ 1. -С.7-11.
5. Абонеев, В.В. Развитие тонкорунного овцеводства в России /В.В. Абонеев, В.В. Марченко, А.И. Суров, А.А. Пикалов, А.И. Ерохин, С.А. Карасев //Овцы. Козы. Шерстяное дело. -2012. -№ 2. -С.6-13.
6. Василенко, В.Н. Племенная база овцеводства Ростовской области /В.Н. Василенко, Ю.А. Колосов //Зоотехния, 2002. -№8. -С.9-12.
7. Завгородняя, Г.В. Мясная продуктивность баранчиков разных генотипов и уровней кормления /Г.В. Завгородняя, Ю.Д. Квитко, И.И. Дмитрик, И.П. Будякова, И.Н. Шарко //Овцы. Козы. Шерстяное дело. -2012. - № 2- С. 42-44.
8. Квитко, Ю.Д. Проблемы и новые подходы в организации производства баранины /Ю.Д. Квитко //Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2005. -Т. 1. -№2. -С.3-7.
9. Квитко, Ю.Д. Мясная продуктивность и качество мяса молодняка овец разного происхождения /Ю.Д. Квитко, А.В. Скокова, С.Ф. Силкина //Овцы. Козы. Шерстяное дело. - 2012. -№ 2. -С.39-41.
10. Ковалюк, Н.В. Молекулярно-биологические методы для оздоровления стад крупного рогатого скота от лейкоза /Н.В. Ковалюк //Ветеринария. -2008. -№2. -С.22-26.
11. Ковалюк, Н.В. Методические рекомендации по использованию маркера BoLA DRB3 в селекционно-племенной работе с крупным рогатым скотом: Рекомендации СКНИИЖ /Н.В. Ковалюк, В.Ф. Сацук //Краснодар, 2010. -24с.
12. Ковалюк, Н.В. Использование генетического маркера BOLA-DRB3 для оптимизации селекционного процесса при скрещивании /Н.В. Ковалюк, В.Ф. Сацук //Молочное скотоводство.-2010.-№2.-С.10-12.
13. Ковалюк, Н.В. Современные методы диагностики лейкоза крупного рогатого скота /Н.В. Ковалюк, В.Ф. Сацук, Е.В. Мачульская //Ветеринария Кубани. -2007. -№ 1. -С.18-20.
14. Колосов, Ю.А. Информационное сопровождение селекционного процесса в овцеводстве: учебное пособие /Ю.А. Колосов, А.И. Бараников, В.Н. Василенко, Н.В. Михайлов //Изд-во Донского ГАУ, пос.Персиановский, 2012. -55с.
15. Колосов, Ю.А. Продуктивность молодняка породы советский меринос и её помесей с эдильбаевскими баранами /Ю.А. Колосов, С. В. Шихов //Овцы, козы, шерстяное дело. -2006. -№ 3. -С.7-10.
16. Колосов, Ю.А. Модель организации селекционно-племенной работы в овцеводстве с применением метода зависимых уровней отбора/Ю.А. Колосов, И.В. Засемчук //Новочеркасск, 2008.-С.76.
17. Новопашина, С.И. Переваримость питательных веществ рациона молочными козами при разном уровне протеина /С.И. Новопашина, Ю.Д. Квитко, М.Ю. Санников, Е.И. Кизилова //Овцы. Козы. Шерстяное дело. -2012. -№ 2. -С.64-67.
18. Ольховская, Л.В. Иммуногенетический анализ в селекции коз /Л.В. Ольховская, Л. Н. Чижова и др. //Ставрополь. ВНИИОК, 2000. -С.3-11.
19. Тимошенко, Н.К. Состояние и перспективы развития первичной обработки шерсти /Н.К. Тимошенко //Овцы, козы, шерстяное дело. -2007. -№ 4. -С.46-50.
20. Тимошенко, Н.К. Рынок шерсти: состояние и тенденции развития /Н.К. Тимошенко, В.В. Абонеев //Овцы, козы, шерстяное дело, 2012. -№
2. -С.50.
21. Чижова, Л. Н. Биохимические тест-системы, генетические маркеры продуктивности, их использование в селекции овец /Л.Н. Чижова //Автореф. дисс.... д-ра с.-х. наук. -Ставрополь, 2004. -58с.
22. Чижова, Л. Н. Методические рекомендации комплексной оценки крупного рогатого скота мясных пород по фенотипу и генотипу /Л.Н. Чижова и др. //Ставрополь, 2008. -52с.