Java 休眠与同一实体的递归多对多关联

Java 休眠与同一实体的递归多对多关联,java,hibernate,jakarta-ee,entity-relationship,hibernate-annotations,Java,Hibernate,Jakarta Ee,Entity Relationship,Hibernate Annotations,另一个冬眠问题…:P 使用Hibernate的注释框架,我有一个用户实体。每个用户都可以拥有一组朋友:一组其他用户s。但是,我还没有弄清楚如何在User类中创建多对多关联,该类由Users列表组成(使用User-friends中间表) 以下是用户类及其注释: @Entity @Table(name="tbl_users") public class User { @Id @GeneratedValue @Column(name="uid") private In

另一个冬眠问题…:P

使用Hibernate的注释框架,我有一个
用户
实体。每个
用户
都可以拥有一组朋友:一组其他
用户
s。但是,我还没有弄清楚如何在
User
类中创建多对多关联,该类由
User
s列表组成(使用User-friends中间表)

以下是用户类及其注释:

@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;
}
@实体
@表(name=“tbl\U用户”)
公共类用户{
@身份证
@生成值
@列(name=“uid”)
私有整数uid;
...
@许多(
cascade={CascadeType.PERSIST,CascadeType.MERGE},
targetEntity=org.beans.User.class
)
@可接合(
name=“tbl_朋友”,
joinColumns=@JoinColumn(name=“personId”),
inverseJoinColumns=@JoinColumn(name=“friendId”)
)
私人名单朋友;
}
用户好友映射表只有两列,这两列都是
tbl_users
表的
uid
列的外键。这两列是
personId
(应该映射到当前用户)和
friendId
(指定当前用户朋友的id)

问题是,“friends”字段一直为空,即使我已经预先填充了friends表,使得系统中的所有用户都是其他所有用户的朋友。我甚至尝试过将关系切换到
@OneToMany
,结果仍然为空(尽管Hibernate调试输出显示了一个
SELECT*,来自tbl_friends,其中personId=?和friendId=?
查询,但没有其他内容)


关于如何填充此列表,有什么想法吗?谢谢大家!

@ManyToMany to self相当令人困惑,因为您通常建模的方式不同于“休眠”方式。你的问题是你错过了另一个收藏

这样想吧——如果你将“作者”/“书”映射为多对多,你需要书上的“作者”集合和作者上的“书籍”集合。在这种情况下,“用户”实体表示关系的两端;因此,您需要“我的朋友”和“我的朋友”系列:

@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;
@manytomy
@JoinTable(name=“tbl_friends”,
joinColumns=@JoinColumn(name=“personId”),
inverseJoinColumns=@JoinColumn(name=“friendId”)
)
私人名单朋友;
@许多
@JoinTable(name=“tbl_friends”,
joinColumns=@JoinColumn(name=“friendId”),
inverseJoinColumns=@JoinColumn(name=“personId”)
)
私人名单;
您仍然可以使用相同的关联表,但请注意,join/inverseJon列在集合上交换


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

实际上,这非常简单,可以通过以下方式实现,比如说您有以下实体

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);
    }




}

对于
@JoinTable
注释,公认的答案似乎过于复杂。稍微简单一点的实现只需要一个
mappedBy
。使用
mappedBy
表示拥有的
实体或财产,这可能是
的引用,因为这将被视为“朋友”。一个
manytomy
关系可以创建一个非常复杂的图形。使用
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
}
这将提供以下日志输出(始终检查生成的SQL语句):


我希望你能第三次来救我:)它工作得很好,你的解释大大澄清了我对E-R的理解。再次非常感谢您!:)我可以把friends和friendOf结合起来吗?如果没有,那么我就需要在一个实体中使用jsonmanagedreference和jsonbackreference来解决这个问题:我使用Hibernate 5,一个集合就足够了-使用两个集合会在tbl_friends中创建重复的行。我怎么能在“tbl_friends”中同时使用“friendId”和“personId”有唯一的约束呢?我认为值得补充一下,如果使用相同的表,其中一个表应该有一个“mappedBy”,而不是
@JoinTable
。friendOf例如:
@ManyToMany(mappedBy=“friends”)
。否则,hibernate将尝试同时保留这两个项,并导致
java.sql.SQLIntegrityConstraintViolationException:key“PRIMARY”的重复条目“x-y”
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();
    }

}
@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
}
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()) );
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=[]