美文网首页
hibernate详解

hibernate详解

作者: annkee | 来源:发表于2018-07-10 11:00 被阅读0次

    介绍

    Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

    Hibernate映射(ORM对象关系映射)

    多和一的映射

    <!-- 员工表的映射配置 -->
    <hibernate-mapping package="cn.hpe.po" >
    <class name="Employee" table="t_employee">
    <!-- 主键 -->
    <id name="empId" column="empid" >
    <!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
    <generator class="sequence">
    <param name="sequence">seq_emp_empid</param>
    </generator>
    </id>
    <property name="empName" length="50"></property>
    <property name="salary" type="double"></property>
    <!-- 多对一 映射的另外一张t_dept表 ,dept_id 和 dept表的 映射的外键key标签的属性都是dept_id -->
    <many-to-one name="dept" class="Dept" column="dept_id" ></many-to-one>
    </class>
    </hibernate-mapping>
    
    
    <!-- 部门表的 映射配置 -->
    <hibernate-mapping package="cn.hpe.po" auto-import="true">
    <class name="Dept" table="t_dept">
    <!-- 主键 -->
    <id name="deptId" column="deptid" >
    <!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
    <generator class="sequence" >
    <param name="sequence">seq_dept_deptid</param>
    </generator>
    </id>
    <property name="deptName" length="50"></property>
    <!--
    set 集合属性映射
    name 指定要映射的set集合的属性
    table 集合属性要映射到的表
    key 指定集合表(t_employee)的外键字段
    element 指定集合表的其他字段
    type 元素类型,一定要指定
    -->
    <!--
    一对多 映射的另外一张t_employee表 ;
    inverse默认false,不控制反转,说明当前方具有控制权;
    inverse=true,说明没有控制权,没有控制权不能进行关系解除和删除数据
    cascade的值 :none(默认),(save-update),(seve-update,delete 一定要有 逗号),(all 所有但不包括默认none)
    cascade=none,默认不级联操作,所谓级联就是操作某个表数据同时也会操作另外关联的表的数据
    面试题:inverse和cascade的关系: 当设置inverse=true时cascade也可以进行级联删除
    -->
    <set name="emps" table="t_employee" inverse="false" cascade="none">
    <!-- 配置外键字段 -->
    <key column="dept_id"></key>
    <one-to-many class="Employee"/>
    </set>
    </class>
    </hibernate-mapping>
    

    一和一的映射

    <!-- 身份证表映射 users 的 映射配置 -->
    <hibernate-mapping package="cn.hpe.po">
    <class name="IdCard" table="t_idcard">
    <!-- 主键 -->
    <id name="cardNum" column="cardid" >
    <!-- 如果此处class设置assigned 指定主键生成策略,为手动指定主键的值 -->
    <generator class="assigned"></generator>
    </id>
    <property name="place" length="100"></property>
    <!-- 特殊的多对一映射,即一对一,有外键方,unique=true,设置外键唯一,此时外键也成了'主键 ',此配置是基于外键的配置-->
    <many-to-one name="users" class="Users" column="usersid" unique="true" cascade="save-update"/>
    </class>
    </hibernate-mapping>
    
    <!-- users用户信息的 映射配置 -->
    <hibernate-mapping package="cn.hpe.po">
    <class name="Users" table="t_users">
    <!-- 主键 -->
    <id name="usersId" column="usersid" >
    <generator class="sequence">
    <param name="sequence">seq_tusers_usersid</param>
    </generator>
    </id>
    <property name="userName" length="50"></property>
    <!-- 一对一映射,没有外键方 -->
    <one-to-one name="idCard1" class="IdCard1" />
    </class>
    </hibernate-mapping>
    

    多和多的映射

    <!-- 项目表的 映射配置 -->
    <hibernate-mapping package="cn.hpe.po">
    <class name="Project" table="t_project">
    <!-- 主键 -->
    <id name="prjId" column="prj_id" >
    <!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
    <generator class="sequence">
    <param name="sequence">seq_project_prjid</param>
    </generator>
    </id>
    <property name="prjName" length="50"></property>
    <!-- 多对多 映射的另外一张t_relation表 -->
    <set name="developers" table="t_relation" cascade="save-update">
    <!-- 配置外键字段 -->
    <key column="prj_id"></key>
    <many-to-many class="Developer" column="d_id" ></many-to-many>
    </set>
    </class>
    </hibernate-mapping>
    
    <!-- 开发人员表的 映射配置 -->
    <hibernate-mapping package="cn.hpe.po">
    <class name="Developer" table="t_developer">
    <!-- 主键 -->
    <id name="dId" column="d_id" >
    <!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
    <generator class="sequence">
    <param name="sequence">seq_developer_did</param>
    </generator>
    </id>
    <property name="dName" length="50"></property>
    <!--
    set 集合属性映射
    name 指定要映射的set集合的属性
    table 集合属性要映射到的表
    key 指定集合表(t_employee)的外键字段
    element 指定集合表的其他字段
    type 元素类型,一定要指定
    -->
    <!--
    多对多 映射的另外一张t_relation表 ;
    inverse默认false,不控制反转,说明当前方具有控制权;
    inverse=true,说明没有控制权,没有控制权不能进行关系解除和删除数据
    cascade的值 :none(默认),(save-update),(seve-update,delete 一定要有 逗号),(all 所有但不包括默认none)
    cascade=none,默认不级联操作,所谓级联就是操作某个表数据同时也会操作另外关联的表的数据
    懒加载属性:lazy值:
    默认 true 设置懒加载
    false 关闭懒加载
    extra (更高级,经常使用这个属性)只有在使用的时候才去数据库取数据,防止只查询记录数的时候会把所有的数据查出来
    目的:提高效率
    -->
    <set name="projects" table="t_relation" cascade="save-update" lazy="extra">
    <!-- 配置外键字段 -->
    <key column="d_id"></key>
    <many-to-many class="Project" column="prj_id" ></many-to-many>
    </set>
    </class>
    </hibernate-mapping>
    

    懒加载

    及时加载--get()

    Developer developer = new Developer();
    // Hibernate: select developer0_.d_id as d1_0_0_, developer0_.dName as
    // dName0_0_ from t_developer developer0_ where developer0_.d_id=?
    // developer = (Developer) session.get(Developer.class, 1);
    // get及时加载,会把所有相关的字段查出来
    

    懒加载--load()

    // 懒加载,内部hibernate其实使用代理对象($Proxy01)进行懒加载,配置lazy=true。只是加载并没有执行数据库查询,没有sql语句输出
    developer = (Developer) session.load(Developer.class, 1);
    
    // 用到时才会查询数据库
    // Hibernate: select developer0_.d_id as d1_0_0_, developer0_.dName as
    // dName0_0_ from t_developer developer0_ where developer0_.d_id=?
    // 用到时才会查询数据库,输出:李四
    // System.out.println(developer.getdName());
    

    如何解决在session关闭后输出懒加载内容异常

    // 方式1: 先使用一下数据
    // developer.getdName();
    // 方式2:强迫代理对象初始化
    Hibernate.initialize(developer);
    // 方式3:关闭懒加载 在映射文件中设置lazy="false"
    
    // 获取事务并提交,这才是执行数据库操作
    session.getTransaction().commit();
    session.close();
    
    // 如何解决在session关闭后输出懒加载内容异常
    System.out.println(developer.getdName());
    

    一级缓存

    Developer developer = null;
    developer = (Developer) session.get(Developer.class, 1);// 查询数据时先检查缓存中是否有数据,没有数据再从数据库查询
    developer = (Developer) session.get(Developer.class, 1);// 此时一级缓存中已经有数据
    // 查询数据时先检查缓存中是否有数据,没有数据再从数据库查询
    
    // 获取事务并提交,这才是执行数据库操作
    session.getTransaction().commit();
    session.close();
    // 执行一条sql语句,说明第一次查询时缓存中没有数据,然后放入缓存,接下来的又一次查询会直接从缓存中获取
    // Hibernate:select developer0_.d_id as d1_0_0_, developer0_.dName as
    // dName0_0_ from t_developer developer0_ where developer0_.d_id=?
    

    测试缓存的方法有:

    session.flush()//缓存中的数据同步到数据库中
    session.clear()//清空当前缓存
    session.evict(developer);// 清空指定对象object
    

    二级缓存

    使用二级缓存的前提是在配置文件配置二级缓存,并指定哪些类会加入二级缓存

    <!-- ****************** 【二级缓存配置】 ****************** -->
    <!-- 开启二级缓存 -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <!-- 开启哪一个缓存框架 -->
    <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
    <!-- 开启二级缓存中的缓存查询 -->
    <property name="hibernate.cache.use_query_cache">true</property>
    
    指定哪一些(常用的)类,需要加入二级缓存,二级缓存会消耗内存,查询速度快
    usage缓存策略:
    <class-cache usage="read-write" class="cn.hpe.po.Dept"/>
    <!-- employee 集合也放入二级缓存查询 -->
    <class-cache usage="read-only" class="cn.hpe.po.Employee"/>
    <!-- 集合缓存[集合缓存的 元素 对象,也加入二级缓存,就是class="cn.hpe.po.Employee"] -->
    <collection-cache usage="read-write" collection="cn.hpe.po.Dept.emps"/>
    
    Dept dept = (Dept) session1.get(Dept.class, 1);//第一次查询,已经放入二级缓存
    ...
    dept = (Dept) session2.get(Dept.class, 1);//另一个session来获取二级缓存的数据,不会从数据库查询
    ...
    

    mappingLocations,mappingDirectoryLocations,

    mappingJarLocations区别

    由于spring对hibernate配置文件hibernate.cfg.xml的集成相当好,所以,在项目中我一直使用
    spring的org.springframework.orm.hibernate.LocalSessionFactoryBean来取代hibernate.cfg.xml文件的功能
    LocalSessionFactoryBean有好几个属性用来查找hibernate映射文件:mappingResources、mappingLocations、
    mappingDirectoryLocations与mappingJarLocations
    他们的区别:

    mappingResources:指定classpath下具体映射文件名
    <property name="mappingResources">
        <value>petclinic.hbm.xml </value>
    </property>
    
    mappingLocations:可以指定任何文件路径,并且可以指定前缀:classpath、file等
    <property name="mappingLocations">
        <value>/WEB-INF/petclinic.hbm.xml </value>
    </property>
    
    <property name="mappingLocations">
        <value>classpath:/com/company/domain/petclinic.hbm.xml </value>
    </property>
    也可以用通配符指定,'*'指定一个文件(路径)名,'**'指定多个文件(路径)名,例如:
    <property name="mappingLocations">
        <value>classpath:/com/company/domainmaps/*.hbm.xml </value>
    </property>
    上面的配置是在com/company/domain包下任何maps路径下的hbm.xml文件都被加载为映射文件 
    
    mappingDirectoryLocations:指定映射的文件路径 
    <property name="mappingDirectoryLocations">
      <list>
      <value>WEB-INF/HibernateMappings</value>
      </list>
    </property>
    
    也可以通过classpath来指出
    <property name="mappingDirectoryLocations">
      <list>
      <value>classpath:/XXX/package/</value>
      </list>
    </property>
    
    mappingJarLocations:指定加载的映射文件在jar文件中 
    

    关于java <action name="emp-*" class="employeeAction" method="{1}">

    这是利用Struts2里面的自动匹配特性。代表通配符,可以匹配任何一个字符串[1]表示匹配的参数。举个例子来说,如果你在xml文件中做了这个配置,那么当你页面里面有一个form
    <form action="/emp-add" method="post">那么,
    匹配到的就是“add”,同时method="{1}",实际上就是method="add"。
    假设有两个参数,我么在xml里面的配置是
    <action name="emp-*-*" class="{1}Action" method="{2}">
    那么第一个星号对应的是{1},第二个星号对应的是{2}
    例如,页面里面有<form action="/emp-employee-add" method="post">
    那么实际上的意思就是
    <action name="emp-employee-*" class="employeeAction" method="add">

    这样做的好处就是我们不必为增删改查资格方法写四个<action>配置。同样如果有相似的逻辑,比如学生信息的增删改查和老师信息的增删改查逻辑是一样的,那么我们也可以通过通配符来节省xml文件中的代码量

    前提是一定要遵守通配符的约定

    struts运行机制(用户访问一个url)

    index.jsp:
    <c:redirect url="/employee_list"></c:redirect>
    

    注意url

    struts.xml配置

    <!-- ###   action 实例交给spring创建;链接传过来的地址在这进行处理 ,
          method="{1}"就是匹配 ‘*’,就是方法的名字,如果url="/employee_list",则method=list,
          自动去class="employeeAction"(action实例),去类 EmployeeAction中找list()方法,执行后方法返回"list"字符串,对应name="list",
          匹配后进入/WEB-INF/list.jsp-->
          <action name="employee_*" class="employeeAction" method="{1}">//这行代码是第三步随后进入action类里执行后还返回这里才往下执行result name="list">/WEB-INF/list.jsp</result>
              <!-- 1.列表展示,放在/WEB-INF 下面 -->
              <result name="list">/WEB-INF/list.jsp</result>
              <!-- 2.进入添加页面,放在/WEB-INF 下面 -->
              <result name="add">/WEB-INF/add.jsp</result>
              <!-- 3.添加成功,进入列表,(防止用户刷新多一条记录) -->
              <result name="listAction" type="redirect">employee_list</result>
              <!-- 4.修改员工数据,进入修改页面 -->
              <result name="edit">/WEB-INF/edit.jsp</result>
    
          </action>
    
    

    EmployeeAction.java

    /**
      * 1. 员工列表展示
      */
    
     public String list() {
          List<Employee> list = employeeService.getAll();
          request.put("listEmp", list);
          return "list";//返回到struts.xml中
    
     }
    
    <result name="list">/WEB-INF/list.jsp</result>
    进入list.jsp页面
    

    list.jsp显示页面

    <table border="1" align="center" width="80%" cellpadding="5" cellspacing="0">
              <tr>
               <th>序号</th>
               <th>员工编号</th>
               <th>员工姓名</th>
               <th>员工薪水</th>
               <th>操作</th>
          </tr>
          <s:if test="#request.listEmp != null">
               <s:iterator var="emp" value="#request.listEmp" status="st">
                   <tr>
                        <td> <s:property value="#st.count"/> </td>
                        <td> <s:property value="#emp.employeeId"/> </td>
                        <td> <s:property value="#emp.employeeName"/> </td>
                        <td> <s:property value="#emp.employeeSalary"/> </td>
                        <td> <s:a href="employee_viewUpdate?employeeId=%{#emp.employeeId}">修改 </s:a>
                        <s:a href="employee_delete?employeeId=%{#emp.employeeId}">删除 </s:a> </td>
                   </tr>
    
               </s:iterator>
          </s:if>
    

    struts登陆成功后用户信息保存到session用actionContext
    ActionContext.getContext().getSession().put("adminInfo",adminInfo);

    config the follow in Oracle

    Employee.hbm.xml:

    <class name="Employee" table="t_employee">
     <!-- 主键 -->
          <id name="empId" column="empid" type="string">
              <generator class="uuid"> (if you set class="native",the type of empId of class Employee must be int)
                   <param name="sequence">seq_emp_empid</param>
              </generator>
          </id>
          <property name="empName" length="50"></property>
          <property name="salary" type="double"></property>
           <!-- 多对一 映射的另外一张t_dept表 ,dept_id 和  dept表的 映射的外键key标签的属性都是dept_id -->
          <many-to-one name="dept" class="Dept" column="dept_id"></many-to-one>
    
     </class>
    
    Employee:
        private String empId;// 员工编号
        private String empName;// 姓名
        private double salary;// 薪水
        // 一个员工对应一个部门
        private Dept dept;// 所属部门
    

    if you set the type of empId String in Employee, you will set " class="uuid""
    and create table auto primary key: uuid

    面试题

    • 不同的session是否会共享缓存数据?

      不会。
      User1 u1 = Session1.get(User.class,1); 把u1对象放入session1的缓存
      Session2.update(u1); 把u1放入session2的缓存
      U1.setName(‘new Name’);
      如果生成2条update sql, 说明不同的session使用不同的缓存区,不能共享。

    • Hibernate --list与iterator查询的区别?

    list()
    一次把所有的记录都查询出来,
    会放入缓存,但不会从缓存中获取数据
    Iterator
    N+1查询; N表示所有的记录总数
    即会先发送一条语句查询所有记录的主键(1),
    再根据每一个主键再去数据库查询(N)!

    缓存策略

    <class-cache usage="read-only"/>    放入二级缓存的对象,只读;
         <class-cache usage="nonstrict-read-write"/> 非严格的读写
         <class-cache usage="read-write"/>   读写;放入二级缓存的对象可以读、写;
         <class-cache usage="transactional"/>   (基于事务的策略)
    

    缓存的机制:Map<条件,对象>

    hibernate工作流程:

    1️⃣ 读取并解析配置文件;
    2️⃣ Configuration负责读取并创建映射信息,创建sessionfactory;
    3️⃣ SessionFactory负责创建session;
    4️⃣ Transaction负责开启事物Transaction;
    5️⃣ Query负责执行持久化操作;
    6️⃣ Transaction负责提交实物;
    7️⃣ 关闭session;
    8️⃣ 关闭sessionfactory。

    懒加载:代理对象

    内部hibernate其实使用代理对象($Proxy01)进行懒加载,配置lazy=true。只是加载并没有执行数据库查询,没有sql语句输出

    developer=(Developer)session.load(Developer.class,1);
    // 用到时才会查询数据库
    //Hibernate: select developer0_.d_id as d1_0_0_, developer0_.dName as
    // dName0_0_ from t_developer developer0_ where developer0_.d_id=?
    // 用到时才会查询数据库,输出:李四
    // System.out.println(developer.getdName());
    
    

    hibernate分页

    publicvoidpage(){
        Sessionsession=HibernateUtiles.getSession();
        // 开始事务
        session.beginTransaction();
        Queryquery=session.createQuery("from Employee");
        // 总记录数--滚动结果集
        ScrollableResultssResults=query.scroll();// 得到滚动的结果集
        sResults.last();// 滚动到最后一行
        inttotalRecords=sResults.getRowNumber()+1;// 获取总记录数
        // 设置分页参数,输出2条记录
        query.setFirstResult(2);
        query.setMaxResults(3);
        // 输出查询
        System.out.println(query.list());
        System.out.println("记录数"+totalRecords);
        // 获取事务并提交,这才是执行数据库操作
        session.getTransaction().commit();
        session.close();
    }
    

    hibernate连接查询--迫切连接

    @Test
    publicvoidfetch(){
        Sessionsession=HibernateUtiles.getSession();
        // 开始事务
        session.beginTransaction();
        // 迫切 内连接,把右表的数据填充到左表,返回的类型就是左表的类型dept
        // Query query = session
        // .createQuery("fromDeptd inner join fetch d.emps");
        // query.list();
        // 迫切 左外连接,把右表的数据填充到左表,返回的类型就是左表的类型dept
        // Query query =
        // session.createQuery("fromDeptd left join fetch d.emps");
        // query.list();
    

    相关文章

      网友评论

          本文标题:hibernate详解

          本文链接:https://www.haomeiwen.com/subject/azlfpftx.html