Почему в спящем режиме, выполняют два запроса для нетерпеливой загрузки @OneToOne двунаправленная ассоциация?

у меня есть объект, который имеет - объект B, и B имеет - с @OneToOne двунаправленной ассоциацией.

Теперь, когда я findall записи, будьте в спящем режиме, выполняют два запроса с левым внешним объединением на B, чем-то вроде этого:

select a.id, a.id_b, a.field1, b.id, b.field1 from A as a, B as b left outer join b ON b.id=a.id_b;
select a.id, a.id_b, a.field1, b.id, b.field1 from A as a, B as b left outer join b ON b.id=a.id_b WHERE b.id=?

Первые поля A и B загрузки запросов и это в порядке, но почему выполняют второй запрос для перезагрузки A? Я думаю эта загрузка запросов содержание в B, но этот A, очевидно, который уже содержит B... так его загруженный первым запросом, не верно?

- РЕДАКТИРОВАНИЕ-

Объект A:

@Entity
public class A implements Serializable{
    // id and other ecc ecc
    @OneToOne
    @JoinColumn(name="id_b")
    B b;
}

Объект B:

@Entity
public class B implements Serializable{
    // id and other ecc ecc
    @OneToOne(mappedBy="b")
    A a;
}

Это - ситуация и findAll на потребности два запроса... почему?

9
задан blow 1 August 2010 в 13:23
поделиться

2 ответа

Удар, если A и B используют один и тот же столбец первичного ключа , где оба объекта объединены с помощью их первичного ключа , вы должны использовать @PrimaryKeyJoinColumn вместо

@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 вам не нужен атрибут mappedBy из-за @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;
    }

}

Давайте протестируем (вы можете протестировать, если хотите)

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

Обратите внимание, что я использую поле MutableInt ( инкапсулированное свойство Integer) вместо Integer, потому что Integer является неизменяемым типом как способ И A, и B совместно используют ОДИНАКОВЫЙ присвоенный идентификатор

Но если A и B соединены с использованием отличного от их первичного ключа ], вы должны использовать @JoinColumn и mappedBy (двунаправленное отношение, справа) следующим образом

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

}   

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

}

Чтобы проверить

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

Используя сторону владельца B, вы получите Два оператора выбора Это происходит из-за того, что таблица B не содержит столбца внешнего ключа, который указывает на таблицу A , но при использовании

"из A выборка левого соединения ab, где a.id =: id "

Вы получите только один оператор выбора , потому что A знает, как получить свой присоединенный B, используя свой столбец внешнего ключа B_ID

6
ответ дан 4 December 2019 в 23:38
поделиться

Как именно выглядит ваше отображение?

Правильно ли ваши классы A и B реализуют hashCode() и equals(), чтобы Hibernate мог сказать, что экземпляр A, на который указывает B, является тем же самым экземпляром первого A?

Похоже, что вы пытаетесь смоделировать двунаправленное отображение один-к-одному - посмотрите раздел в руководстве об этом, чтобы увидеть рекомендуемые методы для достижения этого.

0
ответ дан 4 December 2019 в 23:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: