热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Hibernate延迟加载深入分析-集合属性的延迟加载策略

本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。

Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载。Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hibernate 的运行性能。

下面先来剖析 Hibernate 延迟加载的“秘密”。

集合属性的延迟加载

当 Hibernate 从数据库中初始化某个持久化实体时,该实体的集合属性是否随持久化类一起初始化呢?如果集合属性里包含十万,甚至百万的记录,在初始化持久化实体的同时, 完成所有集合属性的抓取,将导致性能急剧下降。完全有可能系统只需要使用持久化类集合属性中的部分记录,而完全不是集合属性的全部,这样,没有必要一次加 载所有的集合属性。

对于集合属性,通常推荐使用延迟加载策略。所谓延迟加载就是等系统需要使用集合属性时才从数据库装载关联的数据。

例如下面 Person 类持有一个集合属性,该集合属性里的元素的类型为 Address,该 Person 类的代码片段如下:


清单 1. Person.java

Displaycode代码  收藏代码
  1.                
  2. public class Person   
  3. {   
  4. // 标识属性  
  5. private Integer id;   
  6. // Person 的 name 属性  
  7. private String name;   
  8. // 保留 Person 的 age 属性  
  9. private int age;   
  10. // 使用 Set 来保存集合属性  
  11. private Set
     addresses = new HashSet
    ();   
  12. // 下面省略了各属性的 setter 和 getter 方法  
  13. ...   
  14. }   

 

为了让 Hibernate 能管理该持久化类的集合属性,程序为该持久化类提供如下映射文件:


清单 2. Person.hbm.xml

Displaycode代码  收藏代码
  1.                    
  2.  "1.0" encoding="GBK"?>   
  3.  
  4. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  5. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">   
  6.  "org.crazyit.app.domain">   
  7.     
  8.  "Person" table="person_inf">   
  9.     
  10.  "id" column="person_id">   
  11.     
  12.  "identity"/>   
  13.     
  14.     
  15.  "name" type="string"/>   
  16.  "age" type="int"/>   
  17.     
  18.  "addresses" table="person_address" lazy="true">   
  19.     
  20.  "person_id"/>   
  21.  "Address">   
  22.     
  23.  "detail"/>   
  24.     
  25.  "zip"/>   
  26.     
  27.     
  28.     
  29.     

 

从上面映射文件的代码可以看出,Person 的集合属性中的 Address 类只是一个普通的 POJO。该 Address 类里包含 detail、zip 两个属性。由于 Address 类代码非常简单,故此处不再给出该类的代码。

上面映射文件中 元素里的代码指定了 lazy="true"(对于 元素来说,lazy="true"是默认值),它指定 Hibernate 会延迟加载集合属性里 Address 对象。

例如通过如下代码来加载 ID 为 1 的 Person 实体:

Displaycode代码  收藏代码
  1. Session session = sf.getCurrentSession();   
  2. Transaction tx = session.beginTransaction();   
  3. Person p &#61; (Person) session.get(Person.class, 1);  //<1>   
  4. System.out.println(p.getName());   

 

上面代码只是需要访问 ID 为 1 的 Person 实体&#xff0c;并不想访问这个 Person 实体所关联的 Address 对象。此时有两种情况&#xff1a;

  • 如果不延迟加载&#xff0c;Hibernate 就会在加载 Person 实体对应的数据记录时立即抓取它关联的 Address 对象。
  • 如果采用延迟加载&#xff0c;Hibernate 就只加载 Person 实体对应的数据记录。

很明显&#xff0c;第二种做法既能减少与数据库的交互&#xff0c;而且避免了装载 Address 实体带来的内存开销——这也是 Hibernate 默认启用延迟加载的原因。

现在的问题是&#xff0c;延迟加载到底是如何实现的呢&#xff1f; Hibernate 在加载 Person 实体时&#xff0c;Person 实体的 addresses 属性值是什么呢&#xff1f;

为了解决这个问题&#xff0c;我们在 <1>号代码处设置一个断点&#xff0c;在 Eclipse 中进行 Debug&#xff0c;此时可以看到 Eclipse 的 Console 窗口有如图 1 所示的输出&#xff1a;


图 1. 延迟加载集合属性的 Console 输出
图 1. 延迟加载集合属性的 Console 输出 

正如图 1 输出所看到的&#xff0c;此时 Hibernate 只从 Person 实体对应的数据表中抓取数据&#xff0c;并未从 Address 对象对应的数据表中抓取数据&#xff0c;这就是延迟加载。

那么 Person 实体的 addresses 属性是什么呢&#xff1f;此时可以从 Eclipse 的 Variables 窗口看到如图 2 所示的结果&#xff1a;


图 2. 延迟加载的集合属性值
图 2. 延迟加载的集合属性值 

从图 2 的方框里的内容可以看出&#xff0c;这个 addresses 属性并不是我们熟悉的 HashSet、TreeSet 等实现类&#xff0c;而是一个 PersistentSet 实现类&#xff0c;这是 Hibernate 为 Set 接口提供的一个实现类。

PersistentSet 集合对象并未真正抓取底层数据表的数据&#xff0c;因此自然也无法真正去初始化集合里的 Address 对象。不过 PersistentSet 集合里持有一个 session 属性&#xff0c;这个 session 属性就是 Hibernate Session&#xff0c;当程序需要访问 PersistentSet 集合元素时&#xff0c;PersistentSet 就会利用这个 session 属性去抓取实际的 Address 对象对应的数据记录。

那么到底抓取那些 Address 实体对应的数据记录呢&#xff1f;这也难不倒 PersistentSet&#xff0c;因为 PersistentSet 集合里还有一个 owner 属性&#xff0c;该属性就说明了 Address 对象所属的 Person 实体&#xff0c;Hibernate 就会去查找 Address 对应数据表中外键值参照到该 Person 实体的数据。

例如我们单击图 2 所示窗口中 addresses 行&#xff0c;也就是告诉 Eclipse 要调试、输出 addresses 属性&#xff0c;这就是要访问 addresses 属性了&#xff0c;此时就可以在 Eclipse 的 Console 窗口看到输出如下 SQL 语句&#xff1a;

Displaycode代码  收藏代码
  1. select   
  2.     addresses0_.person_id as person1_0_0_,   
  3.     addresses0_.detail as detail0_,   
  4.     addresses0_.zip as zip0_   
  5. from   
  6.     person_address addresses0_   
  7. where   
  8.     addresses0_.person_id&#61;?   

 

这就是 PersistentSet 集合跟据 owner 属性去抓取特定 Address 记录的 SQL 语句。此时可以从 Eclipse 的 Variables 窗口看到图 3 所示的输出&#xff1a;


图 3. 已加载的集合属性值
图 3. 已加载的集合属性值 

从图 3 可以看出&#xff0c;此时的 addresses 属性已经被初始化了&#xff0c;集合里包含了 2 个 Address 对象&#xff0c;这正是 Person 实体所关联的两个 Address 对象。

通过上面介绍可以看出&#xff0c;Hibernate 对于 Set 属性延迟加载关键就在于 PersistentSet 实现类。在延迟加载时&#xff0c;开始 PersistentSet 集合里并不持有任何元素。但 PersistentSet 会持有一个 Hibernate Session&#xff0c;它可以保证当程序需要访问该集合时“立即”去加载数据记录&#xff0c;并装入集合元素。

与 PersistentSet 实现类类似的是&#xff0c;Hibernate 还提供了 PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等实现类&#xff0c;它们的功能与 PersistentSet 的功能大致类似。

熟悉 Hibernate 集合属性读者应该记得&#xff1a;Hibernate 要求声明集合属性只能用 Set、List、Map、SortedSet、SortedMap 等接口&#xff0c;而不能用 HashSet、ArrayList、HashMap、TreeSet、TreeMap 等实现类&#xff0c;其原因就是因为 Hibernate 需要对集合属性进行延迟加载&#xff0c;而 Hibernate 的延迟加载是依靠 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、 PersistentSortedSet 来完成的——也就是说&#xff0c;Hibernate 底层需要使用自己的集合实现类来完成延迟加载&#xff0c;因此它要求开发者必须用集合接口、而不是集合实现类来声明集合属性。

