簡體   English   中英

@ManyToOne(fetch = FetchType.LAZY) 對非主鍵引用列不起作用

[英]@ManyToOne(fetch = FetchType.LAZY) doesn't work on non-primary key referenced column

我在延遲加載 @ManyToOne 關聯時遇到了一些麻煩。 我正在使用 fetch=LAZY 但是當連接不是由主鍵列進行時它不起作用。

我知道這個問題已經有人問過了,但我認為沒有正確回答,所以我提供了詳細信息來澄清這個問題。

這是我的模型:

DummyB -> DummyA

這些是表:

create table dummyA  (
  id number(18,0), --pk
  name varchar2(20) -- unique field
);

create table dummyB  (
  id number(18,0),
  dummya_id number(18,0),
  dummya_name varchar2(20)
);

這些是實體:

@Entity
public class DummyA implements Serializable {

    private Long id;
    private String name;

    @Id
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

@Entity
public class DummyB implements Serializable {

    private Long id;
    private DummyA dummyA;

    @Id
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    /* Case 1: mapping DummyB -> DummyA by DummyA NON primary key (field name) */
    // @ManyToOne(fetch = FetchType.LAZY)
    // @JoinColumn(name = "dummya_id")
    // public DummyA getDummyA() {
    // return dummyA;
    // }

    /* Case 2: mapping DummyB -> DummyA by DummyA primary key */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dummya_name", referencedColumnName = "name")
    @LazyToOne(LazyToOneOption.PROXY)
    public DummyA getDummyA() {
        return dummyA;
    }

    public void setDummyA(DummyA dummyA) {
        this.dummyA = dummyA;
    }

}

注意實體 DummyB 中的 getDummyA 方法是重復的,以嘗試兩種情況來連接實體。

案例 1:通過 DummyA 主鍵映射 DummyB -> DummyA

@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dummya_id")

這工作正常,只執行一個查詢來檢索 DummyB 對象。

案例 2:通過 DummyA NON 主鍵(字段名稱)映射 DummyB -> DummyA

@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dummya_name", referencedColumnName="name")

同樣的 dummyB 選擇被執行,但緊接着,一個 dummyA 選擇被執行過濾 name=? 獲取相關的 A 對象。

我正在使用一個非常簡單的 jUnit 來執行過濾:

public class DummyTest {

    @Autowired
    HibernateTransactionManager transactionManager;

    @Test
    @Transactional
    public void testFindDummyB() throws DAOException {
        Long idDummyB = 2L;

        Session session = getCurrentHibernateSession();

        List lst = session.createCriteria(DummyB.class)
                .add(Restrictions.eq("id", idDummyB)).list();

        assertTrue(lst.size() > 0);
    }

    private Session getCurrentHibernateSession() {
        return this.transactionManager.getSessionFactory().getCurrentSession();
    }

}

我的圖書館:

  • org.hibernate:hibernate-core:jar:4.2.17.Final:compile
  • org.hibernate.common:hibernate-commons-annotations:jar:4.0.2.Final:compile
  • org.hibernate.javax.persistence:hibernate-jpa-2.0-api:jar:1.0.1.Final:compile
  • org.hibernate:hibernate-validator:jar:4.3.2.Final:provided

我已經嘗試過的其他事情:

  • 將 hiberante 的 @LazyToOne 添加到 getDummyA() 方法沒有任何效果。

    @LazyToOne(LazyToOneOption.PROXY) @ManyToOne(fetch = FetchType.LAZY, optional = true) @JoinColumn(name = "dummya_name", referencedColumnName = "name") @LazyToOne(LazyToOneOption.PROXY)

  • 創建從 DummyB 表到 dummyA(以及 dummya.name 字段中的唯一約束)的外鍵無效。

  • 在 DummyA getName() 方法上添加 @Column(unique = true) 沒有成功。
  • 按照此處的建議設置 optional=true 或 false 也無效。
  • 嘗試使用條件中的 setFetchMode 強制延遲加載不起作用,DummyA select 繼續執行。

    List lst = session.createCriteria(DummyB.class) .add(Restrictions.eq("id", idDummyB))。 setFetchMode("dummyA", FetchMode.SELECT) .list();

我在 Hibernate 的文檔中找不到它指代這種行為的地方,所以我想知道我的注釋是否有問題,或者我遇到了 Hibernate 錯誤。

誰能告訴?

由 md-dev 請求更新:將其設置得更清楚:

這是預期的行為還是錯誤? 如果這是預期的行為,它在哪里記錄?

謝謝你。

看到與 Hibernate 5.0.4 完全相同的行為。 如果連接列是主鍵, @ManyToOne (具有互惠的OneToMany )和Lazy獲取工作完美。 如果不是,延遲加載就會中斷,每次實例化對象時,Hibernate 都會急切地獲取所有ManyToOne 如果您為 1000 條記錄執行Criteria.list() ,這可能會非常緩慢。 最初是對 1000 條記錄的單個查詢可以膨脹為 5000 條查詢,以便使用單獨的選擇急切地獲取各種@ManyToOne

絕對沒有任何我能夠測試/更改的內容以任何方式解釋了這一點,我可以可靠地重現它。

我必須在我的應用程序中實現的使用非 PK 進行連接的解決方案是@ManyToOne / @OneToMany注釋對並手動寫入集合提取(使用瞬態變量緩存結果)。 這是更多的工作,但性能要高得多,因為我的一些對象有 5 或 6 個@ManyToOne對象,並且所有這些都被 Hibernate 用單獨的選擇急切地獲取。

不幸的是,我無法重新組織架構以適應 Hibernate 中的這種怪癖。 我正在做一個涉及 Heroku Connect 的項目,當合並來自 Salesforce.com 的數據時,表之間的連接是使用表中非主鍵的“sfid”列完成的。 主鍵是 Heroku Postgres 數據庫中記錄唯一的單獨值,不能用於連接,因為數據庫中沒有其他表引用此主鍵。

我假設這是 Hibernate 中的一個錯誤; 我讀過或無法修改的任何內容都不會以任何方式影響這種行為,正如我所提到的,如果連接列是主鍵,我可以使系統完全按預期工作。

萬一有人仍然有這個問題,我們讓它按以下方式工作:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "dummya_name", referencedColumnName = "name", insertable = false, updatable = false),
@LazyToOne(LazyToOneOption.NO_PROXY)
public DummyA getDummyA() {
    if (fieldHandler != null) {
        return (DummyA) fieldHandler.readObject(this, "dummyA", dummyA);
    }
    return dummyA;
}

public void setDummyA(DummyA dummyA) {
    if (fieldHandler != null) {
        this.dummyA = (DummyA ) fieldHandler.writeObject(this, "dummyA", this.dummyA, dummyA);
        return;
    }
    this.dummyA= dummyA;
}

@Override
public void setFieldHandler(FieldHandler fieldHandler) {
    this.fieldHandler = fieldHandler;
}

@Override
public FieldHandler getFieldHandler() {
    return fieldHandler;
}

Hibernate 延遲加載中對反向一對一解決方法進行了很好的解釋 - 這是如何工作的?

正如@alina-petukhova 提到的,DummyB 類必須實現 FieldHandled 接口。

在 Spring Boot 2 中,FieldHandled 接口被 PersistentAttributeInterceptable 取代,FieldHandler 被 PersistentAttributeInterceptor 取代

https://docs.jboss.org/hibernate/orm/5.3/javadocs/org/hibernate/engine/spi/PersistentAttributeInterceptable.html

@peach解決方案對我有用 只是有幾件事沒有提到:

@Entity
public class DummyB implements Serializable {

應該

@Entity
public class DummyB implements Serializable, FieldHandled {

如果您使用的是 @JsonIgnoreProperties,則應添加 fieldHandler

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fieldHandler"})

您不能將該名稱用作連接列,因為它沒有唯一約束。 因此,它可能導致 ManyToMany 映射而不是 ManyToOne。 我不知道 hibernate 是否接受這一點,但從長遠來看,它會以意想不到的方式結束。 此外,我沒有看到它的用例。 我建議您始終使用 Long id 作為主鍵並通過此字段自動映射。 僅當您有一個非正統的用例或必須與舊數據庫兼容時,才需要這種特殊處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM