英文:
Hibernate OneToOne between PK's with lazy behaviour
问题
我正在尝试使用 Hibernate 5.3.13.Final 和 Wildfly 18 注解来实现一个名为 MyEntity
的实体,以及另一个名为 MyEntityInfo
的实体。
想法是让 MyEntity
存储一些常被请求的字段,而 MyEntityInfo
则存储一些不经常被请求的字段。两者共享名为 SID(Long)的相同主键,且从 Info 的 SID 到 Entity 的 SID 存在一个外键关系。可以存在没有 info 的实体。
通常情况下,您不会需要额外的信息。例如,我不希望在像这样查询实体时获取信息实体:
MyEntityImpl entity = em.find(MyEntityImpl.class, 1L);
然而,当我运行这段代码时,我发现有第二个查询,除了主要查询外,还会获取 Info 实体,就像是 EAGER
行为一样。
我正在使用 @OneToOne
进行关系映射。我已经尝试了多种 FetchType
、optional
和 @LazyToOne
的组合,但迄今为止都没有成功。
以下是 MyEntity
和 MyEntityInfo
类的代码(已省略其他的 getter 和 setter 方法):
MyEntity(ID 生成器是自定义序列生成器):
@Entity
@Table(name = MyEntityImpl.TABLE_NAME)
public class MyEntityImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY";
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "GEN_" +
TABLE_NAME)
@GenericGenerator(name = "GEN_" +
TABLE_NAME, strategy = CoreIdGenerator.ID_GENERATOR, parameters = {
@Parameter(name = "tableName", value = TABLE_NAME) })
@Column(name = "sid", nullable = false, unique = true)
private Long sid;
@OneToOne(mappedBy = "myEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
@LazyToOne(LazyToOneOption.NO_PROXY)
private MyEntityInfoImpl info;
@Column
private String field;
}
MyEntityInfo:
@Entity
@Table(name = MyEntityInfoImpl.TABLE_NAME)
public class MyEntityInfoImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY_INFO";
@Id
@Column(name = "SID", nullable = false, unique = true)
private Long sid;
@OneToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
@Column(name = "INFO_FIELD")
private String infoField;
}
我尝试了这个解决方案,但正如我所说,对我来说并没有起作用:
我已经设法使用 @OneToMany
做了一些类似的事情,并且手动管理数据,但这不是我想做的。然而,关于是否可以使用 @OneToOne
实现这一点,或者适合实现这一点的正确设计模式的其他替代方案和信息也是受欢迎的。
PS:如果您想尝试创建 SQL Server 数据库表,以下是表的创建语句:
create table TMP_MY_ENTITY (SID NUMERIC(19,0) NOT NULL, FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY ADD CONSTRAINT PK_TMP_MY_ENTITY PRIMARY KEY CLUSTERED (SID);
go
create table TMP_MY_ENTITY_INFO (SID NUMERIC(19,0) NOT NULL, INFO_FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY_INFO ADD CONSTRAINT PK_TMP_MY_ENTITY_INFO PRIMARY KEY CLUSTERED (SID);
go
CREATE SEQUENCE SEQ_TMP_MY_ENTITY START WITH 1 INCREMENT BY 1 MINVALUE 1 CACHE 20;
alter table TMP_MY_ENTITY_INFO add constraint FK_TMP_MY_ENT_INFO_MY_ENT FOREIGN KEY (SID) references TMP_MY_ENTITY(SID);
go
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 1');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 1');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 2');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 2');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 3 no info');
-- DELETE ALL
drop table TMP_MY_ENTITY_INFO;
drop table TMP_MY_ENTITY;
drop sequence SEQ_TMP_MY_ENTITY;
英文:
I'm trying to achieve to have an entity called MyEntity
along with another entity called MyEntityInfo
using Hibernate 5.3.13.Final with annotations under Wildfly 18.
The idea is to have MyEntity
store some commonly requested fields, and MyEntityInfo
store some rarely requested fields. Both share the same primary key called SID (Long), and there is a FK from Info's SID to Entity's SID. There can be entities without info.
Normally you will not require the additional info. For example, I don't want the info entity to be fetched when I query my entity like this:
MyEntityImpl entity = em.find(MyEntityImpl.class, 1L);
However, when I run this code, I find that there's a second query, fetching the Info entity along the main one, as in an EAGER
behaviour.
I'm mapping the relationship using @OneToOne
. I've tried several combinations of FetchType
, optional
and @LazyToOne
, but so far without success.
Here is the code for both MyEntity and MyEntityInfo classes (additional getters and setters removed):
MyEntity (ID generator is a custom sequence generator):
@Entity
@Table(name = MyEntityImpl.TABLE_NAME)
public class MyEntityImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY";
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "GEN_" +
TABLE_NAME)
@GenericGenerator(name = "GEN_" +
TABLE_NAME, strategy = CoreIdGenerator.ID_GENERATOR, parameters = {
@Parameter(name = "tableName", value = TABLE_NAME) })
@Column(name = "sid", nullable = false, unique = true)
private Long sid;
@OneToOne(mappedBy = "myEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
@LazyToOne(LazyToOneOption.NO_PROXY)
private MyEntityInfoImpl info;
@Column
private String field;
MyEntityInfo:
@Entity
@Table(name = MyEntityInfoImpl.TABLE_NAME)
public class MyEntityInfoImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY_INFO";
@Id
@Column(name = "SID", nullable = false, unique = true)
private Long sid;
@OneToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
@Column(name = "INFO_FIELD")
private String infoField;
I've tried this solution, but as I said, it didn't work for me:
I've managed to do something somewhat similar using @OneToMany
and managing data manually, but that's not what I'd like to do. However, another alternatives and information on whether this can be achieved or not using @OneToOne
, or the right design pattern to do this are also welcome.
PS: Database tables creation for SQL Server, in case you want to try it:
create table TMP_MY_ENTITY (SID NUMERIC(19,0) NOT NULL, FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY ADD CONSTRAINT PK_TMP_MY_ENTITY PRIMARY KEY CLUSTERED (SID);
go
create table TMP_MY_ENTITY_INFO (SID NUMERIC(19,0) NOT NULL, INFO_FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY_INFO ADD CONSTRAINT PK_TMP_MY_ENTITY_INFO PRIMARY KEY CLUSTERED (SID);
go
CREATE SEQUENCE SEQ_TMP_MY_ENTITY START WITH 1 INCREMENT BY 1 MINVALUE 1 CACHE 20;
alter table TMP_MY_ENTITY_INFO add constraint FK_TMP_MY_ENT_INFO_MY_ENT FOREIGN KEY (SID) references TMP_MY_ENTITY(SID);
go
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 1');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 1');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 2');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 2');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 3 no info');
-- DELETE ALL
drop table TMP_MY_ENTITY_INFO;
drop table TMP_MY_ENTITY;
drop sequence SEQ_TMP_MY_ENTITY;
答案1
得分: 0
以下是翻译好的内容:
在跟随 @SternK 的链接并升级至 Wildfly 19 和 Hibernate 5.4.14 之后,通过使用 @MapsId
最终解决了该问题。
正确的映射如下:
MyEntity:
public class MyEntityImpl {
@OneToOne(mappedBy = "myEntity", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "SID")
private MyEntityInfoImpl info;
MyEntityInfo:
public class MyEntityInfoImpl {
@OneToOne(fetch = FetchType.EAGER, optional = false)
@MapsId
@JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
英文:
After following @SternK link, and upgrading to Wildfly 19 and Hibernate 5.4.14, it finally worked by using @MapsId
.
The right mapping to use is this:
MyEntity:
public class MyEntityImpl {
@OneToOne(mappedBy = "myEntity", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "SID")
private MyEntityInfoImpl info;
MyEntityInfo:
public class MyEntityInfoImpl {
@OneToOne(fetch = FetchType.EAGER, optional = false)
@MapsId
@JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
专注分享java语言的经验与见解,让所有开发者获益!
评论