hibernate之xml映射文件关系维护,懒加载,级联

简介:     一:关系维护    --->inverse默认值false,表示不放弃关系的维护。     --->inverse="true"配置在那一端,表示那一端xml对应的po放弃关系的维护(交由hibernate内部进行维护),由另一端进行关系维护。

 

 

 

一:关系维护

   --->inverse默认值false,表示不放弃关系的维护。  

  --->inverse="true"配置在那一端,表示那一端xml对应的po放弃关系的维护(交由hibernate内部进行维护),由另一端进行关系维护。

 例子:学生班级模型【多对一模型】一端放弃关系的维护,由学生端进行关系维护             

    ----->班级的xml映射文件中<set name="students" class="Student" inverse="true">          

   学生课程模型【多对多模型】谁修改的频繁,谁放弃关系的维护。综合考量。学生端放弃关系的维护     

    ----->学生的xml映射文件中<set name="courses" table="student_course" inverse="true" >

 

 

   [1]放弃关系的维护方,在代码中无法建立关系,无法断掉关系。  

   [2]关系的维护方,无论更新,新建,删除,都要在代码中体现关系的维护。

   [3]如果映射到数据库表中自己属性id的主键,在别的表里是外键,在删除和更新操作过程中,需要代码进行断开和更新操作,来防止关系的丢失和关系阻碍执行(爆出异常)。

  [4]如果没有设置级联操作(一般不会设置级联操作),当删除某个对象时,对象中的某个属性(非主键性属性)映射表中的字段是另一张表的主键,删除操作无需考虑关系(不用去代码操作断开),直接可以删除该对象。sql同样。
  

 

二:懒加载lazy  

   --->懒加载的从属对象,在js代码中不能触发代理对象,去数据库查询真实对象。

    --->在Web应用程序中,系统的瓶颈常在于系统的响应速度。         

    --->Web应用程序做的最多就是和后台数据库交互,而查询数据库是种非常耗时的过程。当数据库里记录过多时,查询优化更显得尤为重要。为了解决这种问题,有人提出了缓存的概念,缓存就是将用户频繁使用的数据放在内存中以便快速访问。在用户执行一次查询操作后,查询的记录会放在缓存中。当用户再次查询时,系统会首先从缓存中读取,如果缓存中没有,再查询数据库。缓存技术在一定程度上提升了系统性能,但是当数据量过大时,缓存就不太合适了。因为内存容量有限,把过多的数据放在内存中,会影响电脑性能。而另一种技术,懒加载可以解决这种问题。

     --->延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载。

 

load-->宿主对象,存在懒加载问题。session未关闭前,代理宿主对象,有效。关闭后,无效。  

get--->不存在懒加载问题。直接从数据库把数据加载到内存中。宿主和从属对象都包含。

  【不同点】:get方法查询,不存在懒加载,如果id对应的记录不存在,则返回null           

       load方法存在懒加载问题,如果id对应的记录不存在,则抛出异常。

 【共同点】:先从一级缓存和二级缓存中查找,如果找不到,才去数据库中查找

 

     load-->宿主对象懒加载。  

    lazy属性在po.hbm.xml的关联关系配置中--->对宿主的从属对象进行懒加载。session未关闭前,从属代理对象有效,可取出数据。session关闭后,从属对象失效。若取数据,报错。

 

 

所谓懒加载(lazy)就是延时加载,延迟加载。 什么时候用懒加载呢,我只能回答要用懒加载的时候就用懒加载。

至于为什么要用懒加载呢,就是当我们要访问的数据量过大时,明显用缓存不太合适,

因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,

我们让数据在需要的时候才进行加载,这时我们就用到了懒加载。

比如部门ENTITY和员工ENTITY,部门与员工1对多,如果lazy设置为 false,那么只要加载了一个部门的po,就会根据一对多配置的关系把所有员工的po也加载出来。但是实际上有时候只是需要用到部门的信息,不需要用到 员工的信息,这时员工po的加载就等于浪费资源。如果lazy设置为true,那么只有当你访问部门po的员工信息时候才回去加载员工的po的信息。

 

