zoukankan      html  css  js  c++  java
  • 关于延迟加载(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(); 
    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属性的原因。

  • 相关阅读:
    CodeForces 659F Polycarp and Hay
    CodeForces 713C Sonya and Problem Wihtout a Legend
    CodeForces 712D Memory and Scores
    CodeForces 689E Mike and Geometry Problem
    CodeForces 675D Tree Construction
    CodeForces 671A Recycling Bottles
    CodeForces 667C Reberland Linguistics
    CodeForces 672D Robin Hood
    CodeForces 675E Trains and Statistic
    CodeForces 676D Theseus and labyrinth
  • 原文地址:https://www.cnblogs.com/writeLessDoMore/p/6732752.html
Copyright © 2011-2022 走看看