У меня есть простое сопоставление «многие ко многим»:
@Entity
@Table(name="ITEM")
public static class Item
{
@Id
@GeneratedValue
long id;
@Column
String name;
@ManyToMany(cascade={ CascadeType.ALL })
Set<Category> categories = new HashSet<Category> ();
}
@Entity
@Table(name="CATEGORY")
public static class Category
{
@Id
@GeneratedValue
long id;
@Column
String name;
}
И этот контрольный пример:
public void testPersist() throws Throwable
{
Category one = new Category ();
one.name = "one";
em.persist (one);
System.out.println ("one.id="+one.id);
Category two = new Category ();
two.name = "two";
em.persist (two);
System.out.println ("two.id="+two.id);
Item item = new Item ();
item.name = "item";
item.categories.add (one);
item.categories.add (two);
em.persist (item);
long id = item.id;
System.out.println ("item.id="+item.id);
System.out.println ("item.categories="+item.categories);
em.clear ();
item = em.find (Item.class, id);
System.out.println ("item.categories="+item.categories);
assertEquals (item.categories.toString (), 2, item.categories.size ());
}
Тест не пройден. В журнале я вижу:
SchemaUpdate - create table CATEGORY (id bigint generated by default as identity (start with 1), name varchar(255), primary key (id))
SchemaUpdate - create table ITEM (id bigint generated by default as identity (start with 1), name varchar(255), primary key (id))
SchemaUpdate - create table ITEM_CATEGORY (ITEM_id bigint not null, categories_id bigint not null, primary key (ITEM_id, categories_id))
SchemaUpdate - alter table ITEM_CATEGORY add constraint FK5C7030AA7C924DF7 foreign key (categories_id) references CATEGORY
SchemaUpdate - alter table ITEM_CATEGORY add constraint FK5C7030AA66304535 foreign key (ITEM_id) references ITEM
Итак, правильные таблицы созданы, двойной первичный ключ в порядке, внешние ключи верны.
Сохранение item
не делает правильных вещей:
[DEBUG] org.hibernate.SQL - insert into CATEGORY (id, name) values (null, ?)
[TRACE] org.hibernate.type.StringType - binding 'one' to parameter: 1
[DEBUG] org.hibernate.SQL - call identity()
one.id=1
[DEBUG] org.hibernate.SQL - insert into CATEGORY (id, name) values (null, ?)
[TRACE] org.hibernate.type.StringType - binding 'two' to parameter: 1
[DEBUG] org.hibernate.SQL - call identity()
two.id=2
[DEBUG] org.hibernate.SQL - insert into ITEM (id, name) values (null, ?)
[TRACE] org.hibernate.type.StringType - binding 'item' to parameter: 1
[DEBUG] org.hibernate.SQL - call identity()
item.id=1
item.categories=[ch.globus.bonus.ManyToManyTest$Category@1d532ae, ch.globus.bonus.ManyToManyTest$Category@42a6eb]
Как видите, сам элемент сохраняется, но не содержит набор категорий (вставки в таблицу объединения отсутствуют). Но в наборе есть значения!
Когда он читает элемент, он запрашивает таблицу соединения:
[DEBUG] org.hibernate.SQL - select manytomany0_.id as id9_0_, manytomany0_.name as name9_0_ from ITEM manytomany0_ where manytomany0_.id=?
[TRACE] org.hibernate.type.LongType - binding '1' to parameter: 1
[TRACE] org.hibernate.type.StringType - returning 'item' as column: name9_0_
[DEBUG] org.hibernate.SQL - select categories0_.ITEM_id as ITEM1_1_, categories0_.categories_id as categories2_1_, manytomany1_.id as id10_0_, manytomany1_.name as name10_0_ from ITEM_CATEGORY categories0_ left outer join CATEGORY manytomany1_ on categories0_.categories_id=manytomany1_.id where categories0_.ITEM_id=?
[TRACE] org.hibernate.type.LongType - binding '1' to parameter: 1
item.categories=[]
но, конечно, он ничего не может найти. В чем дело? Почему hibernate рассматривает таблицу соединений для find()
, а не для persist()
?
Я использую Hibernate 3.3.1, Аннотации Hibernate 3.4.0
[РЕДАКТИРОВАТЬ] Чтобы исправить проблему, я попытался ввести двунаправленное отображение. Сначала я добавил этот код в Category
:
/* BEGIN FIX1: made join bidrectional */
@ManyToMany(cascade={ CascadeType.ALL })
Set<Item> items = new HashSet<Item> ();
/* END FIX1: made join bidrectional */
Затем я изменил тест, чтобы обновить обе стороны:
item.categories.add (two);
/* BEGIN FIX1: made join bidrectional */
one.items.add (item);
two.items.add (item);
/* END FIX1: made join bidrectional */
em.persist (item);
/* BEGIN FIX1: made join bidrectional */
em.persist (one);
em.persist (two);
/* END FIX1: made join bidrectional */
Я даже пошел, чтобы продолжить категории снова. Результат: ничего. Hibernate счастливо игнорирует отображение «многие ко многим».
В качестве второго исправления я попытался добавить аннотацию @JoinTable
, предложенную zoidbeck:
@ManyToMany(cascade={ CascadeType.ALL })
/* BEGIN FIX2: Added JoinTable */
@JoinTable(name = "ITEM_CATEGORY",
joinColumns={@JoinColumn(name="itm_id")},
inverseJoinColumns={@JoinColumn(name="cat_id")}
)
/* END FIX2: Added JoinTable */
Set<Category> categories = new HashSet<Category> ();
Опять ничего.