 |


 |
|
 |
 |
 |
 |
Некоторые аспекты настройки JBoss Исходными данными для этой статьи является фирменная документация JBoss и сама статья собственно ни что иное как вольный пересказ этой документации. Она не претендует на полноту описаний, но может помочь новичку в JBoss быстро настроить некоторые из параметров. Настройка параметров соединения с базой данных с использованием шифрованных паролей Если мы посмотрим на файлы настройки data source в JBoss поставляемым по умолчанию, то заметим, что пароль пользователя под которым осуществляется соединение с базой данных хранится в открытом виде. На самом деле при грамотном администрировании это не является такой уж проблеммой или дырой в безопастности, но в определённых условиях может оказаться не приемлемым. И так, что же мы должны сделать чтобы "спрятать" пароль от взора постороннего? Для примера мы возьмём файл настройки для соединения с Oracle. Для "изъятия" пароля в явном виде, необходимо в файле $JBOSS_HOME$/server/<used_config>/deploy/oracle-ds.xml убрать все явные упоминания имени пользователя и пароля, а вместо этого, добавить строчку: <security-domain>EncryptDBPassword</security-domain> Где EncryptDBPassword – имя домена, описанного в файле файле $JBOSS_HOME$/server/<used_config>/cong/login-config.xml. Описание домена так же очень простое: <application-policy name = "EncryptDBPassword"> <authentication> <login-module code = "org.jboss.resource.security.SecureIdentityLoginModule" flag = "required"> <module-option name = "username">user</module-option> <module-option name = "password">шифрованный_пароль</module-option> <module-option name = "managedConnectionFactoryName">jboss.jca:service=LocalTxCM,name=имя_date source_из_oracle-ds.xml</module-option> </login-module> </authentication> </application-policy> Единственный вопрос, который остался, это как зашифровать пароль, чтобы вставить его в login-config.xml. Шифрованый пароль получается следующей командой: java -cp "libjboss-jmx.jar;libjboss-common.jar;serverdefaultlibjboss-jca.jar;serverdefaultlibjbosssx.jar" org.jboss.resource.security.SecureIdentityLoginModule <пароль> команду надо запустить в корне каталога, в котором установлен JBoss. Вот собственно и всё что надо сделать. Настройка работы через внешний web-сервер с балансировкой нагрузки web запросов. Документация JBoss рекомендует в качестве load balancer использовать Web server Apache с модулем mod_jk. Хотя это и не единственный способ обеспечить балансировку, я не буду изобретать ничего нового и будем следовать этим рекомендациям. Для настройки мы возьмём Apache версии 2.2.4, на самом деле вы можете взять любую версию Apache, единственное требование, это использовать "подходящий" модуль mod_jk. И так, качаем mod_jk подходящий для нашего Apache и копируем его в каталог $APACHE_HOME$/modules. Далее в файл $APACHE_HOME$/conf/httpd.conf надо добавить строку: Include conf/mod-jk.conf Создадим mod-jk.conf с примерно таким содержимым: # Load mod_jk module # Specify the filename of the mod_jk lib LoadModule jk_module modules/mod_jk.so # Where to find workers.properties JkWorkersFile conf/workers.properties # Where to put jk logs JkLogFile logs/mod_jk.log # Set the jk log level [debug/error/info] JkLogLevel info # Select the log format JkLogStampFormat "[%a %b %d %H:%M:%S %Y]" # JkOptions indicates to send SSK KEY SIZE JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories # JkRequestLogFormat JkRequestLogFormat "%w %V %T" # Mount your applications JkMount /CONTEXT/* loadbalancer # You can use external file for mount points. # It will be checked for updates each 60 seconds. # The format of the file is: /url=worker # /examples/*=loadbalancer #JkMountFile conf/uriworkermap.properties # Add shared memory. # This directive is present with 1.2.10 and # later versions of mod_jk, and is needed for # for load balancing to work properly JkShmFile logs/jk.shm # Add jkstatus for managing runtime data <Location /jkstatus/> JkMount status Order deny,allow Deny from all Allow from 127.0.0.1 </Location> И наконец создадим файл workers.properties, в котором хранятся описания или хостов для балансировки. # Define list of workers that will be used # for mapping requests worker.list=loadbalancer,status # Define Node1 # modify the host as your host IP or DNS name. worker.node1.port=8009 worker.node1.host=<JBoss host1> worker.node1.type=ajp13 worker.node1.lbfactor=1 worker.node1.cachesize=10 # Define Node2 # modify the host as your host IP or DNS name. worker.node2.port=8009 worker.node2.host= <JBoss host2> worker.node2.type=ajp13 worker.node2.lbfactor=1 worker.node2.cachesize=10 # Load-balancing behaviour worker.loadbalancer.type=lb worker.loadbalancer.balance_workers=node1,node2 worker.loadbalancer.sticky_session=1 worker.list=loadbalancer # Status worker for managing load balancer worker.status.type=status На этом настройка Apache закончена, можно перестартовать его и перейдти к настройкам JBoss. Для этого надо в файле $JBOSS_HOME$/server/<used_config>/deploy/jbossweb-tomcat55.sar/META-INF/jboss-service.xml установить параметр “UseJK” в true: <attribute name="UseJK">true</attribute> Кроме того, на каждом узле надо прописать имя node, соответствующее имени данного узла в файле workers.properties из настройки Apache. <Engine name="jboss.web" defaultHost="localhost" jvmRoute="node1"> Собственно всё, перестартуем JBoss. Теперь про обращении на хост с Apache по контексту CONTEXT, все запросы будут отсылаться на один из серверов JBoss. Самый простой кластер. И так мы выяснили как балансировать нагрузку web запросов. Теперь попробуем быстро организовать полноценный кластер в JBoss. В самом простом случае, достаточно запустить сервера с конфигурацией all. Делается это командой run.bat –c all. При условии, если каждый из серверов настроен в соответствии с этими инструкциями, они автоматически объединятся в кластер. Порядок запуска серверов не важен. Следует помнить, что в кластерной конфигурации развёртывание приложение производится копированием ear файла в папку farm а не в папку deploy. Кроме того, приложение разворачиваемое на сервере, должно быть готово для работы в условиях кластера. Для этого в файл WEB-INF/web.xml надо в случае отсутствия добавить
который должен идти сразу после корневого элемента <web-app>. Кроме того в файле WEB-INF/jboss-web.xml надо настроить репликацию сессий. <jboss-web> <replication-config> <replication-trigger>SET_AND_NON_PRIMITIVE_GET</replication-trigger> <replication-granularity>SESSION</replication-granularity> <replication-field-batch-mode>true</replication-field-batch-mode> </replication-config> </jboss-web> После этого, приложение необходимо пересобрать и развернуть на сервере. Полное описание настроек кластера можно посмотреть в JBoss Clustering guide.
|
 |
 |
 |
 |
|
 |
 |

 |
|
 |
 |
 |
 |
Выборка данных порциями Карпенков Сергей KarpenkovS@gmail.com Вступление Очень часто, особенно в Web приложениях, встаёт задача разбиения результатов большого запроса на страницы с определённым количеством записей на каждой. Конечно при использовании MySQL эта проблемма не стоит, разработчики этой базы сразу включили в свой диалект ключевое слово limit, но что же делать если у нас в качестве базы используется Oracle? Всё что мне удалось найдти в internet это совет использовать конструкции типа select top 10 но запрос этот очень медленный, и скорость его выполнения падает с каждой следующей выбираемой страницей. Два решения которые смог найдти я одно из них работает только в Oracle, второе по идее должно работать на любых базах поддерживающих ROWNUM. Основная идея состоит в том чтобы сразу получить все интервалы записей для каждой из возможной страницы. Конечно этот способ имеет свои недостатки в частности он не учитывает возможности удаления записей внутри интервалов, хотя в этом случае мы просто выберем меньше записей чем предполагали, ну и кроме того, по крайней мере один раз мы имеем full scan по индексу ну или если поле не индексировано, то по таблице (что конечно гораздо хуже), плюс естественно метод работает только для числовых идентификаторов и не предусматривает возможность сортировки по полям отличным от id. Использование оконных функций Oracle И так, первый метод состоит в использовании специальных функций Oracle появившихся если мне не изменяет память толи в 8й толи в 9й версии продукта. И так у нас есть таблица TABLE из двух полей, первое поле ID, последовательно заполняющееся из sequence и второе NAME - наименование, которое собственно нам и надо показать на нашей странице по 10 штук на страницу, причём показывать надо только наименования начинающиеся с "А". Запрос возвращающий нам границы каждой страницы будет выглядеть следующим образом: | SELECT * FROM (SELECT mn, mx FROM (SELECT MOD (ROWNUM - 1, 10) AS rn, ( MIN (ID) OVER (ORDER BY ID ROWS BETWEEN 1 PRECEDING AND 10 FOLLOWING)) mn, ( MAX (ID) OVER (ORDER BY ID ROWS BETWEEN 1 PRECEDING AND 9 FOLLOWING)) AS mx FROM TABLE WHERE NAME LIKE 'A%' ORDER BY ID ) WHERE rn = 0); | Результатом запроса будет набор данных из двух столбцов первый из которых будет первый Id страницы, а второй - последний, причём первый ID следующей записи равен последниму предидущей. Соответственно запрос выбирающий нужную страницу из базы будет выглядеть следующим образом: | SELECT * FROM TABLE WHERE subs_id >= :min AND subs_id < :max; | где min и max значения первой и второй колонок нужной строки результата первой выборки. Запрос с GROUP BY Но если честно скорость выполнения первого запроса оставляет желать лучшего, да и привязка к оконным функциям Oracle сильно ограничивает область применения данного решения. Попробуем написать запрос лишённый этих недостатков. | SELECT * FROM (SELECT MIN (id) AS min, MAX (id) AS max FROM (SELECT ROWNUM - MOD (ROWNUM, 10) AS ost, id FROM (SELECT DISTINCT id AS id FROM TABLE ORDER BY id)) GROUP BY max); | Удивительное дело, но не смотря на GROUP BY этот запрс выполняется быстрее чем предидущий. Хотя наверное возможна оптимизация и того и другого запроса. Единственное что еще стои сказать об отличии этого запроса это то, что в запросе для выборки данных строгое < должно быть заменено на <=. | SELECT * FROM TABLE WHERE subs_id >= :min AND subs_id <= :max; | Заключение Наверное существуют и другие способы решения данной проблеммы, мне пока удалось найдти только эти и я буду рад если это кому либо поможет в решении его повседневных задач. Copyright (2006) by Sergey Karpenkov
|
 |
 |
 |
 |
