java深刻探究12-框架之Hibernate

1.引入SSH框架java

Struts框架,基於MVC 模式的應用層框架技術mysql

Hibernate,基於持久層框架(數據訪問層使用)面試

Dao代碼編寫的幾種方式:sql

1. 原始jdbc操做,Connection/Statement/ResultSet數據庫

2. 自定義一個持久層框架,封裝dao的通用方法api

3. DbUtils組件,輕量級數據訪問技術數組

4. Hibernate技術【最終執行的就是jdbc代碼!】緩存

2. 開始學習SSH框架服務器

a)  ORM概念session

Object Realtion Mapping:對象關係映射

用於:直接將對象存入數據庫;直接從數據庫拿到對象!

想要作到這點:映射,映射信息存入xml

映射中信息:1.對象與表,2.屬性與字段,3.類型的對應

O R M三者間的關係

b)  組件的學習:源碼引入jar文件

c)  環境搭建

咱們也能夠直接添加jar包,以後只有再添加一個鏈接數據庫的包就行了

 

  1. 下載源碼hibernate-distribution-3.6.0.Final
  2. jar包:實際上咱們能夠直接

hibernate3.jar核心+required必須引入的(6個)+jpa目錄+數據庫驅動包required中的jarantlr-2.7.6commons-collections-3.1dom4j-1.6.1javassist-3.12.0.GAjta-1.1slf4j-api-1.6.1

   Jpahibernate-jpa-2.0-api-1.0.0.Final.jar

               數據庫驅動:mysql-connector-java-5.1.12-bin.jar

  1. 寫對象和對象映射
    1. Employee.java 對象
    2. Employee.hbm.xml對象映射 咱們可在源碼中隨便找個hbm文件再修改成本身的
  2. Src/hibernate.cfg.xml(源碼project/etc/hibernate.cfg.xml能夠找到再自行修改)
    1. 數據庫鏈接配置;2.加載所用的映射(*.hbm.xml

例子:

  獲取sessionFactory的工具類

public class HibernateUtils {
    private static SessionFactory sf;
    static{
        sf=new Configuration().configure().buildSessionFactory();    
    }
    public static  Session getSession(){
        return sf.openSession();
    }
}
View Code

  d) 核心API

    Configuration 配置管理類對象

      Config.configure(); 加載主配置文件的方法(hibernate.cfg.xml

      默認加載src/hibernate.cfg.xml

      Config.configure(cn/config/hibernate.cfg.xml);加載指定路徑下的

      config.buildSessionFactory();建立session的工廠對象

    SessionFactory Session的工廠(或者說表明hibernate.cfg.xml文件)

      sf.openSession(); 建立一個session對象

      sf.getCurrentSession();建立一個session或者取出session對象

    Session session對象維護了一個鏈接(Connection,表明與數據庫鏈接的回話,hibernate最重要的一個對象

      session.beginTransaction();開啓一個事務,hibernate要求全部與數據庫的操做都要有事務,不然報錯

      更新:

        Session.save(obj)

        Session.update(obj)

        session.saveOrUpdate(obj);保存或者更新方法

          —》有主鍵執行更新

          —》沒主鍵執行保存

          —》若是設置的主鍵不存在報錯

      主鍵查詢:

        Session.get(obj.class,1) 主鍵查詢

        Session.load(obj.class,1)主鍵查詢(支持懶加載)

      HQL查詢:

        與SQL區別:SQL(結構化查詢語句)面向表查詢,不區分大小寫

              HQL:Hebernate query language面向對象的查詢語句,由於是對象,區分大小寫

 

//適合有數據庫基礎的HQL面向對象,SQL面向表
    public static void HQLQuery(){
        /*這裏的Hibernate的SQL與jdbc中的sql不一樣,對象對象的,jdbc是面向表字段的*/
        
            Query q=session.createQuery("from Employee where empId=1 or empId=2");
            List<Employee> list=q.list();
            System.out.println(list);
    }
View Code

 

      Criteria查詢:沒有數據庫基礎也能夠用徹底面向對象的查詢

 

//QBC查詢,query by criteria 徹底面向對象的查詢
    public static void CriteriaQuery(){
        Criteria criteria=session.createCriteria(Employee.class);
        //加條件
        criteria.add(Restrictions.eq("empId", 1));
        List<Employee > list=criteria.list();//開始查詢
        System.out.println(list);
    }
View Code

 

      本地SQL查詢:原生sql,缺點不能數據庫跨平臺

 

//本地事務查詢sql直接查詢
    public static void SqlQuery(){
        //把每一行數數據封裝爲對象數組,再添加list集合
        //SQLQuery sq=session.createSQLQuery("select * from employee");
        SQLQuery sq=session.createSQLQuery("select * from employee").addEntity(Employee.class);
        //想要封裝對象-》
        List<?> list=sq.list();
        System.out.println(list);
    }
View Code

 

   e)可能會遇到的問題

    1.ClassNotFoundException...,缺乏jar文件!

    2.若是程序執行,hibernate也生成sql語句,但數據沒有結果影響:

      通常是事務沒有提交。。

  f)Hibernate crud

    增刪改查方法

package crud;

import hello.Employee;

import java.io.Serializable;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import utils.HibernateUtils;

public class EmployeeImpl implements EmployeeDao {
    
    @Override
    public void save(Employee emp) {
        // TODO Auto-generated method stub
        //獲取session 
        Session session=null;
        Transaction ts=null;    
        try {
            
            //業務邏輯
            session=HibernateUtils.getSession();
            ts=session.beginTransaction();
            
            
        } catch (HibernateException e) {
            // TODO Auto-generated catch block
            throw new RuntimeException();
        }finally{
            ts.commit();
            session.close();
        }
        
    }

    @Override
    public void update(Employee emp) {
        // TODO Auto-generated method stub
        //獲取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
            
            //業務邏輯
            session.update(emp);
            
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
                
    }

    @Override
    public Employee findById(Serializable id) {
        //獲取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //業務邏輯
            return (Employee)session.get(Employee.class, id);
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public List<Employee> getAll() {
        //獲取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //業務邏輯
            return session.createQuery("from Employee").list();
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public List<Employee> getAll(String employeeName) {
        // TODO Auto-generated method stub
        //獲取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //業務邏輯
            Query q =session.createQuery("from Employee where empName=?");
            q.setParameter(0, employeeName);
            return q.list();
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public List<Employee> getAll(int index, int count) {
        //獲取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //業務邏輯
            Query q= session.createQuery("from Employee");
            q.setFirstResult(index);//查詢起始行
            q.setMaxResults(count);//查詢的行數
            return q.list();
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public void delete(Serializable id) {
        Session session = null;
        Transaction tx = null;
        try {
            session = HibernateUtils.getSession();
            tx = session.beginTransaction();
            // 先根據id查詢對象,再判斷刪除
            Object obj=session.get(Employee.class, id);
            if(obj!=null){
                session.delete(obj);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            tx.commit();
            session.close();
        }
    }

}
View Code

  g)配置文件

  主配置:hibernate.cfg.xml:主要配置數據庫信息、其餘參數、映射信息

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <!-- 一般,一個session-factory節點表明一個數據庫 -->
    <session-factory>
    
        <!-- 1. 數據庫鏈接配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hib_demo</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <!-- 
            數據庫方法配置, hibernate在運行的時候,會根據不一樣的方言生成符合當前數據庫語法的sql
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        
        
        <!-- 2. 其餘相關配置 -->
        <!-- 2.1 顯示hibernate在運行時候執行的sql語句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 2.2 格式化sql -->
        <property name="hibernate.format_sql">true</property>
        <!-- 2.3 自動建表  -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        
        
        <!-- 3. 加載全部映射 --> 
        <mapping resource="hello/Employee.hbm.xml"/>
        
    </session-factory>
</hibernate-configuration>
View Code

  其中自動建表:就算數據庫中的表被刪了只要配置好就能自動在數據庫中建表

  <property name="hibernate.hbm2ddl.auto">update</property>

#hibernate.hbm2ddl.auto create-drop 每次在建立sessionFactory時候執行建立表;
                                當調用sesisonFactory的close方法的時候,刪除表!
#hibernate.hbm2ddl.auto create   每次都從新建表; 若是表已經存在就先刪除再建立
#hibernate.hbm2ddl.auto update  若是表不存在就建立; 表存在就不建立;
#hibernate.hbm2ddl.auto validate  (生成環境時候) 執行驗證: 當映射文件的內容與數據庫表結構不同的時候就報錯!
View Code

  自動建表代碼方式:SchemaExport export = new SchemaExport(config);主要是建立工具類對象

           export.creat(true,true),參數1顯示在colsole,第二個是在數據庫中執行

// 自動建表
    @Test
    public void testCreate() throws Exception {
        // 建立配置管理類對象
        Configuration config = new Configuration();
        // 加載主配置文件
        config.configure();
        
        // 建立工具類對象
        SchemaExport export = new SchemaExport(config);
        // 建表
        // 第一個參數: 是否在控制檯打印建表語句
        // 第二個參數: 是否執行腳本
        
View Code

   映射配置:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">


<!-- 映射文件: 映射一個實體類對象;  描述一個對象最終實現能夠直接保存對象數據到數據庫中。  -->
<!-- 
    package: 要映射的對象所在的包(可選,若是不指定,此文件全部的類都要指定全路徑)
    auto-import 默認爲true, 在寫hql的時候自動導入包名
                若是指定爲false, 再寫hql的時候必需要寫上類的全名;
                  如:session.createQuery("from cn.itcast.c_hbm_config.Employee").list();
 -->
<hibernate-mapping package="cn.itcast.c_hbm_config" auto-import="true">
    
    <!-- 
        class 映射某一個對象的(通常狀況,一個對象寫一個映射文件,即一個class節點)
            name 指定要映射的對象的類型
            table 指定對象對應的表;
                  若是沒有指定表名,默認與對象名稱同樣 
     -->
    <class name="Employee" table="employee">
        
        <!-- 主鍵 ,映射-->
        <id name="empId" column="id">
            <!-- 
                主鍵的生成策略
                    identity  自增加(mysql,db2)
                    sequence  自增加(序列), oracle中自增加是以序列方法實現
                    native  自增加【會根據底層數據庫自增加的方式選擇identity或sequence】
                            若是是mysql數據庫, 採用的自增加方式是identity
                            若是是oracle數據庫, 使用sequence序列的方式實現自增加
                    
                    increment  自增加(會有併發訪問的問題,通常在服務器集羣環境使用會存在問題。)
                    
                    assigned  指定主鍵生成策略爲手動指定主鍵的值
                    uuid      指定uuid隨機生成的惟一的值
                    foreign   (外鍵的方式, one-to-one講)
             -->
            <generator class="uuid"/>
        </id>
        
        <!-- 
            普通字段映射
            property
                name  指定對象的屬性名稱
                column 指定對象屬性對應的表的字段名稱,若是不寫默認與對象屬性一致。
                length 指定字符的長度, 默認爲255
                type   指定映射表的字段的類型,若是不指定會匹配屬性的類型
                    java類型:     必須寫全名
                    hibernate類型:  直接寫類型,都是小寫
        -->
        <property name="empName" column="empName" type="java.lang.String" length="20"></property>
        <property name="workDate" type="java.util.Date"></property>
        <!-- 若是列名稱爲數據庫關鍵字,須要用反引號或改列名。 -->
        <property name="desc" column="`desc`" type="java.lang.String"></property>
        
    </class>
    

</hibernate-mapping>
View Code

   其中複合主鍵:一個表中主鍵除了一個可能還會有2個或更多在一塊兒做爲複合主鍵

      映射文件中配置複合主鍵<composite-id>

// 複合主鍵類
public class CompositeKeys implements Serializable{
    private String userName;
    private String address;
   // .. get/set
}
public class User {

    // 名字跟地址,不會重複
    private CompositeKeys keys;
    private int age;
}
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.d_compositeKey" auto-import="true">
    
    <class name="User">
        
        <!-- 複合主鍵映射 -->
        <composite-id name="keys">
            <key-property name="userName" type="string"></key-property>
            <key-property name="address" type="string"></key-property>
        </composite-id>
        
        <property name="age" type="int"></property>        
        
    </class>
    

</hibernate-mapping>

App.java

public class App2 {
    
    private static SessionFactory sf;
    static  {        
        // 建立sf對象
        sf = new Configuration()
            .configure()
            .addClass(User.class)  //(測試) 會自動加載映射文件:Employee.hbm.xml
            .buildSessionFactory();
    }

    //1. 保存對象
    @Test
    public void testSave() throws Exception {
        Session session = sf.openSession();
        Transaction tx = session.beginTransaction();
        
        // 對象
        CompositeKeys keys = new CompositeKeys();
        keys.setAddress("廣州棠東");
        keys.setUserName("Jack");
        User user = new User();
        user.setAge(20);
        user.setKeys(keys);
        
        // 保存
        session.save(user);
        
        
        tx.commit();
        session.close();
    }
    
    @Test
    public void testGet() throws Exception {
        Session session = sf.openSession();
        Transaction tx = session.beginTransaction();
        
        //構建主鍵再查詢
        CompositeKeys keys = new CompositeKeys();
        keys.setAddress("廣州棠東");
        keys.setUserName("Jack");
        
        // 主鍵查詢
        User user = (User) session.get(User.class, keys);
        // 測試輸出
        if (user != null){
            System.out.println(user.getKeys().getUserName());
            System.out.println(user.getKeys().getAddress());
            System.out.println(user.getAge());
        }
        
        
        tx.commit();
        session.close();
    }
}
View Code

hibernate執行週期:

-------------------------------------------------------------------------

關聯映射

1. 集合映射  

  開發流程:需求分析/數據庫設計、項目設計/ 編碼/測試/實施部署上線/驗收

  需求:一個用戶有多個收貨地址,經過映射建立映射表到數據庫中,並對錶數據作處理

     核心思路:配置文件中<property name="hibernate.hbm2ddl.auto">update</property>設置爲自動生成表

          映射文件配置set映射配置:沒有啥特殊之處,對應的只需外鍵就能夠

                List集合映射:List有記住存儲順序的能力,處了外鍵,還需能記住存儲順序的list-index

                Map集合映射:map有兼職對的能力,處了外鍵還需鍵的存儲,值得存儲

     查詢數據時,當查詢Map映射信息,或list信息,都屬於懶加載,代碼寫到時纔會從數據庫發送sql

 

<!-- 
            set集合屬性的映射
                name 指定要映射的set集合的屬性
                table 集合屬性要映射到的表
                key  指定集合表(t_address)的外鍵字段
                element 指定集合表的其餘字段
                    type 元素類型,必定要指定
         -->
         <set name="address" table="t_address">
             <key column="uid"></key>
             <element column="address" type="string"></element>
         </set>
View Code

 

完整代碼:

// javabean設計
public class User {

    private int userId;
    private String userName;
    // 一個用戶,對應的多個地址
    private Set<String> address;
    private List<String> addressList = new ArrayList<String>(); 
    //private String[] addressArray; // 映射方式和list同樣     <array name=""></array>
    private Map<String,String> addressMap = new HashMap<String, String>();
    
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.a_collection">
    
    <class name="User" table="t_user">
        <id name="userId" column="id">
            <generator class="native"></generator>
        </id>    
        <property name="userName"></property>
        
        <!-- 
            set集合屬性的映射
                name 指定要映射的set集合的屬性
                table 集合屬性要映射到的表
                key  指定集合表(t_address)的外鍵字段
                element 指定集合表的其餘字段
                    type 元素類型,必定要指定
         -->
         <set name="address" table="t_address">
             <key column="uid"></key>
             <element column="address" type="string"></element>
         </set>
         
         <!-- 
             list集合映射
                 list-index  指定的是排序列的名稱 (由於要保證list集合的有序)
          -->
          <list name="addressList" table="t_addressList">
                <key column="uid"></key>
                <list-index column="idx"></list-index>
                <element column="address" type="string"></element>
          </list>
          
          <!-- 
              map集合的映射
                  key  指定外鍵字段
                  map-key 指定map的key 
                  element  指定map的value
           -->
          <map name="addressMap" table="t_addressMap">
              <key column="uid"></key>
              <map-key column="shortName" type="string" ></map-key>
              <element column="address" type="string" ></element>
          </map>
          
         
    </class>
    

</hibernate-mapping>


    // 保存set
    @Test
    public void testSaveSet() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        //-- 保存
        Set<String> addressSet = new HashSet<String>();
        addressSet.add("廣州");
        addressSet.add("深圳");
        // 用戶對象
        User user = new User();
        user.setUserName("Jack");
        user.setAddress(addressSet);
        
        // 保存
        session.save(user);
        
        session.getTransaction().commit();
        session.close();
    }
    
    // 保存list/map
    @Test
    public void testSaveList() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        User user = new User();
        user.setUserName("Tom");
//        // 用戶對象  --  list
//        user.getAddressList().add("廣州");
//        user.getAddressList().add("深圳");
//        // 保存
//        session.save(user);
        
        // 用戶對象  --  Map
        user.getAddressMap().put("A0001", "廣州");
        user.getAddressMap().put("A0002", "深圳");
        
        // 保存
        session.save(user);
        
        session.getTransaction().commit();
        session.close();
    }
View Code  

2. 關聯映射 (重點)

  有7中關聯映射:1.單向一對一,單項一對一惟一外鍵關聯;2.單向一對多,3.單向多對一,4.單向多對多,5.雙向多對多,6.雙向一對多,7.雙向一對一

  1)引入:集合映射,映射的集合元素,都是普通的類型, 可否爲對象類型

  2)需求1:

      部門與員工:一個部門有多個員工【一對多】;多個員工,屬於一個部門【多對一】

   需求2:

      項目與開發員工:一個項目有多個開發人員,一個開發人員參與多個項目【多對多】

  3)多對一映射與一對多

    映射配置:

 

    <!-- 
            一對多關聯映射配置  (經過部門管理到員工)
            casecade="save-update,delete";//關聯表的聯機影響          
            inverse=false  set集合映射的默認值; 表示有控制權
         -->
         <set name="emps" cascade="save-update,delete" table="t_employee" inverse="true">   <!-- table="t_employee" -->
              <key column="dept_id"></key>
              <one-to-many class="Employee"/>
         </set>
  

      <!--
        多對一關聯映射配置
        
      -->
      <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>

 

Inverse屬性

  維護關係的時候起做用,表示控制權是否轉移(只在一的一方有做用)

  inverse=false 不反轉;當前方有控制權

  inverse-true 反轉:當前方沒有控制權,因此在以一對多存儲(最後由一的結束控制關係)時就沒有控制到關係了

影響

1. 保存數據 
        有影響。
       若是設置控制反轉,即inverse=true, 而後經過部門方維護關聯關係。在保存部門的時候,同時保存員工, 數據會保存,但關聯關係不會維護。即外鍵字段爲NULL
        
    2. 獲取數據
        無。
    3. 解除關聯關係?
        有影響。
        inverse=false,  能夠解除關聯
inverse=true,  當前方(部門)沒有控制權,不能解除關聯關係
(不會生成update語句,也不會報錯)
    4. 刪除數據對關聯關係的影響?
        有影響。
        inverse=false, 有控制權, 能夠刪除。先清空外鍵引用,再刪除數據。
        inverse=true,  沒有控制權: 若是刪除的記錄有被外鍵引用,會報錯,違反主外鍵引用約束!  若是刪除的記錄沒有被引用,能夠直接刪除。
View Code

casecade屬性 【能夠設置到一的方和多的方】

  none          不級聯操做, 默認值

  save-update     級聯保存或更新

  delete   級聯刪除

  save-update,delete    級聯保存、更新、刪除

  all                 同上。級聯保存、更新、刪除

 

例子:

public class Dept {

    private int deptId;
    private String deptName;
    // 【一對多】 部門對應的多個員工
    private Set<Employee> emps = new HashSet<Employee>();
    
public class Employee {

    private int empId;
    private String empName;
    private double salary;
    // 【多對一】員工與部門
    private Dept dept;

Dept.hbm.xml    
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.b_one2Many">
    
    <class name="Dept" table="t_dept">
        <id name="deptId">
            <generator class="native"></generator>
        </id>    
        <property name="deptName" length="20"></property>
        
        <!-- 
            一對多關聯映射配置  (經過部門管理到員工)
            Dept 映射關鍵點:
            1.  指定 映射的集合屬性: "emps"
            2.  集合屬性對應的集合表: "t_employee"
            3.  集合表的外鍵字段   "t_employee. dept_id"
            4.  集合元素的類型
            
         -->
         <set name="emps">   <!-- table="t_employee" -->
              <key column="dept_id"></key>
              <one-to-many class="Employee"/>
         </set>
         
         
    </class>
    

</hibernate-mapping>


Employee.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.b_one2Many">
    
    <class name="Employee" table="t_employee">
        <id name="empId">
            <generator class="native"></generator>
        </id>    
        <property name="empName" length="20"></property>
        <property name="salary" type="double"></property>
        
        <!-- 
            多對一映射配置
            Employee 映射關鍵點:
            1.  映射的部門屬性  :  dept
            2.  映射的部門屬性,對應的外鍵字段: dept_id
            3.  部門的類型
         -->
         <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>
         
    </class>
    

</hibernate-mapping>

測試

public class App {
    
    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Dept.class)   
            .addClass(Employee.class)   // 測試時候使用
            .buildSessionFactory();
    }

    // 保存, 部門方 【一的一方法操做】
    @Test
    public void save() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 部門對象
        Dept dept = new Dept();
        dept.setDeptName("應用開發部");
        // 員工對象
        Employee emp_zs = new Employee();
        emp_zs.setEmpName("張三");
        Employee emp_ls = new Employee();
        emp_ls.setEmpName("李四");
        // 關係
        dept.getEmps().add(emp_zs);
        dept.getEmps().add(emp_ls);

        // 保存
        session.save(emp_zs);
        session.save(emp_ls);
        session.save(dept); // 保存部門,部門下全部的員工  
        
        session.getTransaction().commit();
        session.close();
        /*
         *  結果
         *  Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_dept (deptName) values (?)
            Hibernate: update t_employee set deptId=? where empId=?    維護員工引用的部門的id
            Hibernate: update t_employee set deptId=? where empId=?
         */
    }
    // 【推薦】 保存, 部員方 【多的一方法操做】
    @Test
    public void save2() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 部門對象
        Dept dept = new Dept();
        dept.setDeptName("綜合部");
        // 員工對象
        Employee emp_zs = new Employee();
        emp_zs.setEmpName("張三");
        Employee emp_ls = new Employee();
        emp_ls.setEmpName("李四");
        // 關係
        emp_zs.setDept(dept);
        emp_ls.setDept(dept);
        
        
        // 保存
        session.save(dept); // 先保存一的方法
        session.save(emp_zs);
        session.save(emp_ls);// 再保存多的一方,關係回自動維護(映射配置完)
        
        session.getTransaction().commit();
        session.close();
        /*
         *  結果
         *  Hibernate: insert into t_dept (deptName) values (?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            少生成2條update  sql
         */
    }
    
}
View Code

總結:多的一方來維護關係能提升效率

  一對多的保存:先保存員工,再保存部門,關係有部門來維護,多了些update代碼,不推薦

  多對一的保存:先保存部門再保存員工,關係有員工來維護,效率高些

例子中兩種保存都在測試App中save(),save2()

  配置一對多與多對一,這種叫「雙向關聯」

  只配置一對多,           「單項一對多」

  只配置多對一,           「單項多對一」

  注意:

  配置了哪一方,哪一方纔有維護關聯關係的權限! 

3. 多對多映射

項目與開發人員

  電商系統   OA系統   曹吉   王春

完整代碼:

/**
 * 開發人員
 * 
 * @author Jie.Yuan
 * 
 */
public class Developer {
    private int d_id;
    private String d_name;
    // 開發人員,參數的多個項目
    private Set<Project> projects = new HashSet<Project>();
}

/**
 * 項目
 * 
 * @author Jie.Yuan
 * 
 */
public class Project {
    private int prj_id;
    private String prj_name;
    // 項目下的多個員工
    private Set<Developer> developers = new HashSet<Developer>();
    
Project.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_many2many">
    
    <class name="Project" table="t_project">
        <id name="prj_id">
            <generator class="native"></generator>
        </id>    
        <property name="prj_name" length="20"></property>
        <!-- 
            多對多映射:
            1.  映射的集合屬性: 「developers」
            2.  集合屬性,對應的中間表: 「t_relation」
            3. 外鍵字段:  prjId
            4. 外鍵字段,對應的中間表字段:  did
            5.   集合屬性元素的類型
         -->
         <set name="developers" table="t_relation" cascade="save-update">
             <key column="prjId"></key>
             <many-to-many column="did" class="Developer"></many-to-many>
         </set>
         
    </class>
    

</hibernate-mapping>


Developer.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_many2many">
    
    <class name="Developer" table="t_developer">
        <id name="d_id">
            <generator class="native"></generator>
        </id>    
        <property name="d_name" length="20"></property>
        
        <!-- 
            多對多映射配置: 員工方
                name  指定映射的集合屬性
                table 集合屬性對應的中間表
                key   指定中間表的外鍵字段(引用當前表t_developer主鍵的外鍵字段)
                many-to-many
                    column 指定外鍵字段對應的項目字段
                    class  集合元素的類型
         -->
        <set name="projects" table="t_relation">
            <key column="did"></key>
            <many-to-many column="prjId" class="Project"></many-to-many>
        </set>
        
         
    </class>
    

</hibernate-mapping>
View Code

 

多對多配置:

<set name="projects" table="t_relation">
            <key column="did"></key>
            <many-to-many column="prjId" class="Project"></many-to-many>
        </set>

  在多對多中設置關係維護inverse屬性:

1) 保存數據
有影響。
     inverse=false ,有控制權,能夠維護關聯關係; 保存數據的時候會把對象關係插入中間表;
    inverse=true,  沒有控制權, 不會往中間表插入數據。
2) 獲取數據
    無。

3) 解除關係
    // 有影響。
    // inverse=false ,有控制權, 解除關係就是刪除中間表的數據。
    // inverse=true, 沒有控制權,不能解除關係。
4) 刪除數據
    有影響。
    // inverse=false, 有控制權。 先刪除中間表數據,再刪除自身。
    // inverse=true, 沒有控制權。 若是刪除的數據有被引用,會報錯! 不然,才能夠刪除
View Code

   映射總結:

一對多:
	<set name="映射的集合屬性" table="(可選)集合屬性對應的外鍵表">
	   <key column="外鍵表的,外鍵字段" />
	   <one-to-many class="集合元素的類型" />
	</set>

多對一:
    <many-to-one name="對象屬性" class="對象類型" column="外鍵字段字段" />

多對多
    <set name="" table="">
        <key column="" />
        <many-to-many column="" class="">
    </set>

  -------------------------------------------------------------------

1.對象狀態

   臨時狀態、持久化狀態、遊離狀態

  臨時狀態:new建立出來的,不禁session管理,數據庫中沒有對應的值

  持久化狀態:session來調用save()方法,管理對象,數據庫中有數據

        session.save(user); //持久化狀態後再對user處理就會反應到數據庫中

          user.setUserName("Jack333333"); // 會反映到數據庫

  遊離狀態:session管理,數據庫中有值管理,通常都是持久化對象session關閉後

 

  對對象的處理,不一樣結果是由對象當前的狀態決定的!!!

    user = (User) session.get(User.class, 5);// 先檢查緩存中是否有數據,若是有不查詢數據庫,直接從緩存中獲取

  對象轉換例子:

//1. 對象狀態的轉換
    @Test
    public void testSaveSet() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 建立對象                        【臨時狀態】
        User user = new User();
        user.setUserName("Jack22222");
        // 保存                            【持久化狀態】
        session.save(user);        
        user.setUserName("Jack333333");  // 會反映到數據庫
        
        
        // 查詢
        /*User user = (User) session.get(User.class, 5);
        user.setUserName("Tomcat");// hibernate會自動與數據庫匹配(一級緩存),若是同樣就更新數據庫
*/        
        session.getTransaction().commit();
        session.close();
        
        
        user.setUserName("Jack444444444");
        // 打印                            【遊離狀態】
        System.out.println(user.getUserId());
        System.out.println(user.getUserName());
    }
View Code

  

2.緩存

  緩存目的:減小對數據庫的訪問,從而提供hibernate的執行效率

  1)一級緩存

    session的緩存就是一級緩存;

    1.如何將對象存入緩存:session調用方法save/saveOrUpdate/get/load/list/iterator方法時會將對象存入緩存

    2.緩存感受:小數據感受不到,當操做數據庫次數多時就能體現出來了

    3.如何處理session的緩存:用戶不能操做緩存數據,若是想要操做它的緩存數據必須經過hibernate提供的evit(),clear()方法

    4.緩存的幾個方法:

        session.flush();//讓一級緩存與數據庫同步,同步後就至關事務提交,session就失去管理,對象處於遊離狀態

        session.evit(arg0);//清空緩存中指定的對象

        session.clear();//清空緩存中全部的對象

     5.什麼狀況用上面的方法:批量操做的時候

         Session.flush();   // 先與數據庫同步

         Session.clear();   // 再清空一級緩存內容

    6.面試題:

      一:session是否會共享緩存數據?不會!

      二:list,iterator查詢區別

          list查詢會將全部的查詢的記錄都放入緩存,但不會緩存中獲取數據

          iterator:查詢了N+1次,N是全部的查詢記錄,先將全部記錄的主鍵(1)存入緩存,以後再經過緩存中的主鍵得到每條記錄

 

/**
     * list與iterator區別
     * 1. list 方法
     * 2. iterator 方法
     * 3. 緩存
     * @throws Exception
     */
    //1.  list 方法
    @Test
    public void list() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        // HQL查詢
        Query q = session.createQuery("from User ");
        // list()方法
        List<User> list = q.list();
        
        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i));
        }
        
        session.getTransaction().commit();  
        session.close();
    }
    
    //2. iterator 方法
    @Test
    public void iterator() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        // HQL查詢
        Query q = session.createQuery("from User ");
        // iterator()方法
        Iterator<User> it = q.iterate();
        while(it.hasNext()){
            // 獲得當前迭代的每個對象
            User user = it.next();
            System.out.println(user);
        }
        
        
        
        session.getTransaction().commit();  
        session.close();
    }
View Code

3.懶加載:

  1)session.get()不是懶加載,session.load()默認是懶加載

  2)懶加載對於set集合的配置:

      <!-- 
            集合屬性,默認使用懶加載 
            lazy
                true 懶加載
                extra 懶加載(智能)
                false 關閉懶加載
        
        -->
         <set name="emps" lazy="extra">
              <key column="dept_id"></key>
              <one-to-many class="Employee"/>
         </set>

  3)懶加載運行路線:

    在真正使用數據的時候才向數據庫發送查詢的sql

    若是調用集合的size()/isEmpty()方法,只是統計,不真正查詢數據!

  4)懶加載異常

     Session關閉後,不能使用懶加載數據!

 

     若是session關閉後,使用懶加載數據報錯:

 

         org.hibernate.LazyInitializationException: could not initialize proxy - no Session

     如何解決session關閉後不能加載數據:

        1.在關閉以前使用下數據;//dept.getDeptName();

        2.強迫代理初始化://.Hibernate.initialize(dept);

        3.直接關閉懶加載://lazy=false//在配置文件中直接修改

  5)例子:

//1. 主鍵查詢,及區別
    @Test
    public void get_load() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        Dept dept = new Dept();
        // get: 及時查詢
        //dept = (Dept) session.get(Dept.class, 1);
        //System.out.println(dept.getDeptName());
        
        // load,默認懶加載, 及在使用數據的時候,才向數據庫發送查詢的sql語句!
        dept = (Dept)session.load(Dept.class, 1);
        // 方式1: 先使用一下數據
        dept.getDeptName();
        // 方式2:強迫代理對象初始化
        //Hibernate.initialize(dept);
        // 方式3:關閉懶加載
        
        session.getTransaction().commit();
        session.close();
        
        // 在這裏使用
        System.out.println(dept.getDeptName());
    }


    //1. 主鍵查詢,及區別
    @Test
    public void set() {
        Session session = sf.openSession();
        session.beginTransaction();
        Dept dept = (Dept) session.get(Dept.class, 10);
        System.out.println(dept.getDeptName());
        System.out.println("------");
        System.out.println(dept.getEmps().isEmpty());  //  SQL,不是真正的查詢
        
        session.getTransaction().commit();
        session.close();
        
    }
View Code

