Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Hibernate. Связь many-to-many


Автор: Shaggie 27.6.2007, 13:56
Существует БД:

Код

CREATE TABLE cd (
cd_id INT PRIMARY KEY,
cd_name VARCHAR(255)
);

CREATE TABLE cd_style (
cd_id INT REFERENCES cd,
st_id INT REFERENCES style,
UNIQUE(cd_id, st_id)
);

CREATE TABLE style (
st_id INT PRIMARY KEY,
st_name VARCHAR(255)
);


Связь между таблицами cd и style осуществляется по принципу many-to-many, для чего существует сводная таблица cd_style.

Маппинг таблицы style:
Код

<class name="Style" table="style">
    <id name="id" column="st_id">
        <generator class="native"/>
    </id>
    <property name="name" column="st_name"/>
<class/>


Маппинг таблицы cd:
Код

<class name="Cd" table="cd">
    <id name="id" column="cd_id">
        <generator class="native"/>
    </id>
    <property name="name" column="cd_name"/>

    <set name="styles" table="cd_style">
        <key column="cs_cd"/>
        <many-to-many column="cs_style" class="db.Style"/>
    </set>
<class/>

Всё работает, в программе после вызова Cd.getStyles() генерится собственный сет объектов Style. Это нормально.

Но неэффективно с точки зрения логики программы. Гораздо правильнее было бы использовать коллекцию TreeMap. Но я не могу написать маппинг! Кто-нибудь сталкивался с необходимостью создавать мап объектов? В моём примере ключом, по идее, должен оказаться INT PRIMARY KEY таблицы style, а значением - соответствующий объект Style.

Возможные варианты:
Код

    <map name="styles" table="cd_style">
        <key column="cs_cd"/>
        <map-key ... /> <!--  ??? что написать ??? -->
        <many-to-many column="cs_style" class="db.Style"/>
    </map>

<!-- либо -->

    <map name="styles" table="cd_style">
        <key column="cs_cd"/>
        <map-key-many-to-many column="cs_style" class="db.Style"/>
        <element type="Style"/>
<!-- Если не указать в теге element параметр column, то выдаёт ошибку парсинга и требует его написать.
А если параметр существует, например, column="st_id", ругается: в указанной колонке НЕ тип Style -->
    </map>

Уже голова пухнет, но ответа так и не нашёл. Если можете - помогите, пожалуйста!

Автор: Shaggie 4.7.2007, 12:15
Спасибо powerOn, который запостил в одном из обсуждений ссылку на http://www.java2s.com. Там я нашёл вариант решения проблемы, но он меня не очень устроил.

Как вы могли заметить, в элементе Map должно быть ещё три элемента, причём у каждого необходимо указать колонку из таблицы, с которой делается маппинг. То есть в промежуточной таблице должно быть ТРИ колонки! С одной стороны, это непорядок. С другой, тогда всё действительно работает.

В результате промежуточная таблица в базе выглядит так:
Код

CREATE TABLE cd_style (
    cd_id INT,
    st_key INT,
    st_id INT
);


а мап маппинга (извините... слова почти одинаковые, а понятия разные) таблицы cd так:
Код

    <map name="styles" table="cd_style">
        <key column="cd_id"/>
        <map-key column="st_key" type="java.lang.Integer"/>
        <many-to-many column="st_id" class="db.Style"/>
    </map>


Необходимые пояснения.
Число cd_id - это тот объект Cd, у которого будет свой мап объектов Style.
Число st_key - это объект типа Integer, который будет ключом (key) к для каждого Style в мапе.
Число st_id - это указатель на тот объект Style, который будет значением (value) в мапе.

Теперь после вызова Cd.getStyles() генерится собственный мап объектов Style, и это есть как раз то, что мне было нужно.

НО!

Появляются определённые издержки. Теперь запрос SELECT * FROM cd_style; вернёт такое вот чудо:

 cd_id | st_key | st_id
------------------------
     1 |      1 |     1
     1 |      3 |     3
     2 |      1 |     1
     2 |      6 |     6
     2 |      7 |     7

В результате таблица распухает на треть безо всякой пользы для цивилизации. Налицо подгон базы под hibernate, а ведь по идее он должен работать с тем, что дают, и уж тем более не ломать нормализацию... Однако повторюсь, что сохранять зависимость many-to-many наиболее эффективно всё-таки в мапе.

Поэтому тема остаётся открытой. Есть ли способы замапить связь, используя только ДВЕ колонки в промежуточной таблице?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)