FetchType的区别是什么。LAZY和FetchType。Java持久性API中的EAGER ?
当前回答
FetchType。LAZY和FetchType。EAGER用于定义默认的读取计划。
不幸的是,您只能覆盖LAZY抓取的默认抓取计划。EAGER抓取不太灵活,可能导致许多性能问题。
我的建议是克制将关联设置为EAGER的冲动,因为取回是查询时的责任。因此,您的所有查询都应该使用fetch指令来只检索当前业务用例所需的内容。
其他回答
这两种类型的抓取之间的主要区别是数据加载到内存中的时刻。 我附上了2张照片来帮助你理解这一点。
急于获取
延迟获取
有一个小小的评论: 如果你使用惰性类型,如果你关闭会话,你以后将不能从数据库中获取数据(参见下面的输出)。
但是对于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)
LAZY:它惰性地获取子实体,即在获取父实体时,它只是获取子实体的代理(由cglib或任何其他实用程序创建),当你访问子实体的任何属性时,它实际上是由hibernate获取的。
EAGER:它与父实体一起获取子实体。
为了更好地理解,请参阅Jboss文档,或者使用hibernate。Show_sql =true,检查hibernate发出的查询。
我想在上面说的基础上补充这一点。
假设您正在使用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
}
要理解它们之间的区别,最好的方法是了解lazy-okay。 FetchType。LAZY告诉hibernate在使用关系时只从数据库中获取相关的实体。
p.s.:在我参与过的许多项目中,我看到许多软件开发人员并不重视他们,甚至有人称自己为高级开发人员。如果您正在进行的项目不是在大量数据上进行数据交换,那么在这里使用EAGER是可以的。但是,考虑到可能出现n+1个问题的问题,您需要在默认情况下知道关系的获取类型之后注意这些问题。
这里你可以看到默认值: Hibernate中的默认获取类型为一对一、多对一和一对多
而且,即使在理解了取回类型之后,它也不会就此结束。要理解何时使用LAZY和何时使用EAGER,还需要理解单向和双向的概念。此外,spring引导存储库有一些方法允许它为懒惰或急切的用户读取数据。例如,getOne()或getById()方法允许您从实体中延迟获取数据。简而言之,你用什么,什么时候用,取决于对方想让你做什么。
推荐文章
- 套接字的连接超时和读超时之间的区别是什么?
- Java整数到字节数组
- 如何设置Windows环境下Java的环境变量
- Java Swing revalidate() vs repaint()
- Java中文件中的行数
- 指定的子节点已经有一个父节点。你必须先在子对象的父对象上调用removeView() (Android)
- 对于一个布尔字段,它的getter/setter的命名约定是什么?
- 如何获得当前屏幕方向?
- 如何在Android中渲染PDF文件
- 什么是ORM,它是如何工作的,我应该如何使用它?
- 如何计算一个元素在列表中出现的次数
- c++中类似于java的instanceof
- 我如何解决错误“minCompileSdk(31)指定在一个依赖的AAR元数据”在本机Java或Kotlin?
- 如何POST表单数据与Spring RestTemplate?
- 如何使用Laravel和Eloquent查询两个日期之间?