FetchType的区别是什么。LAZY和FetchType。Java持久性API中的EAGER ?


当前回答

我想在上面说的基础上补充这一点。

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

Controller <-> Service <-> Repository

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

根据设计、性能和开发人员的不同,有两种常见的方法来解决这个问题:

最简单的方法是使用FetchType。EAGER或任何其他反模式解决方案,使会话仍然活跃在控制器方法,但这些方法将影响性能。 最佳实践是使用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 = "addresses", allowSetters = true)
    private User user;

    // Getters, setters, equals, and hashCode
}

其他回答

有一个小小的评论: 如果你使用惰性类型,如果你关闭会话,你以后将不能从数据库中获取数据(参见下面的输出)。

但是对于Eager类型,你在获取Instructor的同时获取数据,所以在session.close()之后,你将能够使用/显示这些课程列表数据。

  @OneToMany(//fetch = FetchType.EAGER, 
        fetch = FetchType.LAZY,  
           mappedBy = "instructor",        
           cascade = {CascadeType.DETACH, CascadeType.MERGE,
                CascadeType.PERSIST, CascadeType.REFRESH})
private List<Course> courseList; 

我建议在调试模式下尝试这两种方法。在这种情况下,我使用惰性类型,你可以看到。

 try {
        //start the transaction
        session.beginTransaction();

        //Get instructor from database
        int instructorId = 7;

        Instructor tempInstructor = session.get(Instructor.class,instructorId);

        System.out.println("Instructor: "+tempInstructor);


        //commit transaction
        session.getTransaction().commit();

       //close session
       session.close();

       //since courselist is lazy loaded... this should fail
       //so in here we are not able to fetch courselist data
       //get courses
        System.out.println("Courses "+tempInstructor.getCourseList() );


        System.out.println("Done!");

    } finally {
        session.close();
        factory.close();
    }
}

输出异常:

Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.exercise.hibernate.entity.Instructor.courseList, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:621)
at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453)
at java.base/java.lang.StringConcatHelper.simpleConcat(StringConcatHelper.java:408)
at com.exercise.hibernate.main.EagerLazyLoading.main(EagerLazyLoading.java:56)

有时你有两个实体它们之间有关系。例如,你可能有一个实体叫University,另一个实体叫Student,一个University可能有很多Student:

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

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

   // setters and getters
}

现在,当您从数据库加载University时,JPA将为您加载它的id、名称和地址字段。但是你有两种选择:

将它与其他字段一起加载(即急切地加载),或者 在调用大学的getStudents()方法时按需加载它(即惰性加载)。

当一所大学有很多学生时,加载所有的学生并不是有效的,特别是当他们不需要的时候,在这种情况下,你可以声明你想要在实际需要学生的时候加载他们。这被称为惰性加载。

下面是一个例子,学生被明确地标记为急切加载:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

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

    // etc.    
}

这里有一个例子,学生被明确地标记为惰性加载:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

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

    // etc.
}

基本上,

LAZY = fetch when needed
EAGER = fetch immediately

public enum FetchType extends java.lang.Enum Defines strategies for fetching data from the database. The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. Example: @Basic(fetch=LAZY) protected String getName() { return name; }

来自Javadoc:

EAGER策略是持久性提供程序运行时的一个要求,即必须急切地获取数据。LAZY策略是对持久性提供程序运行时的一个提示,即在第一次访问数据时应该以惰性方式获取数据。

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