4.映射

  多對一;一對多;多對多;一對一(多對一的特例);組件;繼承

  1)一對一映射

    例如案例:用戶和身份證信息

    

    基於外鍵設計:

      須要注意點:user對象的idcard屬性:沒有外鍵方只有主鍵方用<one-to-one name="idCard" class="IdCard"></one-to-one>

            IdCard對象的user屬性:有外鍵方用 <many-to-one name="user" column="user_id" class="User" unique="true" cascade="save-update"></many-to-one> 

              一對一是特殊的多對一:idCard的user_id外鍵對應惟一一個主鍵;

// 身份證
public class IdCard {

    // 身份證號(主鍵)
    private String cardNum;// 對象惟一表示(Object Identified, OID)
    private String place; //  身份證地址
    // 身份證與用戶,一對一的關係
    private User user;
    
// 用戶
public class User {

    private int userId;
    private String userName;
    // 用戶與身份證信息, 一對一關係
    private IdCard idCard;
    

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_one2one">
    
    <class name="IdCard" table="t_IdCard">
        <id name="cardNum">
            <generator class="assigned"></generator>
        </id>    
        <property name="place" length="20"></property>
        
        <!-- 
            一對一映射,有外鍵方
            unique="true"   給外鍵字段添加惟一約束
         -->
         <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>
            
    </class>
    

</hibernate-mapping>


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_one2one">
    
    <class name="User" table="t_user">
        <id name="userId">
            <generator class="native"></generator>
        </id>    
        <property name="userName" length="20"></property>
        <!-- 
            一對一映射: 沒有外鍵方
         -->
         <one-to-one name="idCard" class="IdCard"></one-to-one>
             
    </class>
    

</hibernate-mapping>
View Code

    基於主鍵設計: 

      須要注意點:主鍵是Icard表user_id是主鍵,也將是User表的外鍵

            IdCard的主鍵user_id的設置:,而且須要給IdCard的user對象設置其外鍵約束<one-to-one name="user" class="User" constrained="true"  cascade="save-update"></one-to-one>

            User的配置就簡單了idCard配置:<one-to-one name="idCard" class="IdCard"></one-to-one>

<id name="user_id">
            <!-- 
                id 節點指定的是主鍵映射, 即user_id是主鍵
                主鍵生成方式: foreign  即把別的表的主鍵做爲當前表的主鍵;
                        property (關鍵字不能修改)指定引用的對象     對象的全名 cn..User、  對象映射 cn.User.hbm.xml、   table(id)
             -->
             <!-- 這裏講user表的主鍵做爲Idcard的外鍵 -->
            <generator class="foreign">
                <!-- 經過他能找到User表的主鍵 -->
                <param name="property">user</param>
            </generator>
        </id>    
View Code
// 身份證
public class IdCard {

    private int user_id;
    // 身份證號
    private String cardNum;
    private String place; //  身份證地址
    // 身份證與用戶,一對一的關係
    private User user;
    
    

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_one2one2">
    
    <class name="IdCard" table="t_IdCard">
        <id name="user_id">
            <!-- 
                id 節點指定的是主鍵映射, 即user_id是主鍵
                主鍵生成方式: foreign  即把別的表的主鍵做爲當前表的主鍵;
                        property (關鍵字不能修改)指定引用的對象     對象的全名 cn..User、  對象映射 cn.User.hbm.xml、   table(id)
             -->
            <generator class="foreign">
                <param name="property">user</param>
            </generator>
        </id>    
        <property name="cardNum" length="20"></property>
        <property name="place" length="20"></property>
        
        <!-- 
            一對一映射,有外鍵方
            (基於主鍵的映射)
             constrained="true"  指定在主鍵上添加外鍵約束
         -->
        <one-to-one name="user" class="User" constrained="true"  cascade="save-update"></one-to-one>
            
    </class>
    

</hibernate-mapping>
View Code

  2)組件映射,繼承映射

    類關係分爲:組合關係:,繼承關係;

    1.組件映射:例如Car ,Wheel就屬於組件映射

      主要Car的映射配置:

<component name="wheel" class="Wheel"><property name="count"></property><property name="size"></property></component>

完整例子:

public class Car {

    private int id;
    private String name;
    // 車輪
    private Wheel wheel;
}
// 車輪
public class Wheel {

    private int count;
    private int size;
}
<hibernate-mapping package="cn.itcast.d_component">
    
    <class name="Car" table="t_car">
        <id name="id">
            <generator class="native"></generator>
        </id>    
        <property name="name" length="20"></property>
        
        <!-- 組件映射 -->
        <component name="wheel">
            <property name="size"></property>
            <property name="count"></property>
        </component>
        
                     
    </class>
    

</hibernate-mapping>
View Code

    2.繼承映射

      1)簡單繼承映射:

          直接寫子類映射就能夠了,父類的屬性直接寫上;

          簡單繼承映射,有多少個子類,寫多少個映射文件!

// 動物類
public abstract class Animal {

    private int id;
    private String name;
    
<!-- 
    簡單繼承
 -->
<hibernate-mapping package="cn.itcast.e_extends1">
    
    <class name="Cat" table="t_Cat">
        <!-- 簡單繼承映射: 父類屬性直接寫 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        <property name="catchMouse"></property>                     
    </class>
    

</hibernate-mapping>

@Test
    public void getSave() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 保存
//        Cat cat = new Cat();
//        cat.setName("大花貓");
//        cat.setCatchMouse("抓小老鼠");
//        session.save(cat);
        
        // 獲取時候注意:當寫hql查詢的使用,經過父類查詢必須寫上類的全名
        Query q = session.createQuery("from cn.itcast.e_extends1.Animal");
        List<Animal> list = q.list();
        System.out.println(list);
        
        session.getTransaction().commit();
        session.close();
        
    }
    
View Code

      2)複雜繼承映射

          需求:貓,狗,動物

          1.全部子類映射到一張表

            1)運用場景:子類較多隻有個別屬性;

            2)好處缺點:只有一個映射文件,可是不符合數據庫的設計原則

            3)保存在數據庫中的表樣子:

            4)配置文件:

              <subclass name="Cat" discriminator-value="cat_">

               每一個子類節點都用subclass節點映射,discriminator-value鑑別器字段

<class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <!-- 指定鑑別器字段(區分不一樣的子類) -->
        <discriminator column="type_"></discriminator>
        
        <property name="name"></property>
        
        <!-- 
            子類:貓
                每一個子類都用subclass節點映射
                注意:必定要指定鑑別器字段,不然報錯!
                鑑別器字段:做用是在數據庫中區別每個子類的信息, 就是一個列
            discriminator-value="cat_"
                指定鑑別器字段,即type_字段的值
                若是不指定,默認爲當前子類的全名
         -->
         <subclass name="Cat" discriminator-value="cat_">
             <property name="catchMouse"></property>
         </subclass>
         
         <!-- 
             子類:猴子
          -->
          <subclass name="Monkey" discriminator-value="monkey_">
              <property name="eatBanana"></property>
          </subclass>
        
    </class>
View Code

          2.每一個類映射到一張表【3張表】

            1)配置文件;

              joined-subclass關鍵字表示子類

<class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子類:貓  t_cat
            key 指定_cat表的外鍵字段
        -->
        <joined-subclass name="Cat" table="t_cat">
            <key column="t_animal_id"></key>
            <property name="catchMouse"></property>
        </joined-subclass>
        
        <!-- 子類:猴子  t_monkey -->
        <joined-subclass name="Monkey" table="t_monkey">
            <key column="t_animal_id"></key>
            <property name="eatBanana"></property>
        </joined-subclass>
        
    </class>
View Code

            2)優勢缺點:一個映射文件存儲全部子類;子類父類都有映射;但表結構比較複雜,子類插入一條數據執行2條sql

          3.(推薦)每一個子類映射一張表,父類不對應表(2張表)

            1)配置文件:

              父類設置abstract="true"指定實體對象不對應表,即在數據庫中不生成表

              union-subclass節點存儲子類

<!-- 
         abstract="true"  指定實體類對象不對應表,即在數據庫段不生成表
     -->
    <class name="Animal" abstract="true">
        <!-- 若是用union-subclass節點,主鍵生成策略不能爲自增加! -->
        <id name="id">
            <generator class="uuid"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子類:貓  t_cat
            union-subclass  
                table 指定爲表名, 表的主鍵即爲id列
        -->
        <union-subclass name="Cat" table="t_cat">
            <property name="catchMouse"></property>
        </union-subclass>
        
        <!-- 子類:猴子  t_monkey -->
        <union-subclass name="Monkey" table="t_monkey">
            <property name="eatBanana"></property>
        </union-subclass>
        
    </class>
View Code

 -------------------------------------------------------------------------------------------

1.hibernate查詢

  1)Get/load主鍵查詢

    Dept dept =  (Dept) session.get(Dept.class, 12);

    Dept dept =  (Dept) session.load(Dept.class, 12);

  2)對象導航查詢

Dept dept =  (Dept) session.get(Dept.class, 12);

    System.out.println(dept.getDeptName());

    System.out.println(dept.getEmps());

  3)HQL查詢Hibernate Query language  hibernate 提供的面向對象的查詢語言。

    // 注意:使用hql查詢的時候 auto-import="true" 要設置true,

            若是是false,寫hql的時候,要指定類的全名

        Query q = session.createQuery("from Dept");

      System.out.println(q.list());

查詢的幾種例子:函數查詢,鏈接查詢

// a. 查詢所有列
//        Query q = session.createQuery("from Dept");  //OK
//        Query q = session.createQuery("select * from Dept");  //NOK, 錯誤,不支持*
//        Query q = session.createQuery("select d from Dept d");  // OK
//        System.out.println(q.list());

        // b. 查詢指定的列  【返回對象數據Object[] 】
//        Query q = session.createQuery("select d.deptId,d.deptName from Dept d");  
//        System.out.println(q.list());
        
        // c. 查詢指定的列, 自動封裝爲對象  【必需要提供帶參數構造器】
//        Query q = session.createQuery("select new Dept(d.deptId,d.deptName) from Dept d");  
//        System.out.println(q.list());
        
        // d. 條件查詢: 一個條件/多個條件and or/between and/模糊查詢
        // 條件查詢: 佔位符
//        Query q = session.createQuery("from Dept d where deptName=?");
//        q.setString(0, "財務部");
//        q.setParameter(0, "財務部");
//        System.out.println(q.list());
        
        // 條件查詢: 命名參數
//        Query q = session.createQuery("from Dept d where deptId=:myId or deptName=:name");
//        q.setParameter("myId", 12);
//        q.setParameter("name", "財務部");
//        System.out.println(q.list());
        
        // 範圍
//        Query q = session.createQuery("from Dept d where deptId between ? and ?");
//        q.setParameter(0, 1);
//        q.setParameter(1, 20);
//        System.out.println(q.list());
        
        // 模糊
//        Query q = session.createQuery("from Dept d where deptName like ?");
//        q.setString(0, "%部%");
//        System.out.println(q.list());
        

        // e. 聚合函數統計
//        Query q = session.createQuery("select count(*) from Dept");
//        Long num = (Long) q.uniqueResult();
//        System.out.println(num);
        
        // f. 分組查詢
        //-- 統計t_employee表中,每一個部門的人數
        //數據庫寫法:SELECT dept_id,COUNT(*) FROM t_employee GROUP BY dept_id;
        // HQL寫法
//        Query q = session.createQuery("select e.dept, count(*) from Employee e group by e.dept");
//        System.out.println(q.list());

//1) 內鏈接   【映射已經配置好了關係,關聯的時候,直接寫對象的屬性便可】
//        Query q = session.createQuery("from Dept d inner join d.emps");
        
        //2) 左外鏈接
//        Query q = session.createQuery("from Dept d left join d.emps");

        //3) 右外鏈接
        Query q = session.createQuery("from Employee e right join e.dept");
        q.list();
View Code

迫切鏈接

//1) 迫切內鏈接    【使用fetch, 會把右表的數據,填充到左表對象中!】
//        Query q = session.createQuery("from Dept d inner join fetch d.emps");
//        q.list();
        
        //2) 迫切左外鏈接
        Query q = session.createQuery("from Dept d left join fetch d.emps");
        q.list();
View Code

HQL優化:HQL放到映射文件中

  映射文件:

<!-- 存放sql語句 -->
    <query name="getAllDept">
        <![CDATA[
            from Dept d where deptId < ?
        ]]>
        
    </query>
// HQL 放到映射文件中
        Query q = session.getNamedQuery("getAllDept");
        q.setParameter(0, 10);
        System.out.println(q.list());
View Code

  4)Criteria查詢,徹底面向對象查詢(Query By Criteria  ,QBC)

Criteria criteria = session.createCriteria(Employee.class);
        // 構建條件
        criteria.add(Restrictions.eq("empId", 12));
//        criteria.add(Restrictions.idEq(12));  // 主鍵查詢
View Code

  5)SQLQuery本地查詢

SQLQuery q = session.createSQLQuery("SELECT * FROM t_Dept limit 5;")
            .addEntity(Dept.class);  // 也能夠自動封裝
        System.out.println(q.list());
