【问题标题】:Spring Data Jpa Lazy Loading not working as expectedSpring Data Jpa 延迟加载未按预期工作
【发布时间】:2020-04-18 19:25:42
【问题描述】:

嘿,我正在研究 Spring Data jpa,我只想懒惰地获取数据。

我在春季项目中使用 lombook

我想通过电子邮件获取用户及其角色。

我的课很少

@Entity
@Table(name = "user")
@Data
public class User extends DateAudit {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @Column(columnDefinition = "char(50)")
    private String email;

    @Column(columnDefinition = "char(15)")
    private String phone;

    private String password;

    @Column(columnDefinition = "bit")
    private boolean isActive;

    @Column(columnDefinition = "ENUM('MALE', 'FEMALE', 'OTHER')")
    @Enumerated(EnumType.STRING)
    private Gender gender;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
    private List<UserRole> userRoles;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
    private UserProfile userProfile;

}
@Entity
@Table(name = "user_profile")
@Data
public class UserProfile extends DateAudit {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String picture;
    private float avgRating;

    @Column(columnDefinition = "char(13)")
    private String cnic;

    @Temporal(TemporalType.DATE)
    private Date dateOfBirth;

    //relations

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "city_id")
    private City city;
}
@Entity
@Data
@Table
public class UserRole {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "role_id")
    private Role role;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospital_id")
    private Hospital hospital;
}

这是我的用户存储库

@Repository
public interface UserRepository extends CrudRepository<User, Integer> {

    @EntityGraph(attributePaths = {"userRoles"})
    User findByEmail(String email);
}

以下是我用来获取用户的操作

    @GetMapping("/test")
    public String test(){

        userRepository.findByEmail("admin@domain.com");
        return "test";
    }

我在 application.properties 中添加了以下属性

spring.jpa.properties.hibernate.show_sql=true

所以当我从邮递员那里击中我的 /test 动作时 我看到在我的应用程序控制台中执行了两个查询。

Hibernate: select user0_.id as id1_21_0_, userroles1_.id as id1_23_1_, user0_.created_at as created_2_21_0_, user0_.updated_at as updated_3_21_0_, user0_.email as email4_21_0_, user0_.gender as gender5_21_0_, user0_.is_active as is_activ6_21_0_, user0_.name as name7_21_0_, user0_.password as password8_21_0_, user0_.phone as phone9_21_0_, userroles1_.hospital_id as hospital2_23_1_, userroles1_.role_id as role_id3_23_1_, userroles1_.user_id as user_id4_23_1_, userroles1_.user_id as user_id4_23_0__, userroles1_.id as id1_23_0__ from user user0_ left outer join user_role userroles1_ on user0_.id=userroles1_.user_id where user0_.email=?
Hibernate: select userprofil0_.id as id1_22_0_, userprofil0_.created_at as created_2_22_0_, userprofil0_.updated_at as updated_3_22_0_, userprofil0_.avg_rating as avg_rati4_22_0_, userprofil0_.city_id as city_id8_22_0_, userprofil0_.cnic as cnic5_22_0_, userprofil0_.date_of_birth as date_of_6_22_0_, userprofil0_.picture as picture7_22_0_, userprofil0_.user_id as user_id9_22_0_ from user_profile userprofil0_ where userprofil0_.user_id=?

我对用户个人资料不感兴趣。我在这里做错了什么。

【问题讨论】:

标签: hibernate spring-boot spring-data-jpa spring-data lombok


【解决方案1】:

这是因为是双向关联,Hibernate 需要知道它是否应该使用 null 或代理类初始化 userProfile 属性,它只能通过查询 user_profile 表来找到它。

您可以避免它删除外键列并为两个关联实体使用相同的主键值,因为您必须在拥有方使用 MapsId 注释,如下所示:

@Entity
@Table(name = "user_profile")
@Data
public class UserProfile extends DateAudit {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;

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

您可以在这里查看此问题的完整指南https://thoughts-on-java.org/hibernate-tip-lazy-loading-one-to-one/


如果您不想使用相同的密钥,另一种选择是将其映射为 OneToMany 关系。

【讨论】:

  • 嘿,你的解决方案看起来很清楚,只是有一点不清楚,user_id 不会在 user_profile 中添加为 fk,我使用你的解决方案吗?
  • 没错,就是这样
【解决方案2】:

一对一的关系被急切地加载。即使我们指示 hibernate 延迟加载它们,hibernate 也会忽略该提示。

为了克服这个问题,我们需要启用字节码增强。

字节码增强

在我们的例子中,我们使用字节码增强插件来增强实体类的字节码,并允许我们利用无代理延迟获取策略。我们可以通过以下方式在 pom.xml 文件中定义插件,

<build>
<plugins>
    <plugin>
        <groupId>org.hibernate.orm.tooling</groupId
        <artifactId>hibernate-enhance-maven-plugin</artifactId>
        <version>5.4.2.Final</version>
        <executions>
        <execution>
        <configuration><enableLazyInitialization>true</enableLazyInitialization></configuration>
        <goals>
            <goal>enhance</goal>
        </goals>
        </execution>
        </executions>
    </plugin>
</plugins>
</build>

启用无代理惰性关联

现在我们需要在实体类中添加@LazyToOne注解,让hibernate知道我们不想为关联实体启用代理延迟获取。

更新:

默认情况下,实体类的所有惰性属性都属于一个名为 DEFAULT 的组。并且获取 DEFAULT 组的任何属性也会获取其他属性。为了解决这个问题,我们需要使用@LazyGroup 注解定义我们希望单独获取的组。

在我们的例子中,我们希望单独获取嵌套对象。因此,我们使用 @LazyGroup 注释来注释 userRoles 和 userProfiles,如下所示。

@OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("userProfile")
private UserProfile userProfile;

【讨论】:

  • 我按你说的试过了,但他的控制台输出还是一样的,它执行了 2 个查询
  • 能否添加查询的控制台日志,现在可能第二次查询不同了
  • 你为什么把 @LazyToOne 放在 userRoles 的 oneToMany 上
  • yar koi faraq nahi parh raha 同样的结果 aa raha hay main khud 1 周说 laga hun 是 maslay ko resolve karnay main
猜你喜欢
  • 2013-10-03
  • 2017-06-23
  • 1970-01-01
  • 2013-11-02
  • 2022-09-29
  • 2021-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多