获取 EAGER 获取对象 OneToOne 时出现 LazyInitializationException

LazyInitializationException when get EAGER fetch object OneToOne

我有两个实体

@Entity
@Table(name = "user")
@Data
@Builder
@EqualsAndHashCode(callSuper=false)
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "name")
    private String name;    
    
    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @PrimaryKeyJoinColumn
    private UserLastLogin userLastLogin;
}


@Entity
@Table(name = "lastLogin")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserLastLogin implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "name")
    private String userName;

    @Column(name = "date")
    private LocalDateTime date;

    @OneToOne
    @MapsId
    @JoinColumn(name = "name")
    private User user;

}

我使用 spring 引导,使用 spring 数据和 jpa,在最新版本中休眠。 在文档中,@OneToOne 是默认的 EAGER,但是当我获取急切的获取对象时,当我在 get 方法中不使用 @Transactional 时,我会得到 lazyInitializationException。我不明白为什么...

    public UserDto getUser(String userName) {
        var user= userRepository.getById(userName);     
        d.getSystemUserLastLogin(); // this throw lazy initialization exception
        return mapper.entityToDto(d);
    }

当我将此方法标记为@Transactioal 时,这项工作。但是,不推荐在 get 方法中使用 transactions。我需要在此关系中使用 EAGER fetch。

当我查看查询休眠时,我有一个 select,但子对象不可用。

Hibernate: 
    select
        user0_.name as nazwa1_4_0_,        
        user2_.name as name1_23_2_,        
        user2_.data as data3_23_2_ 
    from
        user0_     
    left outer join
       last_login user2_ 
            on user0_.name=user2_.name 
    where
        user0_.name=?

我建议您使用辅助表,而不是像这样:

@Entity
@Table(name = "user")
@Data
@Builder
@EqualsAndHashCode(callSuper=false)
@ToString
@AllArgsConstructor
@NoArgsConstructor
@SecondaryTable(name = "lastLogin", pkJoinColumns = @PrimaryKeyJoinColumn(name = "name"))
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "name")
    private String name;    
    
    @Column(table = "lastLogin", name = "date")
    private LocalDateTime date;
}

有关详细信息,另请参阅 https://www.baeldung.com/jpa-mapping-single-entity-to-multiple-tables

问题是尽管使用了 eager 获取,但使用了 lazy。这是由于使用了存储库中的 getById 方法,该方法仅检索对象的引用并在检索延迟时捕捉所有字段。更改为 findById 解决了问题,因为 findById 需要一个对象,而不是一个引用。