hibernate3.0中lazy有三个值,true,false,proxy,默认的是lazy="proxy". 具体设置成什么要看你的需求,并不是说哪个设置就是最好的。 假如在student对象中包含一个head对象 如果你确定在用student对象的时候就要用到head对象里的属性,那你就设置立即加载,因为设置立即加载那么在查询student的同时就会查询 student的head,hibernate就会在查询的时候关联两张表从而生成的sql就可能只有一条。而如果你设置的是延迟加载,那么肯定会要生成 1+N条sql语句:其中“1”是查询student的语句,“N”是根据N个student的id去查询head的N条语句。而且,延迟加载是要用到的 时候才去执行查询,这样系统判断那里需要加载,那里不需要加载也需要时间,性能上肯定就不如立即加载了! 如果,你是有的地方需要用到student的时候才用到head属性,那么你就设置成延迟加载,因为查询2张表的数据肯定要比查询1张表的数据消耗大。 到低要怎样设置就要看你的实际需求了

延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载。

 

A、实体对象的延迟加载

 

如果想对实体对象使用延迟加载,必须要在实体的映射配置文件中进行相应的配置,如下所示:

       <hibernate-mapping>  

        <class name=”com.neusoft.entity.User” table=”user” lazy=”true”>     ……  </class>

      </hibernate-mapping>      

  通过将class的lazy属性设置为true,来开启实体的延迟加载特性。

    如果我们运行下面的代码:     

      User user=(User)session.load(User.class,”1”);(1)     

      System.out.println(user.getName());(2)   

      当运行到(1)处时,Hibernate并没有发起对数据的查询,如果此时通过一些调试工具,观察此时user对象的内存快照,会惊奇的发现,此时返 回的可能是User$EnhancerByCGLIB$$bede8986类型的对象,而且其属性为null,这是怎么回 事?session.load()方法会返回实体对象的代理类对象,这里所返回的对象类型就是User对象的代理类对象。在Hibernate中通过使用 CGLIB,来实现动态构造一个目标对象的代理类对象,并且在代理类对象中包含目标对象的所有属性和方法,而且所有属性均被赋值为null。通过调试器显 示的内存快照,可以看出此时真正的User对象,是包含在代理对象的CGLIB$CALBACK_0.target属性中,当代码运行到(2)处时,此时 调用user.getName()方法,这时通过CGLIB赋予的回调机制,实际上调用CGLIB$CALBACK_0.getName()方法,当调用 该方法时,Hibernate会首先检查CGLIB$CALBACK_0.target属性是否为null,如果不为空,则调用目标对象的getName 方法,如果为空,则会发起数据库查询,生成类似这样的SQL语句:select * from user where id=’1’;来查询数据,并构造目标对象,并且将它赋值到CGLIB$CALBACK_0.target属性中。   这样,通过一个中间代理对象,Hibernate实现了实体的延迟加载,只有当用户真正发起获得实体对象属性的动作时,才真正会发起数据库查询操作。 所以实体的延迟加载是用通过中间代理类完成的,所以只有session.load()方法才会利用实体延迟加载,因为只有session.load()方 法才会返回实体类的代理类对象。

 

  B、集合类型的延迟加载   

 

    在Hibernate的延迟加载机制中,针对集合类型的应用,意义是最为重大的,因为这有可能使性能得到大幅度的提 高,为此Hibernate进行了大量的努力,其中包括对JDK Collection的独立实现,在一对多关联中,定义的用来容纳关联对象的Set集合,并不是java.util.Set类型或其子类型,而是 net.sf.hibernate.collection.Set类型,通过使用自定义集合类的实现,Hibernate实现了集合类型的延迟加载。

 

    为了 对集合类型使用延迟加载,必须如下配置实体类的关于关联的部分:

        <hibernate-mapping>   

               <class name=”com.neusoft.entity.User” table=”user”>      ……    

                  <set name=”addresses” table=”address” lazy=”true” inverse=”true”>     

                      <key column=”user_id”/>      

                         <one-to-many class=”com.neusoft.entity.Arrderss”/>   

                  </set>   

              </class>

        </hibernate-mapping>   

 

     通过将<set>元素的lazy属性设置为true来开启集合类型的延迟加载特性。看下面的代码:  

        User user=(User)session.load(User.class,”1”);  

        Collection addset=user.getAddresses();      (1)  

        Iterator it=addset.iterator();               (2)  

        while(it.hasNext()) {   

            Address address=(Address)it.next();   

            System.out.println(address.getAddress());  }      

    当程序执行到(1)处时,并不会发起对关联数据的查询来加载关联数据,只有运行到(2)处时,真正的数据读取操作才会开始,这时Hibernate会根据缓存中符合条件的数据索引,来查找符合条件的实体对象。         这里引入了一个全新的概念——数据索引,下面首先将说明什么是数据索引。在Hibernate中对集合类型进行缓存时,是分两部分进行缓存的,首先缓存 集合中所有实体的id列表,然后缓存实体对象,这些实体对象的id列表,就是所谓的数据索引。当查找数据索引时,如果没有找到对应的数据索引,这时就会一 条select SQL的执行,获得符合条件的数据,并构造实体对象集合和数据索引,然后返回实体对象的集合,并且将实体对象和数据索引纳入Hibernate的缓存之 中。另一方面,如果找到对应的数据索引,则从数据索引中取出id列表,然后根据id在缓存中查找对应的实体,如果找到就从缓存中返回,如果没有找到,在发 起select SQL查询。在这里我们看出了另外一个问题,这个问题可能会对性能产生影响,这就是集合类型的缓存策略。

 

    如果如下配置集合类型:

     <hibernate-mapping>   

      <class name=”com.neusoft.entity.User” table=”user”>   

          …    

        <set name=”addresses” table=”address” lazy=”true” inverse=”true”>       

          <cache usage=”read-only”/>       

          <key column=”user_id”/>       

          <one-to-many class=”com.neusoft.entity.Arrderss”/>    

        </set>  

        </class>

    </hibernate-mapping>    

    这里应用了<cache usage=”read-only”/>配置,如果采用这种策略来配置集合类型,Hibernate将只会对数据索引进行缓存,而不会对集合中的实体对象进行缓存。如上配置运行下面的代码:  

        User user=(User)session.load(User.class,”1”);  

        Collection addset=user.getAddresses();    

          Iterator it=addset.iterator();             

         while(it.hasNext()) {   

            Address address=(Address)it.next();   

            System.out.println(address.getAddress());  

          }

           System.out.println(“Second query……”);  

           User user2=(User)session.load(User.class,”1”);  

           Collection it2=user2.getAddresses();

           while(it2.hasNext()) {   

                Address address2=(Address)it2.next();  

                System.out.println(address2.getAddress());

           }

      运行这段代码,会得到类似下面的输出:

     Select * from user where id=’1’;    

      Select * from address where user_id=’1’;    Tianjin    Dalian    Second query……    

      Select * from address where id=’1’;    

      Select * from address where id=’2’;    Tianjin    Dalian     

    可以看到,当第二次执行查询时,执行了两条对address表的查询操作,为什么会这样呢?这是因为当第一次加载实体后,根据集合类型缓存策略的配 置,只对集合数据索引进行了缓存,而并没有对集合中的实体对象进行缓存,所以在第二次再次加载实体时,Hibernate找到了对应实体的数据索引,但是 根据数据索引,却无法在缓存中找到对应的实体,所以Hibernate根据找到的数据索引发起了两条select SQL的查询操作,这里造成了对性能的浪费,怎样才能避免这种情况呢?必须对集合类型中的实体也指定缓存策略,对集合类型进行配置:

      <hibernate-mapping>   

          <class name=”com.neusoft.entity.User” table=”user”>    

             ……      

              <set name=”addresses” table=”address” lazy=”true” inverse=”true”>        

                  <cache usage=”read-write”/>        

                  <key column=”user_id”/>     

                  <one-to-many class=”com.neusoft.entity.Arrderss”/>   

               </set>  

           </class>

      </hibernate-mapping>  

     此时Hibernate会对集合类型中的实体也进行缓存,再次运行上面的代码,将会得到类似如下的输出:    

      Select * from user where id=’1’;

      Select * from address where user_id=’1’;    Tianjin    Dalian    Second query……    Tianjin    Dalian   

    这时将不会再有根据数据索引进行查询的SQL语句,因为此时可以直接从缓存中获得集合类型中存放的实体对象。

 

