【问题标题】:jpa - How to create a many to many relationship with an IdClass?jpa - 如何与 IdClass 创建多对多关系?
【发布时间】:2025-12-26 22:30:11
【问题描述】:

我有 3 个具有多对多关系的表

  • 一个(id, name, ....)主键id
  • B (id, name, ....) 主键 id
  • AB (id_a, id_b, date, ....) - no 的关系表 主键

我可以为休眠类建模,以便我使用 IdClass 而不是 EmbeddedId 解决方案(这样我就可以避免使用级联不必要的级别进行调用 - getId().getA ())

PS 尝试混合使用这些解决方案,但不起作用:
http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/
How to map a composite key with Hibernate?

【问题讨论】:

    标签: java hibernate jpa many-to-many


    【解决方案1】:

    在建立实体之间的关系时,
    我旁边最近的墙和我的头经常加入...(Stephan)

    这是实体 A 和 B 之间多对多关系的工作示例:

    A.java

    import java.util.ArrayList;
    import java.util.List;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    
    @Entity
    public class A {
        @Id
        @GeneratedValue(strategy=GenerationType.SEQUENCE)
        @Column(name="id_a")
        private Integer id;
    
        private String name;
    
        @OneToMany(mappedBy="a",cascade=CascadeType.ALL, fetch=FetchType.LAZY)
        private List<AB> abAssociations = new ArrayList<>();
    
        // Getters and setters...
    }
    

    B.java

    import java.util.ArrayList;
    import java.util.List;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    
    @Entity
    public class B {
        @Id
        @GeneratedValue(strategy=GenerationType.SEQUENCE)
        @Column(name = "id_b")
        private Integer id;
    
        private String name;
    
        @OneToMany(mappedBy = "b", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
        private List<AB> abAssociations = new ArrayList<>();
    
        // Getters and setters...
    }
    

    AB.java

    import java.util.Date;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.Id;
    import javax.persistence.IdClass;
    import javax.persistence.ManyToOne;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    
    @Entity
    @IdClass(ABid.class)
    public class AB {
        @Id
        @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
        private A a;
    
        @Id
        @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
        private B b;
    
        @Temporal(TemporalType.TIMESTAMP)
        private Date date;
    
        // Getters and setters...
    }
    

    ABid.java

    import java.io.Serializable;
    
    // The IdClass MUST implement Serializable and override #hashCode and #equals
    public class ABid implements Serializable {
    
        private static final long serialVersionUID = -2834827403836993112L;
    
        private A a;
        private B b;
    
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((a == null) ? 0 : a.hashCode());
            result = prime * result + ((b == null) ? 0 : b.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ABid other = (ABid) obj;
            if (a == null) {
                if (other.a != null)
                    return false;
            } else if (!a.equals(other.a))
                return false;
            if (b == null) {
                if (other.b != null)
                    return false;
            } else if (!b.equals(other.b))
                return false;
            return true;
        }
    }
    

    pom.xml

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.7.Final</version>
        </dependency>
    

    示例代码

    import java.util.Date;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    
    public class Main {
        public static void main(String[] args) {
            // * Init entity manager
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("playground");
    
            EntityManager em = emf.createEntityManager();
            em.getTransaction().begin();
    
            // * Create two entities and persist them
            // We must persist the entities first alone before we build and flush their relation
            A a = new A();
            a.setName("foo");
            em.persist(a);
    
            B b = new B();
            b.setName("bar");
            em.persist(b);
    
            // * Build relationships between the two previous entities
            AB ab = new AB();
            ab.setA(a);
            ab.setB(b);
            ab.setDate(new Date());
            a.getAbAssociations().add(ab);
            b.getAbAssociations().add(ab);
    
            // * Flush our changements in the database
            em.getTransaction().commit();
            em.close();
            emf.close();
        }
    }
    

    这是 Hibernate 在 Postgresql 数据库上创建的表的 sql 代码。

    CREATE TABLE a
    (
      id_a integer NOT NULL,
      name character varying(255),
      CONSTRAINT a_pkey PRIMARY KEY (id_a)
    )
    
    CREATE TABLE b
    (
      id_b integer NOT NULL,
      name character varying(255),
      CONSTRAINT b_pkey PRIMARY KEY (id_b)
    )
    
    CREATE TABLE ab
    (
      date timestamp without time zone,
      b_id_b integer NOT NULL,
      a_id_a integer NOT NULL,
      CONSTRAINT ab_pkey PRIMARY KEY (a_id_a, b_id_b),
      CONSTRAINT fk_3exna7nsxvj1kv9i9pntmwlf1 FOREIGN KEY (a_id_a)
          REFERENCES a (id_a) MATCH SIMPLE
          ON UPDATE NO ACTION ON DELETE NO ACTION,
      CONSTRAINT fk_n3jrq53nr1elew4rytocopkbu FOREIGN KEY (b_id_b)
          REFERENCES b (id_b) MATCH SIMPLE
          ON UPDATE NO ACTION ON DELETE NO ACTION
    )
    

    【讨论】:

    • 太棒了,谢谢! (但似乎有点问题,如果你不在 AB 类中为两个字段 a 和 b 添加 @JoinColumn(name="a", nullable=false, updatable=false) 注释)
    • 你拯救了我的一天 :)
    • 如果我想让 AB 类有自己的增量 ID 该怎么办
    • @valik 在这里查看我的答案:*.com/a/64781726/363573
    【解决方案2】:

    回应@valik的评论。

    试试这个:

    @Entity
    public class AB {
        @Id
        @GeneratedValue(strategy=GenerationType.SEQUENCE)
        @Column(name = "id_ab")
        private Integer id;
    
        @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
        private A a;
    
        @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
        private B b;
    
        @Temporal(TemporalType.TIMESTAMP)
        private Date date;
    
        // Getters and setters...
    }
    

    【讨论】: