hibernate詳解

介紹

Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了很是輕量級的對象封裝,它將POJO與數據庫表創建映射關係,是一個全自動的orm框架,hibernate能夠自動生成SQL語句,自動執行,使得Java程序員能夠爲所欲爲的使用對象編程思惟來操縱數據庫。 Hibernate能夠應用在任何使用JDBC的場合,既能夠在Java的客戶端程序使用,也能夠在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate能夠在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任。java

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

這是利用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">spring

這樣作的好處就是咱們沒必要爲增刪改查資格方法寫四個配置。一樣若是有類似的邏輯,好比學生信息的增刪改查和老師信息的增刪改查邏輯是同樣的,那麼咱們也能夠經過通配符來節省xml文件中的代碼量sql

前提是必定要遵照通配符的約定數據庫

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: uuidbash

面試題

  • 不一樣的session是否會共享緩存數據?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();
複製代碼
相關文章
相關標籤/搜索