C、属性延迟加载      

  在Hibernate3中,引入了一种新的特性——属性的延迟加载,这个机制又为获取高性能查询提供了有力的工具。在大数据对象读取时,假设在User 对象中有一个resume字段,该字段是一个java.sql.Clob类型,包含了用户的简历信息,当加载该对象时,不得不每一次都要加载这个字段,而 不论是否真的需要它,而且这种大数据对象的读取本身会带来很大的性能开销。在Hibernate2中,只有通过面向性能的粒度细分,来分解User类,来 解决这个问题,但是在Hibernate3中,可以通过属性延迟加载机制,来使我们获得只有当我们真正需要操作这个字段时,才去读取这个字段数据的能力, 为此必须如下配置实体类:

   <hibernate-mapping>   

      <class name=”com.neusoft.entity.User” table=”user”>  

           ……   

          <property name=”resume” type=”java.sql.Clob” column=”resume” lazy=”true”/>   

      </class>  

    </hibernate-mapping>      

  通过对<property>元素的lazy属性设置true来开启属性的延迟加载,在Hibernate3中为了实现属性的延迟加载,使用 了类增强器来对实体类的Class文件进行强化处理,通过增强器的增强,将CGLIB的回调机制逻辑,加入实体类,这里我们可以看出属性的延迟加载,还是 通过CGLIB来实现的。CGLIB是Apache的一个开源工程,这个类库可以操纵java类的字节码,根据字节码来动态构造符合要求的类对象。根据上 面的配置我们运行下面的代码:

      String sql=”from User user where user.name=’zx’ ”;  

      Query query=session.createQuery(sql);   (1)  

      List list=query.list();  

      for(int i=0;i<list.size();i++) {   

        User user=(User)list.get(i);

         System.out.println(user.getName());

         System.out.println(user.getResume());   (2)

     }   

    当执行到(1)处时,会生成类似如下的SQL语句:  Select id,age,name from user where name=’zx’;   

        这时Hibernate会检索User实体中所有非延迟加载属性对应的字段数据,

    当执行到(2)处时,会生成类似如下的SQL语句:  Select resume from user where id=’1’;

        这时会发起对resume字段数据真正的读取操作。

 

 

