654

我是 Java Persistence API 和 Hibernate 的新手。

Java Persistence APIFetchType.LAZY和in之间有什么区别?FetchType.EAGER

4

17 回答 17

1265

有时您有两个实体,并且它们之间存在关系。例如,您可能有一个名为的实体University和另一个名为的实体Student,而大学可能有许多学生:

University 实体可能具有一些基本属性,例如 id、name、address 等,以及一个名为 students 的集合属性,该属性返回给定大学的学生列表:

一所大学有很多学生

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

   // setters and getters
}

现在,当您从数据库中加载大学时,JPA 会为您加载其 id、name 和 address 字段。但是对于如何加载学生,您有两种选择:

  1. 将其与其余字段一起加载(即急切地),或
  2. getStudents()当您调用大学的方法时按需(即懒惰地)加载它。

当一所大学有很多学生时,将所有学生一起加载是没有效率的,尤其是当他们不需要时,在这种情况下,您可以声明您希望学生在实际需要时加载。这称为延迟加载。

这是一个示例,其中students明确标记为急切加载:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

    @OneToMany(fetch = FetchType.EAGER)
    private List<Student> students;

    // etc.    
}

这是一个students明确标记为延迟加载的示例:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

    @OneToMany(fetch = FetchType.LAZY)
    private List<Student> students;

    // etc.
}
于 2010-06-07T15:53:56.430 回答
330

基本上,

LAZY = fetch when needed
EAGER = fetch immediately
于 2010-06-07T15:31:16.860 回答
74

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

LAZY另一方面意味着List只有当您尝试访问它们时才会获取它们的内容。例如,通过调用course.getStudents().iterator(). 调用 上的任何访问方法List将启动对数据库的调用以检索元素。List这是通过在(或)周围创建代理来实现的Set。因此,对于您的惰性集合,具体类型不是ArrayListand HashSet,而是PersistentSetand PersistentList(or PersistentBag)

于 2010-06-07T15:54:39.220 回答
19

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

于 2013-01-29T02:39:22.720 回答
12

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

(礼貌:-objectdbcom)

于 2013-07-22T13:21:51.353 回答
11

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

FetchType.LAZY按需提供(即当我们需要数据时)。

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

于 2014-03-11T12:20:53.733 回答
11

两者FetchType.LAZYFetchType.EAGER都用于定义默认提取计划。

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

我的建议是抑制让你的关联变得渴望的冲动,因为获取是查询时的责任。因此,您的所有查询都应使用fetch指令来仅检索当前业务案例所需的内容。

于 2014-11-09T07:31:58.320 回答
9

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

渴望获取
渴望获取

懒惰的获取 懒惰的获取

于 2020-05-25T08:27:43.647 回答
8

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

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

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

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

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

Eager initialization需要更多的内存消耗并且处理速度很慢。

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

于 2017-05-14T14:24:18.930 回答
7

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

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

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

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

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

  1. 最简单的一种是使用FetchType.EAGER或任何其他反模式解决方案,这样会话在控制器方法处仍然是活动的,但这些方法会影响性能。
  2. 最佳实践是使用FetchType.LAZY映射器(如MapStruct)将数据从另一个数据对象传输Entity到另一个数据对象DTO,然后将其发送回控制器,因此如果会话关闭也不例外。

有一个简单的例子:

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

    @GetMapping("/users")
    public Page<UserDTO> getAllUsers(Pageable pageable) {
        return userService.getAllUsers(pageable);
    }
}

@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);
    }
}

@Repository
public interface UserRepository extends JpaRepository<User, String> {

    Page<User> findAll(Pageable pageable);
}

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
}

@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
}

@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 = "addesses", allowSetters = true)
    private User user;

    // Getters, setters, equals, and hashCode
}
于 2019-07-01T12:22:43.003 回答
6

Javadoc

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

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

于 2010-06-07T15:31:35.687 回答
3

图书.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;
        }

        }

主题.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;
}
} 

主.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]for @ManyToOnefetchType=EAGER)。我们可以通过在@OneToManySubject.java 上放置 fetchType.EAGER 或@ManyToOne在 Books.java 中放置 fetchType.LAZY 来改变行为。

于 2016-04-30T04:46:59.560 回答
1

public enum FetchType extends java.lang.Enum 定义从数据库中获取数据的策略。EAGER 策略是对持久性提供程序运行时的要求,即必须急切地获取数据。LAZY 策略是对持久性提供程序运行时的提示,即在首次访问数据时应该延迟获取数据。允许该实现急切地获取已指定 LAZY 策略提示的数据。示例:@Basic(fetch=LAZY) protected String getName() { return name; }

来源

于 2016-08-29T16:10:20.353 回答
1

JOIN很重要

以简单的方式接受它:

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

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

SELECT * FROM users 

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

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

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

哪种获取类型更好?

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

如果您使用的是Spring Boot 框架,请application.properties归档并添加以下命令以了解究竟发生了什么。

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
于 2021-03-09T10:59:20.150 回答
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;
    }
    //...
}
于 2014-03-06T09:06:04.403 回答
0

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

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

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

于 2017-04-01T04:23:27.153 回答
0

了解它们之间区别的最好方法是如果您了解惰性,可以。FetchType.LAZY 告诉 hibernate 仅在您使用关系时从数据库中获取相关实体。

PS:在我做的很多项目中,我看到很多软件开发者不重视,甚至有自称资深的。如果你做的项目不是大数据交换数据量大,这里有 EAGER 没问题。但是考虑到可能出现n+1个问题的问题,在了解了默认关系的fetch类型后,需要注意这些问题。

在这里你可以看到默认值: Hibernate 中一对一、多对一和一对多的默认获取类型

此外,即使在了解获取类型之后,它也不会就此结束。要了解何时使用 LAZY 以及何时使用 EAGER,还需要了解单向和双向的概念。另外,spring boot 仓库有一些方法可以让它为你lazy或者eager读取数据。比如getOne()orgetById()方法可以让你lazy地从实体中获取数据。总之,你用什么,什么时候用取决于对方什么要你做。

于 2022-03-01T05:49:21.560 回答