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

时间:2010-06-07 15:28:44

标签: java hibernate jpa orm java-persistence-api

我是Java Persistence API和Hibernate的新手。

Java Persistence API中FetchType.LAZYFetchType.EAGER之间有什么区别?

16 个答案:

答案 0 :(得分:902)

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

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

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

 // setters and getters
}

现在,当您从数据库加载大学时,JPA会为您加载其ID,名称和地址字段。但是你可以为学生提供两种选择:将它与其他字段一起加载(即急切地)或者在你调用大学的getStudents()方法时按需加载(即懒惰)。

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

答案 1 :(得分:246)

基本上,

LAZY = fetch when needed
EAGER = fetch immediately

答案 2 :(得分:59)

EAGER加载集合意味着在获取父级时完全获取它们。因此,如果您有Course并且List<Student>,那么在提取Course时,所有学生都会从数据库中获取

另一方面,

LAZY表示仅在您尝试访问List时才会获取course.getStudents().iterator()的内容。例如,通过调用List。在List上调用任何访问方法将启动对数据库的调用以检索元素。这是通过在Set(或ArrayList)周围创建代理来实现的。因此,对于您的惰性集合,具体类型不是HashSetPersistentSet,而是PersistentListPersistentBag(或{{1}})

答案 3 :(得分:13)

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

答案 4 :(得分:11)

默认情况下,对于所有集合和映射对象,提取规则为FetchType.LAZY,而对于其他实例,它遵循FetchType.EAGER策略。
简而言之,@OneToMany@ManyToMany关系不会隐含地获取相关对象(集合和映射),但检索操作通过@OneToOne@ManyToOne中的字段级联。

(courtesy :- objectdbcom)

答案 5 :(得分:10)

根据我的知识,两种类型的提取都取决于您的要求。

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

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

答案 6 :(得分:9)

FetchType.LAZYFetchType.EAGER都用于定义default fetch plan

不幸的是,您只能覆盖LAZY抓取的默认抓取计划。 EAGER提取的灵活性较低,可以lead to many performance issues

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

答案 7 :(得分:6)

来自Javadoc

  

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

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

答案 8 :(得分:5)

两种类型的访存之间的主要区别是数据被加载到内存中的时刻。
我附上了2张照片,以帮助您了解这一点。

热切获取
 Eager fetch

懒惰获取 Lazy fetch

答案 9 :(得分:4)

除非您明确标记Lazy抓取类型,否则Hibernate默认选择Eager抓取类型。为了更准确和简洁,差异可以表述如下。

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

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

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

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

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

话虽如此,取决于具体情况可以使用其中一个初始化。

答案 10 :(得分:3)

<强> Book.java

        import java.io.Serializable;
        import javax.persistence.Column;
        import javax.persistence.Entity;
        import javax.persistence.GeneratedValue;
        import javax.persistence.GenerationType;
        import javax.persistence.Id;
        import javax.persistence.ManyToOne;
        import javax.persistence.Table;

        @Entity
        @Table(name="Books")
        public class Books implements Serializable{

        private static final long serialVersionUID = 1L;
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="book_id")
        private int id;
        @Column(name="book_name")
        private String name;

        @Column(name="author_name")
        private String authorName;

        @ManyToOne
        Subject subject;

        public Subject getSubject() {
            return subject;
        }
        public void setSubject(Subject subject) {
            this.subject = subject;
        }

        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAuthorName() {
            return authorName;
        }
        public void setAuthorName(String authorName) {
            this.authorName = authorName;
        }

        }

<强> Subject.java

    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue; 
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;

    @Entity
    @Table(name="Subject")
    public class Subject implements Serializable{

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="subject_id")
    private int id;
    @Column(name="subject_name")
    private String name;
    /**
    Observe carefully i have mentioned fetchType.EAGER. By default its is fetchType.LAZY for @OneToMany i have mentioned it but not required. Check the Output by changing it to fetchType.EAGER
    */

    @OneToMany(mappedBy="subject",cascade=CascadeType.ALL,fetch=FetchType.LAZY,
orphanRemoval=true)
    List<Books> listBooks=new ArrayList<Books>();

    public List<Books> getListBooks() {
        return listBooks;
    }
    public void setListBooks(List<Books> listBooks) {
        this.listBooks = listBooks;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    }

<强> HibernateUtil.java

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {

 private static SessionFactory sessionFactory ;
 static {
    Configuration configuration = new Configuration();
    configuration.addAnnotatedClass (Com.OneToMany.Books.class);
    configuration.addAnnotatedClass (Com.OneToMany.Subject.class);
    configuration.setProperty("connection.driver_class","com.mysql.jdbc.Driver");
    configuration.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate");                                
    configuration.setProperty("hibernate.connection.username", "root");     
    configuration.setProperty("hibernate.connection.password", "root");
    configuration.setProperty("dialect", "org.hibernate.dialect.MySQLDialect");
    configuration.setProperty("hibernate.hbm2ddl.auto", "update");
    configuration.setProperty("hibernate.show_sql", "true");
    configuration.setProperty(" hibernate.connection.pool_size", "10");
    configuration.setProperty(" hibernate.cache.use_second_level_cache", "true");
    configuration.setProperty(" hibernate.cache.use_query_cache", "true");
    configuration.setProperty(" cache.provider_class", "org.hibernate.cache.EhCacheProvider");
    configuration.setProperty("hibernate.cache.region.factory_class" ,"org.hibernate.cache.ehcache.EhCacheRegionFactory");

   // configuration
    StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
    sessionFactory = configuration.buildSessionFactory(builder.build());
 }
public static SessionFactory getSessionFactory() {
    return sessionFactory;
}
} 