Hibernate 对集合属性默认采用延迟加载&#xff0c;在某些特殊的情况下&#xff0c;为 等元素设置 lazy&#61;"false"属性来取消延迟加载。

回页首

关联实体的延迟加载

默认情况下&#xff0c;Hibernate 也会采用延迟加载来加载关联实体&#xff0c;不管是一对多关联、还是一对一关联、多对多关联&#xff0c;Hibernate 默认都会采用延迟加载。

对于关联实体&#xff0c;可以将其分为两种情况&#xff1a;

  • 关联实体是多个实体时&#xff08;包括一对多、多对多&#xff09;&#xff1a;此时关联实体将以集合的形式存在&#xff0c;Hibernate 将使用 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、 PersistentSortedSet 等集合来管理延迟加载的实体。这就是前面所介绍的情形。
  • 关联实体是单个实体时&#xff08;包括一对一、多对一&#xff09;&#xff1a;当 Hibernate 加载某个实体时&#xff0c;延迟的关联实体将是一个动态生成代理对象。

当关联实体是单个实体时&#xff0c;也就是使用 映射关联实体的情形&#xff0c;这两个元素也可通过 lazy 属性来指定延迟加载。

下面例子把 Address 类也映射成持久化类&#xff0c;此时 Address 类也变成实体类&#xff0c;Person 实体与 Address 实体形成一对多的双向关联。此时的映射文件代码如下&#xff1a;


清单 3. Person.hbm.xml

Displaycode代码  收藏代码
  1.                    
  2.  "1.0" encoding&#61;"GBK"?>   
  3.     
  4.  
  5. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  6. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">   
  7.  "org.crazyit.app.domain">   
  8.     
  9.  "Person" table&#61;"person_inf">   
  10.     
  11.  "id" column&#61;"person_id">   
  12.     
  13.  "identity"/>   
  14.     
  15.     
  16.  "name" type&#61;"string"/>   
  17.  "age" type&#61;"int"/>   
  18.     
  19.  "addresses" inverse&#61;"true">   
  20.     
  21.  "person_id"/>   
  22.     
  23.  "Address"/>   
  24.     
  25.     
  26.   
  27.     
  28.  "Address" table&#61;"address_inf">   
  29.     
  30.  "addressId" column&#61;"address_id">   
  31.     
  32.  "identity"/>   
  33.     
  34.     
  35.  "detail"/>   
  36.     
  37.  "zip"/>   
  38.     
  39.  "person" class&#61;"Person"  
  40.  column&#61;"person_id" not-null&#61;"true"/>   
  41.     
  42.     

 

接下来程序通过如下代码片段来加载 ID 为 1 的 Person 实体&#xff1a;

Displaycode代码  收藏代码
  1. // 打开上下文相关的 Session   
  2. Session session &#61; sf.getCurrentSession();   
  3. Transaction tx &#61; session.beginTransaction();   
  4. Address address &#61; (Address) session.get(Address.class , 1); //<1>   
  5. System.out.println(address.getDetail());   

 

为了看到 Hibernate 加载 Address 实体时对其关联实体的处理&#xff0c;我们在 <1>号代码处设置一个断点&#xff0c;在 Eclipse 中进行 Debug&#xff0c;此时可以看到 Eclipse 的 Console 窗口输出如下 SQL 语句&#xff1a;

Displaycode代码  收藏代码
  1. select   
  2.     address0_.address_id as address1_1_0_,   
  3.     address0_.detail as detail1_0_,   
  4.     address0_.zip as zip1_0_,   
  5.     address0_.person_id as person4_1_0_   
  6. from   
  7.     address_inf address0_   
  8. where   
  9.     address0_.address_id&#61;?   

 

从这条 SQL 语句不难看出&#xff0c;Hibernate 加载 Address 实体对应的数据表抓取记录&#xff0c;并未从 Person 实体对应的数据表中抓取记录&#xff0c;这是延迟加载发挥了作用。

从 Eclipse 的 Variables 窗口看到如图 4 所示的输出&#xff1a;