三:级联cascade

(1)多表crud过程,简化操作

(2)操作一个宿主对象时,则将该对象的从属对象进行同样的操作。

(3)级联,在宿主上进行配置cascade属性。在哪端配置,哪端的对象就是宿主。

(4)在实际开发过程中,一般不用级联配置,因为可控性差。对程序员要求比较高。

 

◦ 级联操作。操作一个对象时将该对象相关属性对象也进行同样操作。

  ◦ all:进行任何操作都级联。

  ◦ save-update:保存和更新操作时

  ◦ delete:删除操作时级联

  ◦ all-delete-orpnan:当被关联对象失去宿主时,将其级联删除。

  ◦none(默认) :不级联

四:对象抓取策略fetch

 

    fetch参数指定了关联对象抓取的方式是select查询还是join查询,select方式时先查询返回要查询的主体对象(列表),再根据关联外键id,每一个对象发一个select查询,获取关联的对象,形成n+1次查询(会产生两条以上的sql语句)
而join方式,主体对象和关联对象用一句外键关联的sql同时查询出来,不会形成多次查询。
如果你的关联对象是延迟加载的,它当然不会去查询关联对象。
另外,在hql查询中配置文件中设置的join方式是不起作用的(而在所有其他查询方式如get、criteria或再关联获取等等都是有效的),会使用select方式,除非你在hql中指定join fetch某个关联对象。

 

 

 

fetch策略用于定义 get/load一个对象时,如何获取非lazy的对象/集合。 这些参数在Query中无效(相当于小型非懒加载)。

 

fetch策略用于定义 get/load一个对象时,如何获取非lazy的对象/集合。 这些参数在Query中无效(相当于小型非懒加载)。

在某种特殊的场合下,fetch在hql中还是起作用的。

 

    例子:学生班级模型

      在Student.hbm.xml中

<many-to-one name="classRoom" class="ClassRoom" fetch="join">
            <column name="classRoom_id"></column>

</many-to-one>

      查询指定id的学生时,hibernate产生的sql:

   select
        student0_.id as id1_1_,
        student0_.name as name1_1_,
        student0_.sex as sex1_1_,
        student0_.classRoom_id as classRoom4_1_1_,
        classroom1_.id as id0_0_,
        classroom1_.name as name0_0_
    from
        shang_student student0_
    left outer join
        shang_classRoom classroom1_
            on student0_.classRoom_id=classroom1_.id
    where
        student0_.id=?

 

  在ClassRoom.hbm.xml中