View Code

  6)分頁:

 Query q = session.createQuery("from Employee");
         
         // 從記錄數
         ScrollableResults scroll = q.scroll();  // 獲得滾動的結果集
         scroll.last();                            //  滾動到最後一行
         int totalCount = scroll.getRowNumber() + 1;// 獲得滾到的記錄數,即總記錄數
         
         // 設置分頁參數
         q.setFirstResult(0);
         q.setMaxResults(3);
         
         // 查詢
         System.out.println(q.list());
         System.out.println("總記錄數:" + totalCount);
View Code

2.Hibernate對鏈接池的支持

  1)hibernate自帶鏈接池和配置文件和c3p0配置

Hibernate 自帶的也有一個鏈接池,且對C3P0鏈接池也有支持!

Hbm 自帶鏈接池:
    只維護一個鏈接,比較簡陋。
    能夠查看hibernate.properties文件查看鏈接池詳細配置:

#################################
### Hibernate Connection Pool ###     
#################################

hibernate.connection.pool_size 1        【Hbm 自帶鏈接池: 只有一個鏈接】



###########################
### C3P0 Connection Pool###           【Hbm對C3P0鏈接池支持】
###########################

#hibernate.c3p0.max_size 2                最大鏈接數
#hibernate.c3p0.min_size 2                最小鏈接數
#hibernate.c3p0.timeout 5000           超時時間
#hibernate.c3p0.max_statements 100     最大執行的命令的個數
#hibernate.c3p0.idle_test_period 3000    空閒測試時間
#hibernate.c3p0.acquire_increment 2     鏈接不夠用的時候, 每次增長的鏈接數
#hibernate.c3p0.validate false
View Code

  2)如何選擇本身的鏈接池:能夠在Hibernate.cfg.xml中配置

<!-- 【鏈接池配置】 -->
        <!-- 配置鏈接驅動管理類 -->
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
        <!-- 配置鏈接池參數信息 -->
        <property name="hibernate.c3p0.min_size">2</property>
        <property name="hibernate.c3p0.max_size">4</property>
        <property name="hibernate.c3p0.timeout">5000</property>
        <property name="hibernate.c3p0.max_statements">10</property>
        <property name="hibernate.c3p0.idle_test_period">30000</property>
        <property name="hibernate.c3p0.acquire_increment">2</property>
View Code

3.二級緩存

  1)引入二級緩存i

    一級緩存Session只在當前的有效,Sesssion關閉就會失效,而二級緩存做用了應用程序,能夠跨越多個session

  2)優勢:享用二級緩存在配置文件中配置下就好,不想用刪了就能夠;也能夠換成本身的緩存框架

  3)配置二級緩存

    hibernate.properties中對二級緩存的配置

##########################
### Second-level Cache ###
##########################

#hibernate.cache.use_second_level_cache false【二級緩存默認不開啓,須要手動開啓】
#hibernate.cache.use_query_cache true      【開啓查詢緩存】

## choose a cache implementation        【二級緩存框架的實現】

#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默認實現
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
View Code

    二級緩存的使用步驟:

    打開二級緩存-》選擇緩存框架-》指定哪些類加入緩存-》測試

    其中指定哪些類加入緩存能夠用緩存策略:

    HQL查詢  【setCacheable  指定從二級緩存找,或者是放入二級緩存】

<class-cache usage="read-only"/>     放入二級緩存的對象,只讀; 
    <class-cache usage="nonstrict-read-write"/>  非嚴格的讀寫
    <class-cache usage="read-write"/>    讀寫; 放入二級緩存的對象能夠讀、寫;
    <class-cache usage="transactional"/>   (基於事務的策略)
View Code

例子:

集合緩存:

<usage="read-wirte" collection="Dept.emps">集合緩存的元素對象也加入二級緩存

查詢緩存:list()默認存入緩存不會存入一級,只能存入二級中

Hibernate.cfg.xml
<!--****************** 【二級緩存配置】****************** -->
        <!-- a.  開啓二級緩存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- b. 指定使用哪個緩存框架(默認提供的) -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
        <!-- 開啓查詢緩存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
        <!-- c. 指定哪一些類,須要加入二級緩存 -->
        <class-cache usage="read-write" class="cn.itcast.b_second_cache.Dept"/>
        <class-cache usage="read-only" class="cn.itcast.b_second_cache.Employee"/>
        <!-- 集合緩存[集合緩存的元素對象,也加加入二級緩存] -->
        <collection-cache usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/>
        
App  測試類

public class App {
    
    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Dept.class)   
            .addClass(Employee.class)   // 測試時候使用
            .buildSessionFactory();
    }
    // 1. 測試二級緩存的使用
    // 沒有/有用 二級緩存
    @Test
    public void testCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // a. 查詢一次
        Dept dept = (Dept) session1.get(Dept.class, 10);
        dept.getEmps().size();// 集合
        session1.getTransaction().commit();
        session1.close();
        
        System.out.println("------");
        
        // 第二個session
        Session session2 = sf.openSession();
        session2.beginTransaction();
        // a. 查詢一次
        dept = (Dept) session2.get(Dept.class, 10);  // 二級緩存配置好; 這裏不查詢數據庫
        dept.getEmps().size();
        
        session2.getTransaction().commit();
        session2.close();
    }
    
    
    @Test
    public void listCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // HQL查詢  【setCacheable  指定從二級緩存找,或者是放入二級緩存】
        Query q = session1.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());
        session1.getTransaction().commit();
        session1.close();
        
        
        Session session2 = sf.openSession();
        session2.beginTransaction();
        q = session2.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());  // 不查詢數據庫: 須要開啓查詢緩存
        session2.getTransaction().commit();
        session2.close();
    }
}
View Code

  4)Session的管理方式:

    能夠sf.openSession()方式建立;

    能夠經過線程的方式建立 :首先要在配置文件中配置:

<property name="hibernate.current_session_context_class">thread</property>

以後:經過sf.getCurrentSession()得到當前的線程的session或者存入session

@Test
    public void testSession() throws Exception {
        //openSession:  建立Session, 每次都會建立一個新的session
        Session session1 = sf.openSession();
        Session session2 = sf.openSession();
        System.out.println(session1 == session2);
        session1.close();
        session2.close();
        
        //getCurrentSession 建立或者獲取session
        // 線程的方式建立session  
        // 必定要配置:<property name="hibernate.current_session_context_class">thread</property>
        Session session3 = sf.getCurrentSession();// 建立session,綁定到線程
        Session session4 = sf.getCurrentSession();// 從當前訪問線程獲取session
        System.out.println(session3 == session4);
        
        // 關閉 【以線程方式建立的session,能夠不用關閉; 線程結束session自動關閉】
        //session3.close();
        //session4.close(); 報錯,由於同一個session已經關閉了!
    }
View Code

4.Hibernatestruts小案例

相關文章
相關標籤/搜索