I have a simple many-to-many mapping:
@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;
}
And this test case:
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 ());
}
The test fails. In the log, I can see:
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
So the correct tables are created, the dual primary key is OK, the foreign keys are correct.
The persisting the item
doesn't do the right thing:
[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]
As you can see, the item itself is persisted but not the set with the categories (the inserts into the join table are missing). But there are values in the set!
When it reads the item, it does query the join table:
[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=[]
but of course, it can't find anything. What's wrong? Why is hibernate considering the join table for find()
but not for persist()
?
I'm using Hibernate 3.3.1, Hibernate Annotations 3.4.0
[EDIT] To fix the issue, I tried to introduce a bidirectional mapping. First, I added this code to Category
:
/* BEGIN FIX1: made join bidrectional */
@ManyToMany(cascade={ CascadeType.ALL })
Set<Item> items = new HashSet<Item> ();
/* END FIX1: made join bidrectional */
Then, I changed the test to update both sides:
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 */
I even went to persiste the categories again. Result: Nothing. Hibernate happily ignores the many-to-many mapping.
As a second fix, I tried to add the @JoinTable
annotation as suggested by 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> ();
Again, nothing.