|
 |
 |

 |
|
 |
 |
 |
 |
Работа с persistence из Session Bean Карпенков Сергей KarpenkovS@gmail.com Вступление В предидущей части мы описали модель данных для нашей программы домашней видеотеки. Теперь нам надо реализовать набор методов для доступа и манипуляции данными. В нотации J2EE компоненты реализующие логику приложения называются Session Beans, которые в свою очередь могут быть двух типов, stateful - с сохранением состояния и stateless - без сохранения. Мы ограничимся пока stateless бином. Ну и прежде чем перейдти к делу, несколько слов о контейнере под которым мы будем работать. Все примеры проверялись под JBoss 4.0.4-GA с поддержкой EJB3, следовательно необходимо установить этот продукт. Ну и конечно нужно установить JDK версии 5. Вопрос конфигурирования JBoss для работы с приложением мы пока рассматривать не будем, это тема отдельной статьи, а просто продемонстрируем как в EJB3 создаются Session Bean и как работать с описанными ранее persistence. Session Bean И так, для того чтобы получить доступ к нашим persistence, создадим stateless session bean, который реализует все методы работы с данными. Но для начала нам необходимо описать интерфейс по которому мы будем вызывать нашь бин. Интерфейсы как известно бывают локальными и удалёнными. Мы ограничимся только удалённым интерфейсом. | package kid.business.interfaces; import kid.sample.Films.Films; import kid.sample.Films.MediaType; import kid.sample.Films.Janre; import javax.ejb.Remote; import java.util.ArrayList; @Remote public interface FilmsRemote { ArrayList getAllFilms(); void createFilm(String name, Integer grade, MediaType mType, ArrayList janres); ArrayList getFilmByName(String name); }
| И так, единственная аннотация, которая присутствует в этом интерфейсе, это аннотация @Remote указывающая на то, что методы описанные в этом интерфейсе будут доступны из вне контейнера. Теперь посмотрим на сам бин, реализующий этот интерфейс. | package kid.business; import kid.business.interfaces.FilmsRemote; import kid.sample.Films.Films; import kid.sample.Films.Janre; import kid.sample.Films.MediaType; import org.jboss.annotation.ejb.RemoteBinding; import javax.ejb.Stateless; import javax.persistence.*; import java.util.ArrayList; @Stateless @RemoteBinding(jndiBinding = "comp/sample/films/remote") public class FilmsSessionBean implements FilmsRemote { @PersistenceContext //(unitName = "SAMPLE") private EntityManager em; public ArrayList getAllFilms(){ ArrayList res = new ArrayList(); try { res = (ArrayList) em.createQuery("from Films") .getResultList(); } catch (Exception e) { e.printStackTrace(); } return res; } public void createFilm(String name, Integer grade, MediaType mType, ArrayList janres) { Films newFilm = new Films(); newFilm.setName(name); newFilm.setGrade(grade); newFilm.setMtype(mType); newFilm.setJanre(janres); try { em.persist(newFilm); } catch (Exception e) { e.printStackTrace(); } } public ArrayList getFilmByName(String name) { ArrayList res = new ArrayList(); res = (ArrayList) em.createQuery("from Films f where name like :nm") .setParameter("nm", name) .getResultList(); return res; } }
| Кратко пройдёмся по аннотациям класса. Этих аннотаций всего две, первая, это аннотация @Stateless говорящая о том, что наш бин не будет сохранять состояние, и вторая @RemoteBinding(jndiBinding = "comp/sample/films/remote") связывает бин с именем в JNDI, по которому этот бин будет доступен из приложения клиента. Строго говоря вторая аннотация не обязательна, по стандарту если она отсутствует, бин будет доступен по имени, которое совпадает с полным именем соответствующего интерфейса. На этом пока и всё, дальше переходим к "внутренностям" бина. Для работы с Persistence мы должны получить так называемый Entity Manager. Так как persistence являются POJO, они не хранят никакой информации ни о БД ни тем более о состоянии транзакций с этой БД. Для этой цели и используется Entity Manager. Entity Manager используется в связке с ещё одним новым понятием, Persistence Context. Пока мы не будем останавливать подробно на этом, а просто скажем что в приложении может быть множество Persistence Context, каждый из которых может иметь своё уникальное имя, и скаждым из них будет связан свой Entity Manager в таком случае мы должны указать имя Persistence Context'а который хотим использовать. И так, чтобы получить Entity Manager мы должны воспользоваться специальной аннотацией @PersistenceContext //(unitName = "SAMPLE") private EntityManager em; Обратите внимание, что по причине того, что у нас в конфигурации Persistence Context единственный, мы закоментировали параметр определяющий его имя. Далее напишем два метода, один из которых возвратит нам список всех фильмов, а другой, список фильмов названия которых соответствуют некоторому шаблону. Для получение информации сохранённой в БД используется язык, являющийся подмножеством SQL и называемый EJBQL. Он имеет несколько ограниченные возможности по сравнению скажем с Oracle SQL, но вполне достаточные для большинства операций с БД. Естественно язык является универсальным для всех баз данных, это значит, что вам по большому счёту не очень то и важно, какая конкретно БД используется для работы вашего приложения, точнее сказать вам это совсем не важно. Самой простой операцией является получение всех записей в таблице. ArrayList res = new ArrayList(); try { res = (ArrayList) em.createQuery("from Films") .getResultList(); } catch (Exception e) { e.printStackTrace(); } Для этого должны создать коллекцию объектов Films, в нащем случае это ArrayList, и присвоить этой коллекции результат выполнения запроса. Единственным интересным или скорее забавным моментом является то, что конструкцию "Select * from Entity" в EJB3 можно заменить на более короткую "from Entity" но сути это не меняет. Но на самом деле нам крайне редко надо получать список всех фильмов, чаще всего нам надо найдти некий фильм по установленному фильтру. Для этого напишем метод возвращающий список у которых название соответствует некоторому переданному шаблону, для этого просто в наш предидущий запрос добавим фразу "where name like :nm": ArrayList res = new ArrayList(); res = (ArrayList) em.createQuery("from Films f where name like :nm") .setParameter("nm", name) .getResultList();
далее привяжем к нашему параметру ":nm" конкретное значение, переданное параметром к методу, и получим список подходящих объектов. Обратите внимание, что мы используем like во фразе where, стало быть в качестве названия фильма, можем использовать регулярные выражения, такие как '%'. Ну и последнее, что мы сделаем в этой статье, это создадим новый объект типа Films. Для этого нам достаточно создать объект с помощью обычной операции new, заполнить все обязательные поля и сказать EntityManager'у чтобы он сохранил его для нас в базе: Films newFilm = new Films(); newFilm.setName(name); newFilm.setGrade(grade); newFilm.setMtype(mType); newFilm.setJanre(janres); try { em.persist(newFilm); } catch (Exception e) { e.printStackTrace(); }
Заключение. В этой статье мы постарались на простых примерах показать, как работают persistence в EJB3. В следующей части мы попробуем сделать более сложные вещи с нашими persistence. Copyright (C) 2006 by Sergey Karrpenkov
|
 |
 |
 |
 |
|
 |
 |

 |
|
 |
 |
 |
 |
EJB3 Persistence Карпенков Сергей KarpenkovS@gmail.com Вступление В новом стандарте EJB3 появилось такое средство для организации работы с БД как persistence. Persistence это Plain Old Java Object (POJO), дополненный аннотациями, позволяющими связать класс с таблицей реляционной БД. В этой статье я попробую рассказать как используется данное средство. Для начала надо понять, а почему собственно нам не подходят обычные средства для общения с БД, предоставляемые Java? Для этого вспомним, а что же мы имеем из стандартных механизмов работы с БД. А имеем мы практически только ResultSet с его бесконечными setXXX() и getXXX() методами, да ещё с зачатками навигации. Но даже не это главное, в конце концов по большому счёту, всё необходимое для манипуляции данными в ResultSet есть, главная беда состоит в том, что при работе с ResultSet мы полностью выпадаем из ООП и работаем не с сущностями и их аттрибутами с помощью предоставляемых нам интерфейсов, а с набором безликих данных. Entity Beans в EJB как раз и были спроектированы для того чтобы ликвидировать этот разрыв между реляционными наборами данных и объекнным подходом в языке программирования. Persistences же в EJB3 явились просто логическим продолжением идеологии Entity Beans нацеленными главным образом на упрощение разработки и отладки приложения а так же на возможность использовать данные инструменты вне контейнера приложений, и это действительно возможно и работает. За основу был взят новый инструмент, появившийся в Java 5 называющийся annotation. Использование аннотаций в частности позволило избавиться от дескрипторов развёртывания ну или по крайней мере сильно упростить их написание. Кроме того был значительно расширен язык запросов EJB QL, состояние которого в предидущих версиях стандарта просто не выдерживало никакой критики. Конечно Persistence в EJB3 появились не на пустом месте. Тем кто занимается Java давно и "в серьёз", хорошо известны такие продукты как Hibernate или TopLink, реализующие похожие идеологии, а то что эти подходы нашли своё отражение в новом стандарте говорит только о том, что приёмы реализованные в этих продуктах оказались исключительно удачными и стали пользоваться заслуженной популярностью. Описание предметной области И так, в качестве примера бы возьмём до крайности упрощённую задачу создания домашней видеотеки. ER диаграмма наших данных выглядит следующим образом. Не смотря на всю свою "дубовость" эта структура данных вполне подходит для иллюстрации базовых принципов и приёмов, а в случае необходимости её можно будет немного расширить. Описание persistence И так, в нашей схеме всего три сущности, это жанры, типы носителей и собственно фильмы. Каждая сущность описывается отдельной таблицей, кроме того существует ещё одна таблица, связывающая фильмы и жанры отношением многие ко многим. Теперь эту схему надо описать. Начнём с таблицы MrdiaType. package kid.sample.persistence.Films; import javax.persistence.*; import java.io.Serializable; @Entity @Table(name = "MediaType") public class MediaType implements Serializable { private Integer id; private String name; public MediaType() { } public MediaType(Integer id) { this.id = id; } @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name = "NAME") public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final MediaType mediaType = (MediaType) o; if (id != null ? !id.equals(mediaType.id) : mediaType.id != null) return false; return true; } public int hashCode() { return (id != null ? id.hashCode() : 0); } } Как результат описания, мы видим перед собой обычный класс дополненный несколькими аннотациями. Посмотрим внимательно на эти аннотации. Первой в с коде примера встречается @Entity. Данной аннотацией должен начинаться любой persistence класс, она говорит о том что класс является сущьностью. @Entity аннотация имеет единственный не обязательный параметр name, который используется для обращения к сущности в EJB-QL запросах, в случае, если не указано, то используется имя класса. Второй аннотацией в нашем описании идёт аннотация @Table, эта аннотация определяет основную таблицу БД для данной сущности, она имеет несколько больше параметров чем Entity, но мы использовали лишь один из параметров, указывающий имя таблицы в БД name=” MediaType ”. На этом описание самой сущности млжно считать законченым и мы перейдём к описанию атрибутов класса они же колонки таблицы, хотя далеко не всегда аттрибут должен соответствовать колонке. Из диаграммы мы помним, что в нашей таблице всего два поля, поле ID являющееся первичным ключом и поле Name, определяющее название конкретного типа носителя. Исходя из этого факта, создаём в классе два поля, Integer id и String name и методы доступа к ним. Далее добавляем аннотации к get-методам класса. Для поля name создаём единственную аннотацию @Column связывающую его с колонкой таблицы. Аннотация @Column имеет множество параметров, но нам пока будет достаточно единственного параметра с имененм name, значением которого будет имя поля в БД. С полем id сложнее, так как это поле связывается с первичным ключом таблицы, нам не обойтись только аннотацией @Column, хотя она тоже потребуется. Для констатации факта, что это данное поле связывается с первичным ключом используется аннотация @Id без каких бы то ни было параметров. Далее идёт более интересная аннотация @GeneratedValue, определяющая стратегию генерации значений для первичного ключа. Это достаточно важная часть описания, по этому мы остановимся на ней подробнее. Стратегий генерации существует четыре, TABLE, SEQUENCE, IDENTITY и AUTO. Стратегия TABLE говорит persistence provider’у, что значение для первичного ключа должны выбираться из таблицы и должен быть определён Table Generator, мы не будем останавливаться подробно на этой стратегии, во всяком случае пока, а перейдём к остальным. Стратегия SEQUENCE говорит о том, что уникальные значения для ключа будут браться из sequence базы данных. Для данной стратегии мы должны определить параметр generator, и создать Sequence Generator с помощью соответствующей аннотации @SequenceGenerator(name=”NAME”, sequenceName=”NAME_OF_SEQUENCE”). Стратегия IDENTITY используется для БД поддерживающих identity column в структуре таблицы, и наконец, стратегия AUTO, говорит о том, что способ генерации будет определятся типом и возможностями используемой БД. Эта стратегия применяется по умолчанию и совершенно нас устраивает. Ну и заканчиваем описание первичного ключа знакомой нам уже аннотацией @Column. Вот собственно и всё, что заслуживает внимание в нашем конкретном случае, все остальные части кода в нашем примере тривиальны и не требуют пояснения. Сущность Janre описывается точно по аналогии с MediaType, по этому мы не будем приводить её описание здесь, во всяком случае до того, как не перейдём к bidirectional связям. А вот описание сущности Films рассмотрим прямо сейчас. package kid.sample.persistence.Films; import javax.persistence.*; import java.io.Serializable; import java.util.Collection; @Entity @Table(name = "Films") public class Films implements Serializable { private Integer id; private String name; private Integer grade; private MediaType mtype; private Collection janre; public Films() { } public Films(Integer id) { this.id = id; } @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name = "NAME") public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(name = "GRADE") public Integer getGrade() { return grade; } public void setGrade(Integer grade) { this.grade = grade; } @JoinColumn(name = "MT") @ManyToOne(targetEntity = MediaType.class) public MediaType getMtype() { return mtype; } public void setMtype(MediaType mtype) { this.mtype = mtype; } @ManyToMany(targetEntity = Janre.class, cascade = CascadeType.PERSIST) @JoinTable( name = "Film2Janre", joinColumns = {@JoinColumn(name = "FIL_ID")}, inverseJoinColumns = {@JoinColumn(name="JANRE_ID")} ) public Collection getJanre() { return janre; } public void setJanre(Collection janre) { this.janre = janre; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Films films = (Films) o; if (id != null ? !id.equals(films.id) : films.id != null) return false; return true; } public int hashCode() { return (id != null ? id.hashCode() : 0); } } В этом описании есть несколько не знакомых нам элементов, которые представляют для нас интерес. Во первых это аннотация сопровождающая поле mtype. В декларации мы сразу замечаем, что поле связывается не идентификатором сущности а с самой сущностью. Для отражения этого факта служит аннотация @JoinColumn имеет множество параметров, в массе своей повторяющие параметры аннотации @Column и из которых мы пока использовали только параметр name, определяющий имя колонки в таблице. Далее идёт аннотация указывающая тип связи, в нашем случае это связь Many-To-One, которая обозначается аннотацией @ManyToOne. В качестве параметра этой аннотации мы передали связываемую сущность. Последнее в нашем описании, что заслуживает внимания, это описание связи типа Many-To-Many. Такие связи в реляционных БД организуются через кросс таблицы. В нашей ER диаграмме это таблица Film2Janre. Мы не будем описывать эту таблицу как сущность, пока нам это не надо. Так же мы не будем устанавливать bidirection связь между Films и Janre. Опишем самый простой вариант, когда связь организуется только в сущности Films. … @ManyToMany(targetEntity = Janre.class, cascade = CascadeType.PERSIST) @JoinTable( name = "Film2Janre", joinColumns = {@JoinColumn(name = "FIL_ID")}, inverseJoinColumns = {@JoinColumn(name="ID")} ) … Вобщем то аннотация @ManyToMany аналогична @OneToMany и мы не будем отдельно останавливаться на ней, а остановимся на @JoinTable, описывющей связывающую кросс таблицу. Имена и значения параметров в этой аннотации говорят сами за себя, name определяет имя кросс-таблицы, а параметры joinColumn и inverseJoinColumn определяют поля по которым связываются таблицы. Заключение И так в этой части мы создали описания наших сущностей. Следующий раз мы увидим как пользоваться получившимися описаниями. Copyright (2006) by Sergey Karpenkov
|
 |
 |
 |
 |
|
 |
 |

|
 |
|
 |