1. 程式人生 > >Hibernate快速筆記2

Hibernate快速筆記2

上篇:Hibernate快速筆記1

1.Hibernate的查詢方式
2.Hibernate的抓取策略

Hibernate查詢方式

  1. OID檢索:根據ID查詢物件,Get/Load方法

    session.get(Customer.class1L)
    session.load(Customer.class, 2L)
    
  2. 物件導航檢索:根據已查詢物件,獲得其關聯物件,例如使用者和角色

    Customer cus = session.get(Customer.class1L)
    LinkMan lkm = cus.getLinkMan()
    
  3. HQL檢索

    :使用HQL語句,進行查詢;Hibernate Query Language,面向物件的查詢

    • 簡單查詢

      Query query = session.createQuery("from Customer"); //查詢的是類,不是表
          List<Customer> list = query.list();             //list 鏈式顯示獲取
          for (Customer customer : list) {
              System.out.println(customer);
          }
      //支援別名,不支援select *
      session.
      createQuery("select c from Customer c")
    • 排序查詢:支援Order by 屬性名,預設升序

      List<Customer> list = session.createQuery("from Customer order by cust_id desc").list();
      
    • 條件查詢:1.按位置繫結; 2.按型別繫結

      //1.按位置繫結
      Query query = session.createQuery("from Customer where cust_id = ? and cust_name like ?");
      query.setParameter(0, 1L);
      query.setParameter(1, "c%");
      List<Customer> list = query.list();
      //2.按名稱繫結
      //冒號後必須緊跟名字,名字可以任意取定
      Query query2 = session.createQuery("from Customer where cust_id = :id and cust_name like :name");
      query2.setParameter("id", 2L);
      query2.setParameter("name", "c%");
      List<Customer> list2 = query2.list();
      
    • 投影查詢:查詢某些列或某些欄位的行

      //單個屬性,返回的Object
      Query query = session.createQuery("select c.cust_name from Customer c");
      List<Object> list = query.list();
      for (Object object : list) {
          System.out.println(object);
      }
      //多個列,Object陣列
      Query query2 = session.createQuery("select c.cust_id, c.cust_name from Customer c");
      List<Object[]> list2 = query2.list();
      for (Object[] objects : list2) {
          System.out.println(Arrays.toString(objects));
      }
      //多列,封裝成物件
      Query query3 = session.createQuery("select new Customer(c.cust_id, c.cust_name) from Customer c");
      List<Customer> list3 = query3.list();
      
    • 分頁查詢

      Query query = session.createQuery("from LinkMan");
      query.setFirstResult(10);   //limit ?,?
      query.setMaxResults(10);
      
    • 分組統計查詢

      //count,max,min,avg等
      Query query = session.createQuery("select min(cust_id) from Customer");
      Object result = query.uniqueResult();
      System.out.println(result);   //也可以使用list,輸出第一個元素
      //分組-----注意:lkm_cust_id不是LinkMan的屬性,所以選擇不了
      Query query2 = session.createQuery("select customer, count(*) from LinkMan 		group by lkm_cust_id having count(*) > 1");
      List<Object[]> list = query2.list();
      for (Object[] objects : list) {
          System.out.println(Arrays.toString(objects));
      }
      
  4. QBC檢索:query by criteria,適合多條件查詢

    • 簡單查詢

      //使用criteria
      Criteria criteria = session.createCriteria(Customer.class);
      List<Customer> list = criteria.list();
      for (Customer customer : list) {
      	System.out.println(customer);
      }	
      
      //使用criteria ,預設升序
      Criteria criteria = session.createCriteria(Customer.class);
      criteria.addOrder(Order.desc("cust_id"));  //Order為Hibernate物件
      List<Customer> list = criteria.list();
      
    • 條件查詢:add,新增普通條件;addOrder,新增順序;setProjection,新增聚合函式等

      //條件查詢
          Session session = HibernateUtils.getCurrentSession();
          Transaction transaction = session.beginTransaction();
          //條件預設是與,可以改成or
          Criteria criteria = session.createCriteria(Customer.class);
          // eq = ; lt <; gt >; like,and,or,in,between
      //		criteria.add(Restrictions.eq("cust_name", "cus3"));		
      //		criteria.add(Restrictions.le("cust_id", 2L));
      
          //or,兩個條件
          //criteria.add(Restrictions.or(Restrictions.eq("cust_name", "cus3"), Restrictions.le("cust_id", 2L)));
          //or多個條件----可以使用between或者in
          Disjunction disjunction = Restrictions.disjunction();
          disjunction.add(Restrictions.eq("cust_name", "cus3"));
          disjunction.add(Restrictions.eq("cust_name", "cus1"));
          disjunction.add(Restrictions.eq("cust_name", "cus1"));
          criteria.add(disjunction);		
      
          List<Customer> list = criteria.list();
      
    • 分頁查詢

          Criteria criteria = session.createCriteria(Customer.class);
          criteria.setFirstResult(1);
          criteria.setMaxResults(1);
      
          List<Customer> list = criteria.list();
      
    • 分組統計查詢

      Criteria criteria = session.createCriteria(Customer.class);
      criteria.setProjection(Projections.rowCount());
      //Projections.max,min,avg等,還有having,group by等
      Long num = (Long) criteria.uniqueResult();	
      System.out.println(num);
      
  5. QBC離線檢索: 適用於多表、多條件和分頁的查詢情況,一般在SSH模型中使用的到

    離線:DetachedCriteria,脫離session使用;方便在條件查詢時,減輕了JDBC中的引數查詢語句的匹配重複性問題。可以用來在Web層直接操作,不需要在DAO中判斷

    //Web層的操作
        DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
        dc.add(Restrictions.like("cust_name", "cus%"));
    
        //到Service繫結session
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        //執行
        Criteria criteria = dc.getExecutableCriteria(session);
        List<Customer> list = criteria.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
    
  6. HQL的多表查詢:QBC也可以,支援功能一般

    a) SQL的多表查詢方式:連線查詢和子查詢

    • 連線查詢:

      • 交叉查詢——不常用,笛卡爾積

      • 內連線;查詢的結果是兩個表的交集,inner可以省略

        select * from A,B where A.id = B.id 隱式

        select * from A inner join B on A.id = B.id 顯式

      • 外連結:查詢結果是某個表與兩個表的交集的並集

        • 左外連線:左表的全部和兩個表的交集,許多右邊欄位為空的結果
        • 右外連線:右表的全部和兩個表的交集,許多左邊欄位為空的結果
    • 子查詢:多表巢狀查詢,相關子查詢,非相關子查詢

    b) HQL的多表查詢:

    • 連線查詢

      • 交叉連線
      • 內連線:顯式、隱式、迫切
      • 外連結:左外、右外、迫切左外
    • 普通內連線和迫切內連線只有一個關鍵字fetch不同,其傳送的SQL語句和查詢的結果集都是一樣的,fetch是Hibernate的內容,主要用來將返回結果自動封裝成物件;在普通內連線中,會將連線查詢的每一條結果分成兩個物件來封裝(下面示例程式碼中,能看見普通的內連線會查詢兩個表),而迫切內連線會將相同的封裝到一個物件中。

      普通查詢:
      Transaction transaction = session.beginTransaction();
      //SQL的內連線:SELECT * from cst_customer inner join cst_linkman on cst_customer.cust_id = cst_linkman.lkm_cust_id;
      //HQL只需關聯類中對於外來鍵的集合即可
      //查詢結果會匹配到對應的集合中,若去重加上distinct即可
      Query query = session.createQuery("from Customer c inner join c.linkMans");
      List<Object[]> list = query.list();
      for (Object[] objects : list) {
          System.out.println(Arrays.toString(objects));
      }
      

      在console中能看見:

      Hibernate: 
          select
              customer0_.cust_id as cust_id1_0_0_,
              linkmans1_.lkm_id as lkm_id1_1_1_,
              customer0_.cust_name as cust_nam2_0_0_,
              customer0_.cust_source as cust_sou3_0_0_,
              customer0_.cust_industry as cust_ind4_0_0_,
              customer0_.cust_level as cust_lev5_0_0_,
              customer0_.cust_phone as cust_pho6_0_0_,
              customer0_.cust_mobile as cust_mob7_0_0_,
              linkmans1_.lkm_name as lkm_name2_1_1_,
              linkmans1_.lkm_gender as lkm_gend3_1_1_,
              linkmans1_.lkm_phone as lkm_phon4_1_1_,
              linkmans1_.lkm_mobile as lkm_mobi5_1_1_,
              linkmans1_.lkm_email as lkm_emai6_1_1_,
              linkmans1_.lkm_qq as lkm_qq7_1_1_,
              linkmans1_.lkm_position as lkm_posi8_1_1_,
              linkmans1_.lkm_memo as lkm_memo9_1_1_,
              linkmans1_.lkm_cust_id as lkm_cus10_1_1_ 
          from
              cst_customer customer0_ 
          inner join
              cst_linkman linkmans1_ 
                  on customer0_.cust_id=linkmans1_.lkm_cust_id
      
      [Customer [cust_id=1, ...]
      [Customer [cust_id=1, ..., linkMans=[LinkMan [...l]
      ...  //30 行
      
    • 若使用迫切的內連線查詢,則會返回直接封裝好的結果

      //迫切內連線
      Query query = session.createQuery("from Customer c inner join fetch c.linkMans");
      List<Customer> list = query.list();  //能直接獲取到Customer的物件
      for (Customer customer : list) {
          System.out.println(customer);
      }
      

      我們在console能看到的資訊(若想要看到屬性集合,在tostring函式中新增,否則不會出現linkman)

      Hibernate: 
          select
              customer0_.cust_id as cust_id1_0_0_,
              linkmans1_.lkm_id as lkm_id1_1_1_,
              customer0_.cust_name as cust_nam2_0_0_,
              customer0_.cust_source as cust_sou3_0_0_,
              customer0_.cust_industry as cust_ind4_0_0_,
              customer0_.cust_level as cust_lev5_0_0_,
              customer0_.cust_phone as cust_pho6_0_0_,
              customer0_.cust_mobile as cust_mob7_0_0_,
              linkmans1_.lkm_name as lkm_name2_1_1_,
              linkmans1_.lkm_gender as lkm_gend3_1_1_,
              linkmans1_.lkm_phone as lkm_phon4_1_1_,
              linkmans1_.lkm_mobile as lkm_mobi5_1_1_,
              linkmans1_.lkm_email as lkm_emai6_1_1_,
              linkmans1_.lkm_qq as lkm_qq7_1_1_,
              linkmans1_.lkm_position as lkm_posi8_1_1_,
              linkmans1_.lkm_memo as lkm_memo9_1_1_,
              linkmans1_.lkm_cust_id as lkm_cus10_1_1_,
              linkmans1_.lkm_cust_id as lkm_cus10_1_0__,
              linkmans1_.lkm_id as lkm_id1_1_0__ 
          from
              cst_customer customer0_ 
          inner join
              cst_linkman linkmans1_ 
                  on customer0_.cust_id=linkmans1_.lkm_cust_id
      Customer [cust_id=1, ..., linkMans=[LinkMan [...]]
      Customer [cust_id=1, ..., linkMans=[LinkMan [...]]
      ...
      //30行全部的查詢結果
                
      //使用distinct的結果,只有三行
      
      
  7. SQL查詢

    //SQL查詢
        SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
        List<Object[]> list = sqlQuery.list();
    
    //		for (Object[] objects : list) {
    //			System.out.println(Arrays.toString(objects));
    //		}
        //繫結到類---封裝
        sqlQuery.addEntity(Customer.class);
        List<Customer> list2 = sqlQuery.list();
        for (Customer customer : list2) {
            System.out.println(customer);
        }
    

 


 

Hibernate抓取策略

Hibernate提供了一系列的優化方式,來提高封裝性帶來的效能問題,尤其在獲取關聯物件的時候。例如一級二級快取(二級一般被Redis替代)機制、和抓取策略等,都是一些用來優化的手段。抓取策略主要是對查詢時的語句等做的優化處理。

優化的方式:1)快取怎麼處理;2)查詢的優化(抓取)

  1. 延遲載入:懶載入,執行語句的時候,不直接查詢;當使用到關聯的物件才傳送查詢語句。

    • 類級別的延遲載入:如load方法,可以在xml的class配置檔案中的lazy = ‘true’(預設),若配置為false則load方法也是直接載入,不使用延遲載入。

      <class name="com.leehao.hibernateLearning2.domain.Customer" table="cst_customer" lazy="true">
      
      • 類級別延遲載入通過<class>上的lazy進行配置,如果讓lazy失效

        • 將lazy設定為false

        • 將持久化類使用final修飾

        • Hibernate. Initialize()

    • 關聯級別的延遲載入:查詢某個物件,獲取物件的關聯物件時。抓取策略往往會和關聯級別的延遲載入一起使用,優化語句。

  2. 抓取策略:通過類載入其關聯物件時,需要傳送SQL語句,如何傳送、什麼時候傳送,與設定的抓取策略有關。可以通過在<set>和<many-to-one>標籤上的fetch屬性來設定。fetch 屬性和lazy屬性一起使用。

<Set>上的fetch和lazy抓取
  1. Fetch控制抓取策略,控制的是傳送的SQL語句的格式

    • select:預設,傳送普通的select語句,來查詢關聯物件;如根據Customer來查詢聯絡人,會發送多次SQL,每一個物件會查一次————可以使用批量查詢的設定解決
    • join:傳送一條迫切的左外連線查詢關聯的物件,只發送一次查詢SQL,這時lazy的控制沒有作用
    • subselect:傳送一條子查詢來查詢關聯的物件,和select唯一的不同就是使用子查詢;也是兩次SQL,第二次將所有關聯的東西放入一個表。“相當於”使用了一次select和一次join。
  2. lazy:延遲載入,控制查詢關聯物件的時候是否使用延遲

    • true:預設,使用延遲載入查詢關聯物件
    • false:不使用延遲載入
    • extra:“極其懶惰?”
  3. 在實際開發中,一般都採用預設值。如果有特殊的需求,可能需要配置join

    //關聯物件的抓取策略----set中,也就是1的一方,如何查詢它關聯的多的一方的集合
    //預設set中,<set name="linkMans" fetch="select" lazy="true">
    Customer customer = session.get(Customer.class, 1L);
    //獲取的時候才會傳送SQL
    for (LinkMan linkMan : customer.getLinkMans()) {
        System.err.println(linkMan.getLkm_name());
    }
    
    -------------------------------------------------------------
    //lazy的false或者extra
    //<set name="linkMans" fetch="select" lazy="extra">
    Customer customer = session.get(Customer.class, 1L);
    //獲取的時候才會傳送SQL
    System.out.println(customer.getLinkMans().size());
    -------------------------------------------------------------
    /**注:使用extra時,傳送的SQL語句如下
    Hibernate: 
        select
            count(lkm_id) 
        from
            cst_linkman 
        where
            lkm_cust_id =?*/
    
<many-to-one>上的fetch和lazy抓取
  1. fetch

    • select:預設
    • join:迫切的左外連線,此時lazy失效
  2. lazy

    • proxy:預設,proxy具體的取值,取決於另一端的<class>上的lazy的值
    • false
    • no-proxy:不使用
  3. 在實際開發中,一般都採用預設值。如果有特殊的需求,可能需要配置join

    //<many-to-one name="customer" fetch="select" lazy="proxy"  class=...
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
    
        LinkMan linkMan = session.get(LinkMan.class, 1L);
        System.out.println(linkMan.getLkm_name());
    
        System.out.println(linkMan.getCustomer().getCust_name());
    
批量抓取

獲取多個使用者的多個聯絡人的資訊,可以想象成抓取兩個表的內連線的結果。

1的一方中,在Set中配置batch-size=“n”即可,表示每次抓取n個,預設是1;反之,如果使用如LinkMan批量查詢Customer,則需要在Customer中的class配置。

//<set name="linkMans" batch-size="3">
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		List<Customer> list = session.createQuery("from Customer").list();
		for (Customer customer : list) {
			System.out.println(customer.getCust_name());
			for (LinkMan linkMan: customer.getLinkMans()) {
				System.out.println(linkMan.getLkm_name());
			}
		}
---------------------------------------------------------
Hibernate: 
    select
        customer0_.cust_id as cust_id1_0_,
        customer0_.cust_name as cust_nam2_0_,
        customer0_.cust_source as cust_sou3_0_,
        customer0_.cust_industry as cust_ind4_0_,
        customer0_.cust_level as cust_lev5_0_,
        customer0_.cust_phone as cust_pho6_0_,
        customer0_.cust_mobile as cust_mob7_0_ 
    from
        cst_customer customer0_
cus1
Hibernate: 
    select
        linkmans0_.lkm_cust_id as lkm_cus10_1_1_,
        linkmans0_.lkm_id as lkm_id1_1_1_,
        linkmans0_.lkm_id as lkm_id1_1_0_,
        linkmans0_.lkm_name as lkm_name2_1_0_,
        linkmans0_.lkm_gender as lkm_gend3_1_0_,
        linkmans0_.lkm_phone as lkm_phon4_1_0_,
        linkmans0_.lkm_mobile as lkm_mobi5_1_0_,
        linkmans0_.lkm_email as lkm_emai6_1_0_,
        linkmans0_.lkm_qq as lkm_qq7_1_0_,
        linkmans0_.lkm_position as lkm_posi8_1_0_,
        linkmans0_.lkm_memo as lkm_memo9_1_0_,
        linkmans0_.lkm_cust_id as lkm_cus10_1_0_ 
    from
        cst_linkman linkmans0_ 
    where
        linkmans0_.lkm_cust_id in (
            ?, ?, ?
        )