图 4. 延迟加载的实体
图 4. 延迟加载的实体 

从图 4 可以清楚地看到&#xff0c;此时 Address 实体所关联的 Person 实体并不是 Person 对象&#xff0c;而是一个 Person_$$_javassist_0 类的实例&#xff0c;这个类是 Hibernate 使用 Javassist 项目动态生成的代理类——当 Hibernate 延迟加载关联实体时&#xff0c;将会采用 Javassist 生成一个动态代理对象&#xff0c;这个代理对象将负责代理“暂未加载”的关联实体。

只要应用程序需要使用“暂未加载”的关联实体&#xff0c;Person_$$_javassist_0 代理对象会负责去加载真正的关联实体&#xff0c;并返回实际的关联实体——这就是最典型的代理模式。

单击图 4 所示 Variables 窗口中的 person 属性&#xff08;也就是在调试模式下强行使用 person 属性&#xff09;&#xff0c;此时看到 Eclipse 的 Console 窗口输出如下的 SQL 语句&#xff1a;

Displaycode代码  收藏代码
  1. select   
  2.     person0_.person_id as person1_0_0_,   
  3.     person0_.name as name0_0_,   
  4.     person0_.age as age0_0_   
  5. from   
  6.     person_inf person0_   
  7. where   
  8.     person0_.person_id&#61;?   

 

上面 SQL 语句就是去抓取“延迟加载”的关联实体的语句。此时可以看到 Variables 窗口输出图 5 所示的结果&#xff1a;


图 5. 已加载的实体
图 5. 已加载的实体 

Hibernate 采用“延迟加载”管理关联实体的模式&#xff0c;其实就在加载主实体时&#xff0c;并未真正去抓取关联实体对应数据&#xff0c;而只是动态地生成一个对象作为关联实体的代理。当应用程序真正需要使用关联实体时&#xff0c;代理对象会负责从底层数据库抓取记录&#xff0c;并初始化真正的关联实体。

在 Hibernate 的延迟加载中&#xff0c;客户端程序开始获取的只是一个动态生成的代理对象&#xff0c;而真正的实体则委托给代理对象来管理——这就是典型的代理模式。

回页首

代理模式

代理模式是一种应用非常广泛的设计模式&#xff0c;当客户端代码需要调用某个对象时&#xff0c;客户端实际上也不关心是否准确得到该对象&#xff0c;它只要一个能提供该功能的对象即可&#xff0c;此时我们就可返回该对象的代理&#xff08;Proxy&#xff09;。

在这种设计方式下&#xff0c;系统会为某个对象提供一个代理对象&#xff0c;并由代理对象控制对源对象的引用。代理就是一个 Java 对象代表另一个 Java 对象来采取行动。在某些情况下&#xff0c;客户端代码不想或不能够直接调用被调用者&#xff0c;代理对象可以在客户和目标对象之间起到中介的作用。

对客户端而言&#xff0c;它不能分辨出代理对象与真实对象的区别&#xff0c;它也无须分辨代理对象和真实对象的区别。客户端代码并不知道真正的被代理对象&#xff0c;客户端代码面向接口编程&#xff0c;它仅仅持有一个被代理对象的接口。

总而言之&#xff0c;只要客户端代码不能或不想直接访问被调用对象——这种情况有很多原因&#xff0c;比如需要创建一个系统开销很大的对象&#xff0c;或者被调用对象在远程主机上&#xff0c;或者目标对象的功能还不足以满足需求……&#xff0c;而是额外创建一个代理对象返回给客户端使用&#xff0c;那么这种设计方式就是代理模式。

下面示范一个简单的代理模式&#xff0c;程序首先提供了一个 Image 接口&#xff0c;代表大图片对象所实现的接口&#xff0c;该接口代码如下&#xff1a;


清单 3. Image.java

Displaycode代码  收藏代码
  1.                
  2. public interface Image   
  3. {   
  4. void show();   
  5. }   

 

该接口提供了一个实现类&#xff0c;该实现类模拟了一个大图片对象&#xff0c;该实现类的构造器使用 Thread.sleep() 方法来暂停 3s。下面是该 BigImage 的程序代码。


