Java Persistence API中FetchType LAZY和EAGER之间的区别?

leo*_*eon 512 java orm hibernate jpa java-persistence-api

我是Java Persistence API和Hibernate的新手.

Java Persistence API FetchType.LAZY和之间的区别是什么FetchType.EAGER

Lam*_*bda 984

有时你有两个实体,它们之间有关系.例如,您可能有一个名为University的实体和另一个名为Student的实体.

大学实体可能有一些基本属性,如身份证,姓名,地址等,以及一个名为学生的财产:

public class University {
   private String id;
   private String name;
   private String address;
   private List<Student> students;

   // setters and getters
}
Run Code Online (Sandbox Code Playgroud)

现在,当您从数据库加载大学时,JPA会为您加载其ID,名称和地址字段.但是对于学生来说,您有两种选择:将其与其他字段一起加载(即热切地)或者在您调用大学的getStudents()方法时按需加载(即懒惰).

当一所大学有很多学生时,在不需要的时候加载所有学生并不高效.因此,在这种情况下,您可以声明您希望学生在实际需要时加载.这称为延迟加载.

  • @ADTC为了使延迟加载工作,当目标实体想要通过调用getter方法(例如`getStudents()`)加载到内存中时,JDBC会话仍然必须打开,但有时这是不可能的,因为在调用此方法时,会话已关闭且实体已分离.类似地,有时我们有一个客户端/服务器架构(例如Swing客户端/ JEE服务器),实体/ DTO通过线路传输到客户端,并且在这些场景中最常见的是,由于实体的方式,延迟加载将不起作用通过电线序列化. (64认同)
  • @BehrangSaeedzadeh你能列​​出一些实际的差异,或者每种加载方式的优缺点(除了你提到的效率).为什么要使用急切加载? (4认同)
  • 我想从我的书中为这个答案添加更多信息 - 为了节省内存,延迟加载通常用于一对多和多对多的关系.对于一对一,通常使用Eager. (3认同)
  • 在延迟加载中,当我第一次调用`getStudents()`方法时,结果是否被缓存?这样我下次可以更快地访问这些结果? (2认同)
  • @JavaTechnical取决于是否启用二级缓存(默认情况下启用) (2认同)

unb*_*eli 265

基本上,

LAZY = fetch when needed
EAGER = fetch immediately
Run Code Online (Sandbox Code Playgroud)

  • 非常清楚,但只有在阅读@Bhanghang的回答后.谢谢你的总结.:-) (11认同)

Boz*_*zho 64

EAGER加载集合意味着在获取父级时完全获取它们.因此,如果你拥有CourseList<Student>,那么所有的学生都会在获取时从数据库Course获取.

LAZY另一方面,意味着List只有在您尝试访问它们时才会获取内容.例如,通过呼叫course.getStudents().iterator().调用任何访问方法List将启动对数据库的调用以检索元素.这是通过围绕List(或Set)创建代理来实现的.所以对于你的懒惰集合,具体类型不是ArrayListHashSet,但是PersistentSetPersistentList(或PersistentBag)

  • @Bozho您仅指定了集合的延迟加载。简单的字符串字段可以延迟加载吗? (2认同)

Kyu*_*Min 16

我可以考虑性能和内存利用率.一个很大的区别是EAGER获取策略允许在没有会话的情况下使用获取的数据对象.为什么?
当会话连接时,当对象中的标记数据急切时,将获取所有数据.但是,在延迟加载策略的情况下,如果会话断开连接(session.close()语句之后),则延迟加载标记的对象不会检索数据.所有这些都可以通过hibernate代理来完成.急切策略可让数据在关闭会话后仍然可用.

  • 是的,它使我的应用程序崩溃了,直到我切换到 eager。我认为会话管理本身就是一门艺术和科学,但 JawsDB 在免费层上提供了 10 个连接,而在付费层上则不多。 (2认同)

小智 11

默认情况下,对于所有集合和映射对象,提取规则是FetchType.LAZY,对于其他实例,它遵循FetchType.EAGER策略.
简言之,@OneToMany@ManyToMany关系不implicictly获取相关对象(采集和地图),但检索操作通过现场级联@OneToOne@ManyToOne的.

(礼貌: - objectdbcom)


JDG*_*ide 10

据我所知,两种类型的提取都取决于您的要求.

FetchType.LAZY 按需(即我们需要数据时).

FetchType.EAGER 是即时的(即在我们的要求到来之前,我们不必要地获取记录)


Ebr*_*ee' 10

我想将这条注释添加到上面所说的内容中。

假设您将 Spring(MVC 和数据)与这个简单的架构师一起使用:

控制器 <-> 服务 <-> 存储库

如果你想返回一些数据到前端,如果你使用FetchType.LAZY,你会LazyInitializationException在将数据返回到控制器方法后得到一个 ,因为会话在 Service 中关闭,所以JSON Mapper Object无法获取数据。

根据设计、性能和开发人员的不同,有两种常见的选项可以解决此问题:

  1. 最简单的一种是使用FetchType.EAGER或任何其他反模式解决方案,以便会话在控制器方法中仍然有效,但这些解决方案会影响性能。
  2. 最佳实践是FetchType.LAZY与映射器(如)一起使用,将数据从另一个数据对象MapStruct传输到另一个数据对象,然后将其发送回控制器,因此即使会话关闭也不会出现异常。EntityDTO

有一个简单的例子:

@RestController
@RequestMapping("/api")
public class UserResource {