<强> Main.java

    import org.hibernate.Session;
    import org.hibernate.SessionFactory;

    public class Main {

    public static void main(String[] args) {
        SessionFactory factory=HibernateUtil.getSessionFactory();
        save(factory);
        retrieve(factory);

    }

     private static void retrieve(SessionFactory factory) {
        Session session=factory.openSession();
        try{
            session.getTransaction().begin();
            Subject subject=(Subject)session.get(Subject.class, 1);
            System.out.println("subject associated collection is loading lazily as @OneToMany is lazy loaded");

            Books books=(Books)session.get(Books.class, 1);
            System.out.println("books associated collection is loading eagerly as by default @ManyToOne is Eagerly loaded");
            /*Books b1=(Books)session.get(Books.class, new Integer(1));

            Subject sub=session.get(Subject.class, 1);
            sub.getListBooks().remove(b1);
            session.save(sub);
            session.getTransaction().commit();*/
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            session.close();
        }

        }

       private static void save(SessionFactory factory){
        Subject subject=new Subject();
        subject.setName("C++");

        Books books=new Books();
        books.setAuthorName("Bala");
        books.setName("C++ Book");
        books.setSubject(subject);

        subject.getListBooks().add(books);
        Session session=factory.openSession();
        try{
        session.beginTransaction();

        session.save(subject);

        session.getTransaction().commit();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            session.close();
        }
    }

    }

检查Main.java的retrieve()方法。当我们获得Subject时,它的集合 listBooks ,用@OneToMany注释,将被懒惰地加载。但是,另一方面,书籍相关的集合主题的关联,用@ManyToOne注释,正在加载[default][1] @ManyToOnefetchType=EAGER )。我们可以通过在@OneToMany Subject.java上放置fetchType.EAGER或在Books.java中的@ManyToOne上放置fetchType.LAZY来更改行为。

答案 11 :(得分:1)

  

public enum FetchType   扩展java.lang.Enum   定义从数据库中获取数据的策略。 EAGER策略是持久性提供程序运行时的一项要求,必须急切地获取数据。 LAZY策略是持久性提供程序运行时的提示,即数据在首次访问时应该被懒惰地获取。允许实现急切地获取已指定LAZY策略提示的数据。      例:      @Basic(取=懒惰)      protected String getName(){返回名称; }

Source

答案 12 :(得分:0)

@ drop-shadow如果你正在使用Hibernate,你可以在调用Hibernate.initialize()方法时调用getStudents()

Public class UniversityDaoImpl extends GenericDaoHibernate<University, Integer> implements UniversityDao {
    //...
    @Override
    public University get(final Integer id) {
        Query query = getQuery("from University u where idUniversity=:id").setParameter("id", id).setMaxResults(1).setFetchSize(1);
        University university = (University) query.uniqueResult();
        ***Hibernate.initialize(university.getStudents());***
        return university;
    }
    //...
}

答案 13 :(得分:0)

LAZY:它懒惰地获取子实体,即在获取父实体时它只获取子实体的代理(由cglib或任何其他实用程序创建),当您访问子实体的任何属性时,它实际上被获取通过hibernate。

EAGER:它与父级一起获取子实体。

为了更好地理解,请转到Jboss文档,或者您可以将hibernate.show_sql=true用于您的应用,并检查hibernate发出的查询。

答案 14 :(得分:0)

我想将此注释添加到上面的“庆焕民”中。

假设您正在使用带有此简单架构师的Spring Rest:

  

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

并且您想将一些数据返回到前端,如果您使用的是FetchType.LAZY,则由于将服务中的会话关闭,因此在将数据返回到控制器方法之后,您将获得异常,因此{ {1}}无法获取数据。

有三种常见的解决方案,取决于设计,性能和开发人员:

  1. 最简单的方法是使用JSON Mapper Object,以便该会话在控制器方法下仍然有效。
  2. Anti-patterns解决方案,要使会话有效直到执行结束,这在系统中是一个巨大的性能问题。
  3. 最佳实践是将FetchType.EAGER与转换器方法一起使用,以将数据从FetchType.LAZY传输到另一个数据对象Entity并将其发送到控制器,因此,如果会话关闭,也不例外。

答案 15 :(得分:0)

JOIN 很重要

简单点:

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

FetchType.LAZY 像没有 join 一样执行 sql 命令:

SELECT * FROM users 

FetchType.EAGER 像在 join 中那样执行 sql 命令:

SELECT * FROM users u join address a on a.user_id = u.user_id

注意:上面的查询只是为了给你澄清图像,但实际上 Hibernate 框架会执行上面类似的查询。

哪种抓取类型更好?

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

如果您使用的是 Spring Boot Framework,那么转到 application.properties 文件并添加以下命令以了解究竟发生了什么。

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE