2015-05-06 270 views
5

我有一些麻煩讓@ManyToOne關聯被加載到lazilly。我正在使用fetch = LAZY,但在主鍵列未建立連接時不起作用。@ManyToOne(fetch = FetchType.LAZY)在非主鍵引用列上不起作用

我知道這個問題已經是asked但我認爲它沒有得到妥善的回答,所以我提供了詳細的信息來澄清問題。

這是我的模型:

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

} 

注意getDummyA方法在實體DummyB是複製到嘗試兩種情況加入實體。

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

@ManyToOne(取= FetchType.LAZY) @JoinColumn(名稱= 「dummya_id」)

這工作得很好,只是執行一個查詢來檢索DummyB對象。

情況2:映射DummyB - > DummyA通過DummyA NON主鍵(字段名)

@ManyToOne(取= FetchType.LAZY) @JoinColumn(名稱= 「dummya_name」,referencedColumnName =「名「)

相同的dummyB選擇是執行,但之後,執行dummyA選擇過濾名稱=?獲取相關的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的核心:罐子:4.2.17.Final:編譯
  • 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:編譯
  • org.hibernate作爲:休眠-驗證:罐子:4.3.2.Final:提供

我已經嘗試過其他的東西:

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

    @LazyToOne(LazyToOneOption.PROXY) @ManyToOne(取= FetchType.LAZY,可選=真) @JoinColumn(名稱= 「dummya_name」,referencedColumnName = 「名稱」) @LazyToOne(LazyToOneOption。PROXY)

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

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

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

我不能在Hibernate中的文檔找到一個點,它是指這種行爲,所以我不知道如果有什麼是錯誤的,我的註解或我來到一個Hibernate錯誤。

任何人都可以告訴?

由MD-dev的要求更新時間: 設定更明確:

這是預期的行爲,或者是一個錯誤嗎?如果這是預期的行爲,那麼記錄在哪裏?

謝謝。

回答

0

您不能將該名稱用作連接列,因爲沒有唯一的約束。所以它可能會導致一個ManyToMany映射而不是ManyToOne。 我不知道hibernate是否接受這種做法,但最終會出現意想不到的情況。 此外,我沒有看到用例。我建議你始終使用Long ID作爲主鍵並通過此字段自動映射。如果你有非正統的用例或者必須與傳統的數據庫兼容,這種特殊的處理纔是必要的。

+0

上方露出的情況下的簡化我的問題,在我的實際案例中,加入的列是一個獨特的列。關於如果沒有任何意義,解釋起來很複雜,這不是我的問題的重點,我問:「這是預期的行爲嗎?如果是,它在哪裏記錄?」 – kothvandir

+0

比請更正您的問題,因爲上面的一個比錯誤。 –

+0

這正是我在最後一段中所要求的,但按照您的要求,我將其更清楚。 – kothvandir

3

看到與Hibernate 5.0.4完全相同的行爲。 @ManyToOne(帶有倒數OneToMany)和Lazy如果連接列是主鍵,則獲取完美。如果不是這樣,延遲加載就會中斷,Hibernate每次實例化對象時都會熱切地提取所有的ManyToOne。如果您爲例如1000條記錄執行Criteria.list(),那麼這可能會非常緩慢。最初作爲1000條記錄的單個查詢可以擴展成5000個查詢,以便使用單個選擇熱切地獲取各種@ManyToOne

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

我必須在我的應用程序中使用非PK的連接來實現的解決方案是僅垃圾@ManyToOne/@OneToMany註釋對並手動編寫集合提取(使用瞬態變量緩存結果)。由於我的一些對象有5或6個對象,並且所有這些對象都被Hibernate單獨挑選出來,所以性能要高得多。

不幸的是,我無法重組我的模式以適應Hibernate中的這個怪癖。我正在做一個涉及Heroku Connect的項目,以及表格之間的連接,這些合併來自Salesforce.com的數據是使用表中不是主鍵的「sfid」列完成的。主鍵是Heroku Postgres數據庫中記錄唯一的唯一值,不能用於連接,因爲數據庫中沒有其他表引用此主鍵。

我假設這是Hibernate中的一個錯誤;沒有任何我已閱讀或能夠修改已經以任何方式影響了此行爲,正如我所提到的,如果連接列是主鍵,我可以使系統完全按預期工作。

1

如果有人仍然有這個問題 我們得到它的工作方式如下:

@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 lazy loading for reverse one to one workaround - how does this work?

0

@peach解決方案很好地解釋爲我工作。只需幾件事情沒有提到:

@Entity 
public class DummyB implements Serializable { 

應該

@Entity 
public class DummyB implements Serializable, FieldHandled { 

,如果你正在使用@JsonIgnoreProperties你應該添加fieldHandler

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