Hibernate OneToOne between PK's with lazy behaviour

↘锁芯ラ 提交于 2020-04-18 03:54:50

问题


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:

Hibernate lazy loading for reverse one to one workaround - how does this work?

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:


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;


来源:https://stackoverflow.com/questions/61140887/hibernate-onetoone-between-pks-with-lazy-behaviour

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!