Hibernate递归与同一实体的多对多关联

Mag*_*sol 45 java entity-relationship hibernate java-ee hibernate-annotations

另一个Hibernate问题......:P

使用Hibernate的Annotations框架,我有一个User实体.每个人都User可以拥有一群朋友:其他人User的集合.但是,我还没有弄清楚如何在UserUsers 列表组成的类中创建多对多关联(使用用户朋友中间表).

这是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",
            joinColumns=@JoinColumn(name="personId"),
            inverseJoinColumns=@JoinColumn(name="friendId")
    )
    private List<User> friends;
}
Run Code Online (Sandbox Code Playgroud)

用户朋友的映射表只有两列,这两者都是外键到uid该列tbl_users表.这两列是personId(应该映射到当前用户)和friendId(指定当前用户的朋友的id).

问题是,即使我已经预先填充了朋友表,使得系统中的所有用户都是所有其他用户的朋友,"朋友"字段仍会显示为空.我甚至尝试过切换关系@OneToMany,但它仍然是null(尽管Hibernate调试输出显示了一个SELECT * FROM tbl_friends WHERE personId = ? AND friendId = ?查询,但没有别的).

有关如何填充此列表的任何想法?谢谢!

Chs*_*y76 65

@ManyToMany to self是相当混乱的,因为你通常模拟它的方式不同于"Hibernate"方式.你的问题是你错过了另一个系列.

可以这样想 - 如果你将"作者"/"书"映射为多对多,你需要书上的"作者"集合和作者的"书籍"集合.在这种情况下,您的"用户"实体代表关系的两端; 所以你需要"我的朋友"和"朋友"系列:

@ManyToMany
@JoinTable(name="tbl_friends",
 joinColumns=@JoinColumn(name="personId"),
 inverseJoinColumns=@JoinColumn(name="friendId")
)
private List<User> friends;

@ManyToMany
@JoinTable(name="tbl_friends",
 joinColumns=@JoinColumn(name="friendId"),
 inverseJoinColumns=@JoinColumn(name="personId")
)
private List<User> friendOf;
Run Code Online (Sandbox Code Playgroud)

您仍然可以使用相同的关联表,但请注意,join/inverseJon列在集合上交换.

"friends"和"friendOf"集合可能匹配也可能不匹配(取决于您的"友谊"是否始终是相互的),当然,您不必在API中以这种方式公开它们,但这是映射的方式它在Hibernate中.

  • 我认为值得补充的是,如果使用同一个表,其中一个应该有一个“mappedBy”而不是“@JoinTable”。_friendOf_ 例如:`@ManyToMany(mappedBy = "friends")`。否则,休眠将尝试保留两者并导致“java.sql.SQLIntegrityConstraintViolationException:键“PRIMARY”的重复条目“xy”” (2认同)

K.N*_*las 10

接受的答案似乎与@JoinTable注释过于复杂。稍微简单一点的实现只需要一个mappedBy. 使用mappedBy表示拥有Entity或财产,这可能应该是referencesTo因为它将被视为“朋友”。关系ManyToMany可以创建非常复杂的图表。使用mappedBy使代码如下:

@Entity
public class Recursion {
    @Id @GeneratedValue
    private Integer id;
    // what entities does this entity reference?
    @ManyToMany
    private Set<Recursion> referencesTo;
    // what entities is this entity referenced from?
    @ManyToMany(mappedBy="referencesTo")
    private Set<Recursion> referencesFrom;
    public Recursion init() {
        referencesTo = new HashSet<>();
        return this;
    }
    // getters, setters
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您需要考虑拥有的属性是referencesTo. 您只需将关系放入该属性中即可引用它们。当您阅读Entity背面时,假设您执行了fetch join,JPA 将为结果创建集合。当您删除实体时,JPA 将删除对其的所有引用。

tx.begin();
Recursion r0 = new Recursion().init();
Recursion r1 = new Recursion().init();
Recursion r2 = new Recursion().init();
r0.getReferencesTo().add(r1);
r1.getReferencesTo().add(r2);
em.persist(r0);
em.persist(r1);
em.persist(r2);

tx.commit();
// required so that existing entities with null referencesFrom will be removed from cache.
em.clear();
for ( int i=1; i <= 3; ++i ) {
    Recursion r = em.createQuery("select distinct r from Recursion r left join fetch r.referencesTo left join fetch r.referencesFrom where id = :id", Recursion.class).setParameter("id",  i).getSingleResult();
    System.out.println(r + " To=" + Arrays.toString(r.getReferencesTo().toArray()) + " From=" + Arrays.toString(r.getReferencesFrom().toArray()) );
}
tx.begin();
em.createQuery("delete from Recursion where id = 2").executeUpdate();
tx.commit();
// required so that existing entities with referencesTo will be removed from cache.
em.clear();
Recursion r = em.createQuery("select distinct r from Recursion r left join fetch r.referencesTo left join fetch r.referencesFrom where id = :id", Recursion.class).setParameter("id",  1).getSingleResult();
System.out.println(r + " To=" + Arrays.toString(r.getReferencesTo().toArray()) + " From=" + Arrays.toString(r.getReferencesFrom().toArray()) );
Run Code Online (Sandbox Code Playgroud)

它给出以下日志输出(始终检查生成的 SQL 语句):

Hibernate: create table Recursion (id integer not null, primary key (id))
Hibernate: create table Recursion_Recursion (referencesFrom_id integer not null, referencesTo_id integer not null, primary key (referencesFrom_id, referencesTo_id))
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: alter table Recursion_Recursion add constraint FKsi0wfuwfs0bl19jjpofw4n8pt foreign key (referencesTo_id) references Recursion
Hibernate: alter table Recursion_Recursion add constraint FKarrkuyh2v1j5qnlui2vbpl7tk foreign key (referencesFrom_id) references Recursion
Hibernate: call next value for hibernate_sequence
Hibernate: call next value for hibernate_sequence
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Recursion (id) values (?)
Hibernate: insert into Recursion (id) values (?)
Hibernate: insert into Recursion (id) values (?)
Hibernate: insert into Recursion_Recursion (referencesFrom_id, referencesTo_id) values (?, ?)
Hibernate: insert into Recursion_Recursion (referencesFrom_id, referencesTo_id) values (?, ?)
Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=?
model.Recursion@7bdf6bb7 To=[model.Recursion@1bc53649] From=[]
Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=?
model.Recursion@1bc53649 To=[model.Recursion@42deb43a] From=[model.Recursion@7bdf6bb7]
Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=?
model.Recursion@42deb43a To=[] From=[model.Recursion@1bc53649]
Hibernate: delete from Recursion_Recursion where (referencesTo_id) in (select id from Recursion where id=2)
Hibernate: delete from Recursion_Recursion where (referencesFrom_id) in (select id from Recursion where id=2)
Hibernate: delete from Recursion where id=2
Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=?
model.Recursion@6b739528 To=[] From=[]
Run Code Online (Sandbox Code Playgroud)