首页 > 代码库 > 关于延迟加载(lazy)和强制加载(Hibernate.initialize(Object proxy) )
关于延迟加载(lazy)和强制加载(Hibernate.initialize(Object proxy) )
PO 即Persistence Object
VO 即Value Object
PO 和VO 是Hibernate 中号码大全两个比较要害的概念。
首要,何谓VO,很简略,VO 即是一个简略的值方针。
如:
TUser user = new TUser();
user.setName("Emma");
这儿的user 即是一个VO。VO 仅仅简略关键词挖掘工具携带了方针的一些特点信息。
何谓PO? 即归入Hibernate 办理结构中的VO。看下面两个比如:
?
TUser user = new TUser();
TUser anotherUser = new TUser();
user.setName("Emma");
anotherUser.setName("Kevin");
//此刻user和anotherUser都是VO
Transaction tx = session.beginTransaction();
session.save(user);
//此刻的user现已经过Hibernate的处置,成为一个PO ,而anotherUser依然是个VO
tx.commit();
//业务提交以后,库表中现已刺进一条用户”Emma”的记载,关于anotherUser则无任何操作
Transaction tx = session.beginTransaction();
user.setName("Emma_1"); //PO
anotherUser.setName("Kevin_1");//VO
tx.commit();
//业务提交以后,PO的状况被固化到数据库中,也即是说数据库中“Emma”的用户记载现已被更新为“Emma_1”,此刻anotherUser依然是个通常Java方针,它的特点更改不会对数据库产生任何影响,别的,经过Hibernate回来的方针也是PO: 由Hibernate回来的PO ,如:
TUser user = (TUser)session.load(TUser.class,new Integer(1));
VO经过Hibernate进行处置,就成为了PO。上面的示例代码session.save(user)中,咱们把一个VO “user”传递给Hibernate的Session.save办法进行保留。在save办法中,Hibernate对其进
行如下处置:
1. 在当时session所对应的实体容器(Entity Map)中查询是不是存在user方针的引证。
2. 假如引证存在,则直接回来user方针id,save进程完毕. Hibernate中,关于每个Session有一个实体容器(实践上是一个Map方针), 假如此容器中现已保留了方针方针的引证,那么hibernate 会以为此方针现已与Session相有关。
关于save操作而言,假如方针现已与Session相有关(即现已被参加Session 的实体容器中),则无需进行详细的操作。由于以后的Session.flush 进程中,Hibernate会对此实体容器中的方针进行遍历,查找出发作改变的实体,生成并履行相应的update句子。
3. 假如引证不存在,则依据映射联系,履行insert操作。
a) 在咱们这儿的示例中,采用了native的id生成机制,因而hibernate会从数据库取得insert操作生成的id并赋予user方针的id特点。
b) 将user方针的引证归入Hibernate的实体容器。
c) save 进程完毕,回来方针id.
而Session.load办法中,再回来方针之前,Hibernate 就现已将此方针归入其实体容器中。
VO和PO的首要差异在于:
. VO是独立的Java Object。
. PO是由Hibernate归入其实体容器(EntityMap)的方针,它代表了与数
据库中某条记载对应的Hibernate实体,PO的改变在业务提交时将反应到实
际数据库中。假如一个PO与Session对应的实体容器中别离(如Session 封闭后的PO),那么
此刻,它又会成为一个VO。
关于unsaved-value
在非显现数据保留时,Hibernate 将依据这个值来判别方针是不是需求保留。
所谓显式保留,是指代码中清晰调用session 的save 、update 、saveOrupdate 办法对方针进行持久化。如:
session.save(user);
而在某些情况下,如映射联系中,Hibernate 依据级联(Cascade )联系对联接类进行保留。此刻代码中没有关于级联方针的显现保留句子,需求Hibernate 依据方针当时状
态判别是不是需求保留到数据库。此刻,Hibernate 行将依据unsaved-value 进行断定。
首要Hibernate 会取出方针方针的id。以后,将此值与unsaved-value 进行比对,假如相等,则以为方针方针没有保留,不然,以为方针现已保留,无需再进行保留操作。
如:user 方针是之前由hibernate 从数据库中获取,一起,此user 方针的若干个有关方针address 也被加载,此刻咱们向user 方针新增一个address 方针,此刻调用
session.save(user),hibernate 会依据unsaved-value 判别user 方针的数个address
有关方针中,哪些需求履行save 操作,而哪些不需求。
关于咱们新参加的address 方针而言,由于其id(Integer 型)没有赋值,因而为null,与咱们设定的unsaved-value(null )相同,因而hibernate 将其视为一个未保留方针,将为其生成insert 句子并履行。
这儿能够会产生一个疑问,假如“原有”有关方针发作改变(如user 的某个“原有” 的address 方针的特点发作了改变,所谓“原有”即此address 方针现已与user 相有关,而不是咱们在此进程中为之新增的),此刻id 值是从数据库中读出,并没有发作改变,天然
与unsaved-value(null)也不一样,那么Hibernate 是不是就不保留了?
上面关于PO、VO 的评论中从前涉及到数据保留的疑问,实践上,这儿的“保留”, 实践上是“insert”的概念,仅仅关于新有关方针的参加,而非数据库华夏有有关方针的
“update”。所谓新有关方针,通常情况下能够理解为未与Session 发作有关的VO。而“原有”有关方针,则是PO。:
关于save操作而言,假如方针现已与Session相有关(即现已被参加Session的实体容器中),则无需进行详细的操作。由于以后的Session.flush 进程中,Hibernate
会对此实体容器中的方针进行遍历,查找出发作改变的实体,生成并履行相应的update 句子。
Inverse和Cascade
Inverse,直译为“回转”。在Hibernate语义中,Inverse 指定了相相联系中的方向。相相联系中,inverse=”false”的为主动方,由主动方担任保护相相联系。详细可参见一对多联系中的描绘。
而Cascade,译为“级联”,标明方针的级联联系,如TUser 的Cascade设为all, 就标明假如发作对user方针的操作,需求对user所有关的方针也进行相同的操作。如对user方针履行save操作,则有必要对user方针相有关的address也履行save操作。
初学者常常混杂inverse和cascade,实践上,这是两个互不有关的概念。Inverse 指的是相相联系的操控方向,而cascade指的是层级之间的连锁操作。
推迟加载(Lazy Loading)
为了防止一些情况下,相相联系所带来的无谓的功用开支。Hibernate引入了推迟加载的概念。如,示例中user方针在加载的时分,会一起读取其所有关的多个地址(address)方针,
关于需求对address进行操作的应用逻辑而言,有关数据的主动加载机制确实非常有用。可是,假如咱们仅仅想要取得user的性别(sex)特点,而不关心user的地址(address)
信息,那么主动加载address的特性就显得剩余,而且造成了极大的功用浪费。为了取得user 的性别特点,咱们能够还要一起从数据库中读取数条无用的地址数据,这致使了很多无谓的体系开支。
推迟加载特性的呈现,恰是为了处理这个疑问。所谓推迟加载,即是在需求数据的时分,才真实履行数据加载操作。
关于咱们这儿的user方针的加载进程,也就意味着,加载user方针时只关于其自身的特点, 而当咱们需求获取user方针所有关的address信息时(如履行user.getAddresses时),才
真实从数据库中加载address数据并回来。
测验履行以下代码:
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
List userList = criteria.list();
- indexRead arguments from command-line "http://www.shoudashou.com"
- indexRead arguments from command-line "http://www.4lunwen.cn"
- indexRead arguments from command-line "http://www.zx1234.cn"
- indexRead arguments from command-line "http://www.penbar.cn"
- indexRead arguments from command-line "http://www.whathappy.cn"
- indexRead arguments from command-line "http://www.lunjin.net"
- indexRead arguments from command-line "http://www.ssstyle.cn"
- indexRead arguments from command-line "http://www.91fish.cn"
- indexRead arguments from command-line "http://www.fanselang.com"
TUser user =(TUser)userList.get(0);
System.out.println("User name => "+user.getName());
Set hset = user.getAddresses();
session.close();//封闭Session
TAddress addr = (TAddress)hset.toArray()[0];
System.out.println(addr.getAddress());
运行时抛出反常:
LazyInitializationException - Failed to lazily initialize a collection - no session or session was closed
假如咱们稍做调整,将session.close放在代码结尾,则不会发作这样的疑问。这意味着,只要咱们实践加载user 有关的address时,Hibernate 才企图经过
session从数据库中加载实践的数据集,而由于咱们读取address之前现已封闭了session,所以报出session已封闭的过错。
这儿有个疑问,假如咱们采用了推迟加载机制,但期望在一些情况下,完成非推迟加载时的功用,也即是说,咱们期望在Session封闭后,依然答应操作user的addresses
特点。如,为了向View层供给数据,咱们有必要供给一个完好的User方针,包含其所有关的address信息,而这个User方针有必要在Session封闭以后依然能够运用。
Hibernate.initialize办法能够经过强行加载有关方针完成这一功用:
Hibernate.initialize(user.getAddresses());
session.close();
//经过Hibernate.initialize办法强行读取数据
//addresses方针即可脱离session进行操作
Set hset= user.getAddresses();
TAddress addr = (TAddress)hset.toArray()[0];
System.out.println(addr.getAddress());
为了完成透明化的推迟加载机制,hibernate进行了很多努力。其中包含JDK Collection接口的独立完成。
假如咱们测验用HashSet强行转化Hibernate回来的Set 型方针:
Set hset = (HashSet)user.getAddresses();
就会在运行期得到一个java.lang.ClassCastException, 实践上,此刻回来的是一个Hibernate的特定Set完成“net.sf.hibernate.collection.Set”方针,而非
传统意义上的JDK Set完成。这也恰是咱们为何在编写POJO时,有必要用JDKCollection 接口(如Set,Map), 而非特定的JDKCollection 完成类(如HashSet、HashMap)申明Collection特点的缘由。