[英]Hibernate: @ManyToOne(fetch = FetchType.LAZY) does not work on non-primary key referenced column
[英]@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();
}
}
我的圖書館:
我已經嘗試過的其他事情:
將 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 字段中的唯一約束)的外鍵無效。
嘗試使用條件中的 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 取代
您不能將該名稱用作連接列,因為它沒有唯一約束。 因此,它可能導致 ManyToMany 映射而不是 ManyToOne。 我不知道 hibernate 是否接受這一點,但從長遠來看,它會以意想不到的方式結束。 此外,我沒有看到它的用例。 我建議您始終使用 Long id 作為主鍵並通過此字段自動映射。 僅當您有一個非正統的用例或必須與舊數據庫兼容時,才需要這種特殊處理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.