[英]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.