<set name="students" inverse="true" fetch="join">
            <key column="classRoom_id"></key>
            <one-to-many class="Student"/>
</set>

  查询指定id的班级时,hibernate产生的sql:

select
        classroom0_.id as id0_1_,
        classroom0_.name as name0_1_,
        students1_.classRoom_id as classRoom4_3_,
        students1_.id as id3_,
        students1_.id as id1_0_,
        students1_.name as name1_0_,
        students1_.sex as sex1_0_,
        students1_.classRoom_id as classRoom4_1_0_
    from
        shang_classRoom classroom0_
    left outer join
        shang_student students1_
            on classroom0_.id=students1_.classRoom_id
    where
        classroom0_.id=?


 

 

 


相关文章
|
2月前
|
XML 前端开发 Java
讲解SSM的xml文件
本文详细介绍了SSM框架中的xml配置文件,包括springMVC.xml和applicationContext.xml,涉及组件扫描、数据源配置、事务管理、MyBatis集成以及Spring MVC的视图解析器配置。
70 1
|
28天前
|
XML Android开发 数据格式
Eclipse 创建 XML 文件
Eclipse 创建 XML 文件
25 2
|
1月前
|
Java Maven
maven项目的pom.xml文件常用标签使用介绍
第四届人文,智慧教育与服务管理国际学术会议(HWESM 2025) 2025 4th International Conference on Humanities, Wisdom Education and Service Management
112 8
|
2月前
|
XML JavaScript Java
java与XML文件的读写
java与XML文件的读写
28 3
|
2月前
|
XML 存储 缓存
C#使用XML文件的详解及示例
C#使用XML文件的详解及示例
110 0
|
2月前
|
XML 存储 Web App开发
查看 XML 文件
查看 XML 文件
|
3月前
|
Java 数据库连接 API
解锁高效开发秘籍:深入探究 Hibernate 如何优雅处理一对多与多对多关系,让数据映射再无烦恼!
【9月更文挑战第3天】Hibernate 是 Java 领域中最流行的 ORM 框架之一,广泛用于处理实体对象与数据库表之间的映射。尤其在处理复杂关系如一对多和多对多时,Hibernate 提供了丰富的 API 和配置选项。本文通过具体代码示例,展示如何使用 `@OneToMany`、`@JoinColumn`、`@ManyToMany` 和 `@JoinTable` 等注解优雅地实现这些关系,帮助开发者保持代码简洁的同时确保数据一致性。
59 4
|
3月前
|
SQL XML Java
mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法
当然,这些仅是MyBatis功能的初步介绍。MyBatis还提供了高级特性,如动态SQL、类型处理器、插件等,可以进一步提供对数据库交互的强大支持和灵活性。希望上述内容对您理解MyBatis的基本操作有所帮助。在实际使用中,您可能还需要根据具体的业务要求调整和优化SQL语句和配置。
64 1
|
4月前
|
Java 数据库连接 数据库
AI 时代风起云涌,Hibernate 实体映射引领数据库高效之路,最佳实践与陷阱全解析!
【8月更文挑战第31天】Hibernate 是一款强大的 Java 持久化框架,可将 Java 对象映射到关系数据库表中。本文通过代码示例详细介绍了 Hibernate 实体映射的最佳实践,包括合理使用关联映射(如 `@OneToMany` 和 `@ManyToOne`)以及正确处理继承关系(如单表继承)。此外,还探讨了常见陷阱,例如循环依赖可能导致的无限递归问题,并提供了使用 `@JsonIgnore` 等注解来避免此类问题的方法。通过遵循这些最佳实践,可以显著提升开发效率和数据库操作性能。
89 0
|
4月前
|
数据库 开发者 Java
Hibernate映射注解的魔力:实体类配置的革命,让你的代码量瞬间蒸发!
【8月更文挑战第31天】Hibernate 是一款出色的对象关系映射框架,简化了 Java 应用与数据库的交互。其映射注解让实体类配置变得直观简洁。本文深入剖析核心概念与使用技巧,通过示例展示如何简化配置。
53 0