Why hibernate perform two queries for eager load a @OneToOne bidirectional association?

后端 未结 2 1056
太阳男子
太阳男子 2020-12-28 22:23

i have entity A that has-a B entity, and B has-a A with @OneToOne bidirectional association.

Now, when i findall A records, hibernate perform two queries with a left

相关标签:
2条回答
  • 2020-12-28 22:32

    What does your mapping look like exactly?

    Do your A and B classes correctly implement hashCode() and equals() so that Hibernate can tell that the A instance pointed to by B is the same instance of the first A?

    Sounds like you are trying to model a bi-directional one-to-one mapping - take a look at the section in the manual on this to see the recommended methods for accomplishing it.

    0 讨论(0)
  • 2020-12-28 22:46

    Blow, if A and B share The same primary key column where both entities are joined by using their primary key, you should use @PrimaryKeyJoinColumn instead

    @Entity
    public class A implements Serializable {
    
        private MutableInt id = new MutableInt();
    
        private B b;
    
        public void setIdAsMutableInt(MutableInt id) {
            this.id = id;
        }
    
        @Id
        @GeneratedValue
        public Integer getId() {
            return id.intValue();
        }
    
        public void setId(Integer id) {
            this.id.setValue(id);
        }
    
        /**
          * Any ToOne annotation, such as @OneToOne and @ManyToOne, is EARGELY loaded, by default
          */
        @OneToOne(fetch=FetchType.LAZY)
        @PrimaryKeyJoinColumn
        @Cascade(CascadeType.SAVE_UPDATE)
        public B getB() {
            return b;
        }
    
        public void setB(B b) {
            b.setIdAsMutableInt(id);
    
            this.b = b;
        }
    
    }
    

    And B Notice you do not need mappedBy attribute because of @PrimaryKeyJoinColumn

    @Entity
    public class B implements Serializable {
    
        private MutableInt id = new MutableInt();
    
        private A a;
    
        public void setIdAsMutableInt(MutableInt id) {
            this.id = id;
        }
    
        @Id
        public Integer getId() {
            return id.intValue();
        }
    
        public void setId(Integer id) {
            this.id.setValue(id);
        }
    
        @OneToOne(fetch=FetchType.LAZY)
        @PrimaryKeyJoinColumn
        public A getA() {
            return a;
        }
    
        public void setA(A a) {
            this.a = a;
        }
    
    }
    

    Let's Test (You can Test if you want)

    A a = new A();
    B b = new B();
    
    a.setB(b);
    
    /**
      * b property will be saved because Cascade.SAVE_UPDATE
      */
    Serializable id = session.save(a);
    
    b = (B) session
            .createQuery("from B b left join fetch b.a where b.id = :id")
            .setParameter("id", id)
            .list()
            .get(0);
    
    Assert.assertEquals(b.getId(), b.getA().getId());
    

    Notice I use a MutableInt field (encapsulated by a Integer property) instead of Integer because Integer is a immutable Type as a way Both A and B share The SAME assigned id

    But if A and B are joined by using other Than their primary key, you should use @JoinColumn and mappedBy (bi-directional relationship, right) as follows

    @Entity
    public class A implements Serializable {
    
        private Integer id;
    
        private B b;
    
        @Id
        @GeneratedValue
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        /**
          * mappedBy="a" means: Look at "a" field / property at B Entity. If it has any assigned value, join us Through B_ID foreign key column
          */
        @OneToOne(fetch=FetchType.LAZY, mappedBy="a")
        /**
          * Table A has a foreign key column called "B_ID"
          */ 
        @JoinColumn(name="B_ID")
        @Cascade(CascadeType.SAVE_UPDATE)
        public B getB() {
            return b;
        }
    
        public void setB(B b) {
            this.b = b;
        }
    
    }   
    

    And B

    @Entity
    public class B implements Serializable {
    
        private Integer id;
    
        private A a;
    
        public void setIdAsMutableInt(MutableInt id) {
            this.id = id;
        }
    
        @Id
        @GeneratedValue
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        @OneToOne(fetch=FetchType.LAZY)
        public A getA() {
            return a;
        }
    
        public void setA(A a) {
            this.a = a;
        }
    
    }
    

    To test

    A a = new A();
    B b = new B();
    
    /**
      * Set up both sides
      * Or use some kind of add convenience method
      */ 
    a.setB(b);
    b.setA(a);
    
    /**
      * b property will be saved because Cascade.SAVE_UPDATE
      */
    Serializable id = session.save(a);
    
    b = (B) session
            .createQuery("from B b left join fetch b.a where b.id = :id")
            .setParameter("id", id)
            .list()
            .get(0);
    

    By using The owner side B, you will get Two select statements It occurs because B Table does not contain any foreign key column which points To Table A But by using

    "from A a left join fetch a.b where a.id = :id"

    You will get just one select statement because A knows how To retrieve its joined B by using its B_ID foreign key column

    0 讨论(0)
提交回复
热议问题