    @GetMapping("/users")
    public Page<UserDTO> getAllUsers(Pageable pageable) {
        return userService.getAllUsers(pageable);
    }
}
Run Code Online (Sandbox Code Playgroud)
@Service
@Transactional
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional(readOnly = true)
    public Page<UserDTO> getAllUsers(Pageable pageable) {
        return userRepository.findAll(pageable).map(UserDTO::new);
    }
}
Run Code Online (Sandbox Code Playgroud)
@Repository
public interface UserRepository extends JpaRepository<User, String> {

    Page<User> findAll(Pageable pageable);
}
Run Code Online (Sandbox Code Playgroud)
public class UserDTO {

    private Long id;

    private String firstName;

    private String lastName;

    private String email;
    
    private Set<String> addresses;

    public UserDTO() {
        // Empty constructor needed for Jackson.
    }

    public UserDTO(User user) {
        this.id = user.getId();
        this.firstName = user.getFirstName();
        this.lastName = user.getLastName();
        this.email = user.getEmail();
        this.addresses = user.getAddresses().stream()
            .map(Address::getAddress)
            .collect(Collectors.toSet());
    }

    // Getters, setters, equals, and hashCode
}
Run Code Online (Sandbox Code Playgroud)
@Entity
@Table(name = "user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @Column
    private String firstName;

    @Column
    private String lastName;

    @Column(unique = true)
    private String email;
    
    @OneToMany(mappedBy = "address", fetch = FetchType.LAZY)
    private Set<Address> addresses = new HashSet<>();

    // Getters, setters, equals, and hashCode
}
Run Code Online (Sandbox Code Playgroud)
@Entity
@Table(name = "address")
public class Address implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @Column
    private String address;

    @ManyToOne
    @JsonIgnoreProperties(value = "addresses", allowSetters = true)
    private User user;

    // Getters, setters, equals, and hashCode
}
Run Code Online (Sandbox Code Playgroud)


Vla*_*cea 9

双方FetchType.LAZYFetchType.EAGER用来定义默认提取计划.

不幸的是,您只能覆盖LAZY抓取的默认抓取计划.EAGER提取不太灵活,可能导致许多性能问题.

我的建议是限制使你的协会成为EAGER的冲动,因为抓取是一个查询时间的责任.因此,您的所有查询都应该使用fetch指令来仅检索当前业务案例所需的内容.

  • *“ EAGER提取的灵活性较差,可能会导致许多性能问题。” * ...更真实的说法是“使用或不使用EAGER提取会导致性能问题”。在那种特殊情况下,当延迟初始化的字段访问起来昂贵且不经常使用时,延迟获取将有利于性能。但是,在频繁使用变量的情况下,与急切的初始化相比,懒惰的初始化可能会*需要更多的数据库访问,从而实际上降低性能。我建议正确而不是教条地应用FetchType。 (2认同)

Dul*_*sta 8

Lazy提取类型默认情况下由Hibernate选择,除非你明确地标记Eager取型。为了更准确和简洁,差异可以说明如下。

FetchType.LAZY = 这不会加载关系,除非您通过 getter 方法调用它。

FetchType.EAGER = 这将加载所有关系。

这两种获取类型的优缺点。

Lazy initialization 通过避免不必要的计算和减少内存需求来提高性能。

Eager initialization 占用内存较多,处理速度较慢。

话虽如此,根据情况可以使用这些初始化中的任何一个。

  • 值得注意的是,“除非您通过 getter 方法调用它,否则它不会加载关系”这一说法很重要,而且在我看来,这也是一个相当迟钝的设计决策……我刚刚遇到了一个情况,我认为它会在访问时获取它* 但它没有,因为我没有明确地为它调用 getter 函数。顺便问一下,什么是“getter”函数?JPA 是否会推迟加载属性,直到调用与成员名称模式完全匹配的名为“getMember”的函数? (2认同)

Ali*_*neh 8

两种类型的获取之间的主要区别在于数据加载到内存中的时刻。
我附上了2张照片来帮助你理解这一点。

渴望获取
 渴望获取

懒惰取 懒惰取


T.J*_*der 6

来自Javadoc:

EAGER策略是持久性提供程序运行时的一项要求,必须急切地获取数据.LAZY策略是持久性提供程序运行时的提示,即数据在首次访问时应该被懒惰地获取.

例如,渴望比懒惰更积极主动.懒惰只在第一次使用时发生(如果提供者接受提示),而急切的事情(可能)会预先获取.

  • @MahmoudS:默认fetchtypes:OneToMany:LAZY,ManyToOne:EAGER,ManyToMany:LAZY,OneToOne:EAGER,专栏:EAGER (2认同)

Abd*_*leh 5

JOIN有什么大不了的

简单来说:

假设我们有一个名为 an 的类User,另一个名为 an 的类Address,并且假设每个用户都有一个或多个表示关系(一对多)的地址,如果您执行:

FetchType.LAZY执行 sql 命令就像没有join

SELECT * FROM users 
Run Code Online (Sandbox Code Playgroud)

FetchType.EAGER执行 sql 命令,如下所示join

SELECT * FROM users u join address a on a.user_id = u.user_id
Run Code Online (Sandbox Code Playgroud)

注意:上述查询只是为了向您澄清图像,但 Hibernate 框架在实际中执行与上述查询类似的查询。

哪种获取类型更好?

  • 由于急切获取会自动加载所有关系,因此它对性能要求很高
  • 除非被告知,延迟获取不会加载任何关系,这会带来更好的性能
  • 由于需要更少的代码,急切的获取使编程变得更容易
  • 如果整个系统没有经过适当的测试,延迟加载可能会导致错误(异常)
  • 考虑到所有因素,您仍然应该更喜欢延迟加载而不是急切加载,因为它的性能更高

如果您使用的是Spring Boot 框架,请提交application.properties文件并添加以下命令以了解到底发生了什么。

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
Run Code Online (Sandbox Code Playgroud)