首页 > 解决方案 > 为什么 Hibernate with Specification 在第一个得到所有被询问的内容时会进行两个查询?

问题描述

我正在做一个练习 Spring Boot 应用程序。

我有 3 个实体(按推荐编辑):

class User extends EntityModelTemplate {
   String username; //unique

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
   List<UserRoles> userRoles; 
}

@IdClass(UserRolesKey.class)
class UserRoles {

   @Id
   @ManyToOne
   @JoinColumn(name = "user_id")
   User user;

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


class Role extends EntityModelTemplate{
   String role;

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "role")
   List<UserRoles> userRoles;
}

我想进行一个单独的数据库调用来获取用户和角色,所以我尝试使用规范。我阅读了文档并在线搜索了示例,然后才想出了应该像我想要的那样工作的东西。

服务方式(按推荐编辑):

public UserDTO getUserForSecurityCheck(String username) throws UsernameNotFoundException {
    log.info("Repository call start!");
    Optional<User> user = userRepo.findOne(Specification.where(UserSpecifications.usernameEquals(username)));
    log.info("Repository call end!");
    return user.map(UserDTO::new).orElseThrow(() -> new UsernameNotFoundException("Username "+username+" not found!"));
}

规格:

public static Specification<User> usernameEquals(String username){

    return (root, query, criteriaBuilder) -> {
        ( (Join<Object, Object>) root.fetch(User_.USER_ROLES)).fetch(UserRoles_.ROLE);
        return criteriaBuilder.equal(root.get(User_.USERNAME), username);
    };
}

除了 Hibernate 进行两次数据库调用外,它工作得很好。

(编辑)

日志

第一个使用两个连接选择所有需要的用户名=?(它相当长,因此是简短的版本)。这正是我想要它做的。但随后它进行了第二次调用,该调用实际上是从 user_id=? 的用户中选择 *(userRoles 除外)。

为什么?

尽管我在一般情况下仍然是编码新手,但我很确定我了解规范和 Hibernate 是如何工作的。至少是基本的。显然我没有。

所以,我的问题是:假设要进行两次数据库调用还是我做错了什么?

提前致谢。

标签: javaspring-boothibernate-criteria

解决方案


我发现了自己的错误,并且因为没有立即注意到它而感到非常尴尬:默认情况下,UserRoles 中的用户被急切地加载。那是第二个电话。我把它改成了懒惰的,它按预期工作。

如果不是 M.Deinum 询问一个惰性集合,我一百万年都不会注意到它。


推荐阅读