Hibernate соединение "многие-ко-многим" с одним и тем же объектом
Другой вопрос спящего режима...: P
Используя структуру аннотаций Hibernate, у меня есть объект User
. Каждый User
может иметь коллекцию друзей: Коллекция других User
s. Однако мне не удалось выяснить, как создать ассоциацию Many-to-Many в классе User
, состоящую из списка User
(используя промежуточную таблицу друзей пользователя).
Здесь класс пользователя и его аннотации:
@Entity
@Table(name="tbl_users")
public class User {
@Id
@GeneratedValue
@Column(name="uid")
private Integer uid;
...
@ManyToMany(
cascade={CascadeType.PERSIST, CascadeType.MERGE},
targetEntity=org.beans.User.class
)
@JoinTable(
name="tbl_friends",
[email protected](name="personId"),
[email protected](name="friendId")
)
private List<User> friends;
}
Таблица сопоставления пользователя-друга имеет только два столбца, оба из которых являются внешними ключами столбца uid
таблицы tbl_users
. Два столбца: personId
(которые должны отображаться для текущего пользователя) и friendId
(который указывает идентификатор текущего пользователя).
Проблема заключается в том, что поле "друзья" продолжает выдаваться нулевым, даже если я предварительно заполнил таблицу друзей, чтобы все пользователи в системе были друзьями со всеми другими пользователями. Я даже попытался переключить отношение на @OneToMany
, и он по-прежнему выходит нулевым (хотя вывод отладки Hibernate показывает запрос SELECT * FROM tbl_friends WHERE personId = ? AND friendId = ?
, но больше ничего).
Любые идеи о том, как заполнить этот список? Спасибо!
Ответы
Ответ 1
@ManyToMany для себя довольно запутанно, потому что способ, которым вы обычно моделируете это, отличается от способа "Hibernate". Ваша проблема в том, что вам не хватает другой коллекции.
Подумайте об этом так: если вы сопоставляете "автор" / "книгу" как много-ко-многим, вам нужна коллекция "авторов" в книге "Книга" и "книги" на Авторе. В этом случае ваш объект "Пользователь" представляет оба конца отношений; поэтому вам нужны "мои друзья" и "друг" коллекций:
@ManyToMany
@JoinTable(name="tbl_friends",
[email protected](name="personId"),
[email protected](name="friendId")
)
private List<User> friends;
@ManyToMany
@JoinTable(name="tbl_friends",
[email protected](name="friendId"),
[email protected](name="personId")
)
private List<User> friendOf;
Вы все равно можете использовать одну и ту же таблицу связей, но обратите внимание, что столбцы join/inverseJon меняются местами в коллекциях.
Коллекции "друзья" и "friendOf" могут соответствовать или не совпадать (в зависимости от того, всегда ли ваша "дружба" всегда взаимна), и вам не нужно раскрывать их таким образом в вашем API, конечно, но что способ сопоставить его в Hibernate.
Ответ 2
На самом деле это очень просто и может быть достигнуто следующим образом: у вас есть следующая сущность
public class Human {
int id;
short age;
String name;
List<Human> relatives;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public short getAge() {
return age;
}
public void setAge(short age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Human> getRelatives() {
return relatives;
}
public void setRelatives(List<Human> relatives) {
this.relatives = relatives;
}
public void addRelative(Human relative){
if(relatives == null)
relatives = new ArrayList<Human>();
relatives.add(relative);
}
}
HBM для этого:
<hibernate-mapping>
<class name="org.know.july31.hb.Human" table="Human">
<id name="id" type="java.lang.Integer">
<column name="H_ID" />
<generator class="increment" />
</id>
<property name="age" type="short">
<column name="age" />
</property>
<property name="name" type="string">
<column name="NAME" length="200"/>
</property>
<list name="relatives" table="relatives" cascade="all">
<key column="H_ID"/>
<index column="U_ID"/>
<many-to-many class="org.know.july31.hb.Human" column="relation"/>
</list>
</class>
</hibernate-mapping>
И тестовый пример
import org.junit.Test;
import org.know.common.HBUtil;
import org.know.july31.hb.Human;
public class SimpleTest {
@Test
public void test() {
Human h1 = new Human();
short s = 23;
h1.setAge(s);
h1.setName("Ratnesh Kumar singh");
Human h2 = new Human();
h2.setAge(s);
h2.setName("Praveen Kumar singh");
h1.addRelative(h2);
Human h3 = new Human();
h3.setAge(s);
h3.setName("Sumit Kumar singh");
h2.addRelative(h3);
Human dk = new Human();
dk.setAge(s);
dk.setName("D Kumar singh");
h3.addRelative(dk);
HBUtil.getSessionFactory().getCurrentSession().beginTransaction();
HBUtil.getSessionFactory().getCurrentSession().save(h1);
HBUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
HBUtil.getSessionFactory().getCurrentSession().beginTransaction();
h1 = (Human)HBUtil.getSessionFactory().getCurrentSession().load(Human.class, 1);
System.out.println(h1.getRelatives().get(0).getName());
HBUtil.shutdown();
}
}