我是Java Persistence API和Hibernate的新手。
FetchType.LAZY
和FetchType.EAGER
Java Persistence API 之间有什么区别?
我是Java Persistence API和Hibernate的新手。
FetchType.LAZY
和FetchType.EAGER
Java Persistence API 之间有什么区别?
Answers:
有时您有两个实体,并且它们之间存在关系。例如,您可能有一个名为的实体University
,另一个实体名为,Student
而一所大学可能有很多学生:
University实体可能具有一些基本属性,例如id,名称,地址等,还有一个名为students的集合属性,该属性返回给定大学的学生列表:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
现在,当您从数据库加载大学时,JPA会为您加载其ID,名称和地址字段。但是对于如何加载学生,您有两个选择:
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.
}
getStudents()
)将目标实体加载到内存中时,JDBC会话仍必须处于打开状态,但是有时这是不可能的,因为到此方法时调用时,会话已经关闭并且实体已分离。同样,有时我们具有客户端/服务器体系结构(例如,Swing客户端/ JEE服务器),并且实体/ DTO通过电线传输到客户端,并且在大多数情况下,由于实体的方式,延迟加载无法正常工作通过电线进行序列化。
getStudents()
第一次调用该方法时,结果是否被缓存?以便下次可以更快地访问这些结果?
EAGER
集合的加载意味着在获取其父项时将其完全获取。所以,如果你有Course
,它有 List<Student>
,所有的学生都获取从数据库在当时Course
被取出。
LAZY
另一方面意味着List
仅当您尝试访问时,才会获取的内容。例如,通过调用course.getStudents().iterator()
。调用上的任何访问方法List
将启动对数据库的调用以检索元素。这是通过在List
(或Set
)周围创建一个代理来实现的。因此,对于您的惰性集合,具体类型不是ArrayList
和HashSet
,而是PersistentSet
和PersistentList
(或PersistentBag
)
course.getStudents()
,它会触发一个SQL查询(在控制台上看到了)。在懒惰获取类型中,同样的事情也会发生。那么,有什么区别呢?
fetchtype = LAZY
默认的,即使尝试获得与吸气hibernete集合抛出一个错误,告诉我它无法评估
默认情况下,对于所有收集和映射对象,提取规则均为该规则,FetchType.LAZY
而对于其他实例,它遵循该FetchType.EAGER
策略。
简言之,@OneToMany
而@ManyToMany
关系不implicictly获取相关对象(采集和地图),但检索操作通过现场级联@OneToOne
和@ManyToOne
的。
双方FetchType.LAZY
并FetchType.EAGER
用来定义默认提取计划。
不幸的是,您只能为LAZY提取覆盖默认的提取计划。EAGER提取的灵活性较差,可能导致许多性能问题。
我的建议是限制使关联变得更EAGER的冲动,因为获取是查询时的责任。因此,所有查询都应使用fetch指令来仅检索当前业务案例所需的内容。
从Javadoc:
EAGER策略是对持久性提供程序运行时的要求,必须热切地获取数据。LAZY策略向持久性提供程序运行时提供了提示,即首次访问数据时应延迟获取数据。
例如,渴望比懒惰更积极主动。懒惰仅在首次使用时发生(如果提供者接受了提示),而急切的事情(可能)会被预先获取。
该Lazy
提取类型默认情况下由Hibernate选择,除非你明确地标记Eager
取型。为了更加准确和简洁,可以将差异说明如下。
FetchType.LAZY
=除非您通过getter方法调用关系,否则不会加载关系。
FetchType.EAGER
=这将加载所有关系。
这两种类型的利弊。
Lazy initialization
通过避免不必要的计算来提高性能并减少内存需求。
Eager initialization
需要更多的内存消耗并且处理速度很慢。
话虽如此,根据情况,可以使用这些初始化中的任何一种。
getMember
调用一个与成员名称模式完全匹配的函数为止?
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]
for @ManyToOne
,fetchType=EAGER
)。我们可以通过在@OneToMany
Subject.java上放置fetchType.EAGER 或在Books.java上放置fetchType.LAZY来更改行为@ManyToOne
。
公共枚举FetchType扩展java.lang.Enum定义用于从数据库中获取数据的策略。EAGER策略是对持久性提供程序运行时的要求,必须热切地获取数据。LAZY策略向持久性提供程序运行时提供了提示,即首次访问数据时应延迟获取数据。该实现被允许急切地获取已为其指定LAZY策略提示的数据。示例:@Basic(fetch = LAZY)受保护的字符串getName(){返回名称;}
我想将此注释添加到上面的“庆焕民”中。
假设您正在使用带有此简单架构师的Spring Rest:
控制器<->服务<->存储库
而且您想将一些数据返回到前端,如果您正在使用FetchType.LAZY
,则在将数据返回到controller方法后,由于服务中的会话已关闭,因此JSON Mapper Object
无法获取数据,因此您将获得异常。
有三种常见的解决方案,取决于设计,性能和开发人员:
FetchType.EAGER
,以使该会话在控制器方法下仍然有效。FetchType.LAZY
与转换器方法一起使用,以将数据从Entity
另一个数据对象传输DTO
并将其发送到控制器,因此,如果会话关闭,也不例外。@ 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;
}
//...
}