清单 4. BigImage.java

Displaycode代码  收藏代码
  1.                
  2. // 使用该 BigImage 模拟一个很大图片  
  3. public class BigImage implements Image   
  4. {   
  5. public BigImage()   
  6. {   
  7. try   
  8. {   
  9. // 程序暂停 3s 模式模拟系统开销   
  10.              Thread.sleep(3000);   
  11. System.out.println("图片装载成功 ...");   
  12. }   
  13. catch (InterruptedException ex)   
  14. {   
  15. ex.printStackTrace();   
  16. }   
  17. }   
  18. // 实现 Image 里的 show() 方法  
  19. public void show()   
  20. {   
  21. System.out.println("绘制实际的大图片");   
  22. }   
  23. }   

 

上面的程序代码暂停了 3s&#xff0c;这表明创建一个 BigImage 对象需要 3s 的时间开销——程序使用这种延迟来模拟装载此图片所导致的系统开销。如果不采用代理模式&#xff0c;当程序中创建 BigImage 时&#xff0c;系统将会产生 3s 的延迟。为了避免这种延迟&#xff0c;程序为 BigImage 对象提供一个代理对象&#xff0c;BigImage 类的代理类如下所示。


清单 5. ImageProxy.java

Displaycode代码  收藏代码
  1.                
  2. public class ImageProxy implements Image   
  3. {   
  4. // 组合一个 image 实例&#xff0c;作为被代理的对象  
  5. private Image image;   
  6. // 使用抽象实体来初始化代理对象  
  7. public ImageProxy(Image image)   
  8. {   
  9. this.image &#61; image;   
  10. }   
  11. /**   
  12. * 重写 Image 接口的 show() 方法  
  13. * 该方法用于控制对被代理对象的访问&#xff0c;  
  14. * 并根据需要负责创建和删除被代理对象  
  15. */   
  16. public void show()   
  17. {   
  18. // 只有当真正需要调用 image 的 show 方法时才创建被代理对象  
  19. if (image &#61;&#61; null)   
  20. {   
  21.  image &#61; new BigImage();   
  22.  }   
  23. image.show();   
  24. }   
  25. }   

 

上面的 ImageProxy 代理类实现了与 BigImage 相同的 show() 方法&#xff0c;这使得客户端代码获取到该代理对象之后&#xff0c;可以将该代理对象当成 BigImage 来使用。

在 ImageProxy 类的 show() 方法中增加了控制逻辑&#xff0c;这段控制逻辑用于控制当系统真正调用 image 的 show() 时&#xff0c;才会真正创建被代理的 BigImage 对象。下面程序需要使用 BigImage 对象&#xff0c;但程序并不是直接返回 BigImage 实例&#xff0c;而是先返回 BigImage 的代理对象&#xff0c;如下面程序所示。


清单 6. BigImageTest.java

Displaycode代码  收藏代码
  1.                
  2. public class BigImageTest   
  3. {   
  4. public static void main(String[] args)   
  5. {   
  6. long start &#61; System.currentTimeMillis();   
  7. // 程序返回一个 Image 对象&#xff0c;该对象只是 BigImage 的代理对象  
  8. Image image &#61; new ImageProxy(null);   
  9. System.out.println("系统得到 Image 对象的时间开销 :" &#43;   
  10. (System.currentTimeMillis() - start));   
  11. // 只有当实际调用 image 代理的 show() 方法时&#xff0c;程序才会真正创建被代理对象。  
  12. image.show();   
  13. }   
  14. }   

 

上面程序初始化 image 非常快&#xff0c;因为程序并未真正创建 BigImage 对象&#xff0c;只是得到了 ImageProxy 代理对象——直到程序调用 image.show() 方法时&#xff0c;程序需要真正调用 BigImage 对象的 show() 方法&#xff0c;程序此时才真正创建 BigImage 对象。运行上面程序&#xff0c;看到如图 6 所示的结果。


图 6. 使用代理模式提高性能
图 6. 使用代理模式提高性能 

看到如图 6 所示的运行结果&#xff0c;读者应该能认同&#xff1a;使用代理模式提高了获取 Image 对象的系统性能。但可能有读者会提出疑问&#xff1a;程序调用 ImageProxy 对象的 show() 方法时一样需要创建 BigImage 对象啊&#xff0c;系统开销并未真正减少啊&#xff1f;只是这种系统开销延迟了而已啊&#xff1f;

我们可以从如下两个角度来回答这个问题&#xff1a;

  • 把创建 BigImage 推迟到真正需要它时才创建&#xff0c;这样能保证前面程序运行的流畅性&#xff0c;而且能减少 BigImage 在内存中的存活时间&#xff0c;从宏观上节省了系统的内存开销。
  • 有些情况下&#xff0c;也许程序永远不会真正调用 ImageProxy 对象的 show() 方法——意味着系统根本无须创建 BigImage 对象。在这种情形下&#xff0c;使用代理模式可以显著地提高系统运行性能。

与此完全类似的是&#xff0c;Hibernate 也是通过代理模式来“推迟”加载关联实体的时间&#xff0c;如果程序并不需要访问关联实体&#xff0c;那程序就不会去抓取关联实体了&#xff0c;这样既可以节省系统的内存开销&#xff0c;也可以缩短 Hibernate 加载实体的时间。

回页首

小结

Hibernate 的延迟加载&#xff08;lazy load&#xff09;本质上就是代理模式的应用&#xff0c;我们在过去的岁月里就经常通过代理模式来降低系统的内存开销、提升应用的运行性能。Hibernate 充分利用了代理模式的这种优势&#xff0c;并结合了 Javassist 或 CGLIB 来动态地生成代理对象&#xff0c;这更加增加了代理模式的灵活性&#xff0c;Hibernate 给这种用法一个新名称&#xff1a;延迟加载。无论怎样&#xff0c;充分分析、了解这些开源框架的实现可以更好的感受经典设计模式的优势所在。

转:https://www.cnblogs.com/zhangshitong/p/5306442.html



推荐阅读
  • 深入解析Gradle中的Project核心组件
    在Gradle构建系统中,`Project` 是一个核心组件,扮演着至关重要的角色。通过使用 `./gradlew projects` 命令,可以清晰地列出当前项目结构中包含的所有子项目,这有助于开发者更好地理解和管理复杂的多模块项目。此外,`Project` 对象还提供了丰富的配置选项和生命周期管理功能,使得构建过程更加灵活高效。 ... [详细]
  • 分布式开源任务调度框架 TBSchedule 深度解析与应用实践
    本文深入解析了分布式开源任务调度框架 TBSchedule 的核心原理与应用场景,并通过实际案例详细介绍了其部署与使用方法。首先,从源码下载开始,详细阐述了 TBSchedule 的安装步骤和配置要点。接着,探讨了该框架在大规模分布式环境中的性能优化策略,以及如何通过灵活的任务调度机制提升系统效率。最后,结合具体实例,展示了 TBSchedule 在实际项目中的应用效果,为开发者提供了宝贵的实践经验。 ... [详细]
  • SQLite数据库CRUD操作实例分析与应用
    本文通过分析和实例演示了SQLite数据库中的CRUD(创建、读取、更新和删除)操作,详细介绍了如何在Java环境中使用Person实体类进行数据库操作。文章首先阐述了SQLite数据库的基本概念及其在移动应用开发中的重要性,然后通过具体的代码示例,逐步展示了如何实现对Person实体类的增删改查功能。此外,还讨论了常见错误及其解决方法,为开发者提供了实用的参考和指导。 ... [详细]
  • 本文深入探讨了CGLIB BeanCopier在Bean对象复制中的应用及其优化技巧。相较于Spring的BeanUtils和Apache的BeanUtils,CGLIB BeanCopier在性能上具有显著优势。通过详细分析其内部机制和使用场景,本文提供了多种优化方法,帮助开发者在实际项目中更高效地利用这一工具。此外,文章还讨论了CGLIB BeanCopier在复杂对象结构和大规模数据处理中的表现,为读者提供了实用的参考和建议。 ... [详细]
  • 在处理遗留数据库的映射时,反向工程是一个重要的初始步骤。由于实体模式已经在数据库系统中存在,Hibernate 提供了自动化工具来简化这一过程,帮助开发人员快速生成持久化类和映射文件。通过反向工程,可以显著提高开发效率并减少手动配置的错误。此外,该工具还支持对现有数据库结构进行分析,自动生成符合 Hibernate 规范的配置文件,从而加速项目的启动和开发周期。 ... [详细]
  • 在尝试为 Unity 编译一个简单的 Java 库时,运行 `ant jar` 命令后遇到了 Java I/O 异常。具体错误信息为“无法启动程序 ${aAPT},错误代码 2”,这通常表示指定的文件或目录不存在。此问题可能是由于环境配置不正确或路径设置有误导致的。建议检查相关路径和环境变量,确保所有依赖项都已正确安装和配置。 ... [详细]
  • 本文探讨了利用Java实现WebSocket实时消息推送技术的方法。与传统的轮询、长连接或短连接等方案相比,WebSocket提供了一种更为高效和低延迟的双向通信机制。通过建立持久连接,服务器能够主动向客户端推送数据,从而实现真正的实时消息传递。此外,本文还介绍了WebSocket在实际应用中的优势和应用场景,并提供了详细的实现步骤和技术细节。 ... [详细]
  • 在搭建Hadoop集群以处理大规模数据存储和频繁读取需求的过程中,经常会遇到各种配置难题。本文总结了作者在实际部署中遇到的典型问题,并提供了详细的解决方案,帮助读者避免常见的配置陷阱。通过这些经验分享,希望读者能够更加顺利地完成Hadoop集群的搭建和配置。 ... [详细]
  • Hadoop 2.6 主要由 HDFS 和 YARN 两大部分组成,其中 YARN 包含了运行在 ResourceManager 的 JVM 中的组件以及在 NodeManager 中运行的部分。本文深入探讨了 Hadoop 2.6 日志文件的解析方法,并详细介绍了 MapReduce 日志管理的最佳实践,旨在帮助用户更好地理解和优化日志处理流程,提高系统运维效率。 ... [详细]
  • 如何在Spark数据排序过程中有效避免内存溢出(OOM)问题
    本文深入探讨了在使用Spark进行数据排序时如何有效预防内存溢出(OOM)问题。通过具体的代码示例,详细阐述了优化策略和技术手段,为读者在实际工作中遇到类似问题提供了宝贵的参考和指导。 ... [详细]
  • Java中高级工程师面试必备:JVM核心知识点全面解析
    对于软件开发人员而言,随着技术框架的不断演进和成熟,许多高级功能已经被高度封装,使得初级开发者只需掌握基本用法即可迅速完成项目。然而,对于中高级工程师而言,深入了解Java虚拟机(JVM)的核心知识点是必不可少的。这不仅有助于优化性能和解决复杂问题,还能在面试中脱颖而出。本文将全面解析JVM的关键概念和技术细节,帮助读者全面提升技术水平。 ... [详细]
  • 本文介绍了UUID(通用唯一标识符)的概念及其在JavaScript中生成Java兼容UUID的代码实现与优化技巧。UUID是一个128位的唯一标识符,广泛应用于分布式系统中以确保唯一性。文章详细探讨了如何利用JavaScript生成符合Java标准的UUID,并提供了多种优化方法,以提高生成效率和兼容性。 ... [详细]
  • 在将旧版库中的 JUnit 包升级到 4.12 版本后,进行测试时发现原本应顺利通过的测试用例却失败了。通过检查报告中的详细信息,我们发现了一些初始化错误。本文将深入分析这些错误的原因,并提供有效的解决方案,以确保测试的稳定性和可靠性。 ... [详细]
  • 开发笔记:深入解析Android自定义控件——Button的72种变形技巧
    开发笔记:深入解析Android自定义控件——Button的72种变形技巧 ... [详细]
  • Eclipse JFace Text框架中IDocument接口的getNumberOfLines方法详解与编程实例 ... [详细]
author-avatar
半宅半学
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有