Hibernate

 

基本配置和操做

1)配置hibernate-cfg.xml java

<hibernate-configuration> mysql

    <session-factory> sql

    <!-- hibernate的方言,用來肯定鏈接的數據庫 --> 數據庫

       <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 數組

    <!-- 數據庫的鏈接類 --> 緩存

       <property name="hibernate.connection.driver_class"> 安全

com.mysql.jdbc.Driver session

</property> 併發

    <!-- 數據庫的鏈接字符串和用戶名密碼 --> app

       <property name="hibernate.connection.url">

jdbc:mysql://localhost:3306/itat_hibernate

</property>

       <property name="hibernate.connection.username">root</property>

       <property name="hibernate.connection.password">

123456

</property>

    <!-- 在使用hibernate時會顯示相應的SQL -->

       <property name="show_sql">true</property>

    <!-- 會自動完成類到數據表的轉換 -->

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

    <!-- 加入實體類的映射文件 -->

       <mapping resource="org/zttc/itat/model/User.hbm.xml"/>

       <mapping resource="org/zttc/itat/model/Book.hbm.xml"/>

    </session-factory>

</hibernate-configuration>

2)建立實體映射文件:

(略)

3)代碼編寫:建立SessionFatory

SessionFactory是線程安全的,因此SessionFatory要基於單利模式來建立。

    @Test

    public void test01() {

       Configuration cfg = new Configuration().configure();

       //cfg.buildSessionFactory();

//hibernate3中都是使用該種方法建立,可是在4中被禁用了,使用如下方法

       ServiceRegistry serviceRegistry =

new ServiceRegistryBuilder()

                         .applySettings(cfg.getProperties()).buildServiceRegistry();

       SessionFactory factory =

                       cfg.buildSessionFactory(serviceRegistry);

       Session session = null;

       try {

           session = factory.openSession();

           //開啓事務

           session.beginTransaction();

           User u = new User();

           u.setNickname("張三");

           session.save(u);

           //提交事務

           session.getTransaction().commit();

       } catch (HibernateException e) {

           e.printStackTrace();

           if(session!=null) session.getTransaction().rollback();

       } finally {

           if(session!=null) session.close();

       }

    }

【總結】:

臨時狀態 à 持久狀態:作save操做;

離線狀態 à 臨時狀態:作delete操做;

有時候不知道對象是離線仍是臨時狀態,這時候能夠調用sessionsaveandupdate()方法

Hibernate的狀態

l  Transient:是指臨時狀態,即new出一個對象,可是數據庫中尚未這個對象;

l  Persistentnew一個對象並save後就是Persistent狀態(持久化狀態);

l  Detached:當session關閉而且對象保存在了數據庫中,這時候就是離線狀態。

關於Persistent

    @Test

    public void testTransient() {

       Session session = null;

       try {

           session = HibernateUtil.openSession();

           session.beginTransaction();

           User u = new User();

           u.setBorn(sdf.parse("1976-2-3"));

           u.setUsername("zxl");

           u.setNickname("趙曉六");

           u.setPassword("123");

//以上u就是Transient(瞬時狀態),表示未被session管理且數據庫中沒有

//save以後,被session所管理,且數據庫中已經存在,此時就是Persistent狀態

session.save(u); //u變成持久化狀態,被sessio管理,正常保存

u.setNickname("趙曉其");//更新緩存內容,提交時候更新數據庫

//若是沒有提交那麼上面的(從save開始)就是持久化狀態,session中有完整的對象的//信息處於持久化狀態的對象設置了新數據,提交的時候就會作更新操做

           session.getTransaction().commit();//致使更新操做執行

       } catch (Exception e) {

           e.printStackTrace();

           if(session!=null) session.getTransaction().rollback();

       } finally {

           HibernateUtil.close(session);

       }

    }

繼續解釋:

    @Test

    public void testPersistent02() {

       Session session = null;

       try {

           session = HibernateUtil.openSession();

           session.beginTransaction();

           User u = new User();

           u.setBorn(sdf.parse("1976-2-3"));

           u.setUsername("zxq");

           u.setNickname("趙曉八");

           u.setPassword("123");

//save後緩存(session)保存這個u對象信息

           session.save(u);

           u.setPassword("222");//不會致使緩存中內容變化

           //緩存內容沒有變化,因此不會執行(這部分有些不明白??)

           session.save(u);

           u.setNickname("趙曉吧");

           //下面語句不執行(持久化狀態的update不執行,提交時候纔會執行update

           session.update(u);

           u.setBorn(sdf.parse("1988-12-22"));

           //沒有意義

           session.update(u);

            //執行update

           session.getTransaction().commit();

       } catch (Exception e) {

           e.printStackTrace();

           if(session!=null) session.getTransaction().rollback();

       } finally {

           HibernateUtil.close(session);

       }

    }

load出來的對象也是持久化對象:

    //此時uPersistent

    User u = (User)session.load(User.class, 10);

    @Test

    public void testDetach05() {

       Session session = null;

       try {

           session = HibernateUtil.openSession();

           session.beginTransaction();

           User u = new User();

           //u.setId(110);

           u.setNickname("abc");

           //若是u是離線狀態就執行update操做,若是是瞬時狀態就執行Save操做

           //可是注意:該方法並不經常使用

           session.saveOrUpdate(u);

           session.getTransaction().commit();

       } catch (Exception e) {

           e.printStackTrace();

           if(session!=null) session.getTransaction().rollback();

       } finally {

           HibernateUtil.close(session);

       }

    }

懶加載

load()get()

當使用load加載一個對象的時候,若是取出的對象沒有使用,那麼就不會發sql——這既是Hibernate的「延遲加載(懶加載)」。且,該對象實際上是一個代理對象,該對象中只有一個id的值,若打印該id也不會發sql(不用從數據庫中取)。

    @Test

    public void testLazy02() {

       Session session = null;

       try {

           session = HibernateUtil.openSession();

           User u = (User)session.load(User.class, 1);

           //此時一條sql都沒有發,這就是hibernate的延遲加載

/**延遲加載指的就是,當完成load操做以後,並不會馬山發出sql語句,只有在使用到該對象時纔會發出sql,當完成load以後,u實際上是一個代理對象,這個代理對象中僅僅只有一個id的值*/

           //此時不會發sql

           System.out.println(u.getId());

           //nickname沒有值,必須去數據庫中取。因此會發出sql

           System.out.println(u.getNickname());

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           HibernateUtil.close(session);

       }

    }

【例】:

    @Test

    public void testLazy04() {

       Session session = null;

       try {

           session = HibernateUtil.openSession();

           //get是隻要一執行就會發出sqlget沒有延遲加載

           User u = (User)session.get(User.class, 101);

//get的時候發現數據庫中並無該數據,因此unull,打印u.getId(),會拋出空指針異常,若是是使用load的話,會打印101(代理對象存儲了id的值)

           System.out.println(u.getId());

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           HibernateUtil.close(session);

       }

    }

【例】:

    @Test

    public void testLazy05() {

       Session session = null;

       try {

           session = HibernateUtil.openSession();

           User u = (User)session.load(User.class, 101);

           //因爲id已經存在,因此不會拋出異常

           System.out.println(u.getId());

           //此時會去數據庫中取數據,發現沒有這個對象,可是u並非空,因此會拋出ObjectNotFoundException

           System.out.println(u.getNickname());

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           HibernateUtil.close(session);

       }

    }

開發中的懶加載的問題

【情景演示】:

Dao中的load方法:

public User load(int id) {

    Session session = null;

    User u = null;

    try {

       session = HibernateUtil.openSession();

       u = (User)session.load(User.class, 1);

    } catch (Exception e) {

       e.printStackTrace();

    } finally {

       HibernateUtil.close(session);

    }

    return u; //這裏返回的僅是一個代理對象,且此時session已經關閉

}

控制器中調用這個方法用來在頁面顯示:

@Test

public void testLazyQuestion() {

    UserDao ud = new UserDao();

    /*因爲使用了loadload是有延遲加載的,返回的時候的u是一個代理對象,僅僅只有一個id

     * 可是在返回的時候Session已經被關閉了,此時當須要使用u的其餘屬性時就須要去數據庫中

     * 可是Session關閉了,因此就拋出org.hibernate.LazyInitializationException no session

     */

    User u = ud.load(2);

    System.out.println(u.getUsername());

}

解決方法:

方法1:使用get方法;

方法2:在過濾器中獲取session放到context中,而後在過濾器中關閉。Spring中使用openSessionInView

ID生成策略

<hibernate-mapping package="org.zttc.itat.model">

    <class name="Book" table="t_book">

        <id name="id">

            // assigned:表示要手動指定主鍵

            // uuid:表示自動生成一個uuid字符串,因此主鍵必須是String

            // native:也是自動生成,生成的是123等有序列,因此檢索會快些

           // native的缺點是每次插入一條數據庫都會查詢一下數據庫,開發中查詢

           // 操做會比較多,因此開發中會使用native

            <generator class="uuid"/>

        </id>

        <property name="name"/>

        <property name="price"/>

        <property name="bookPage" column="book_page" type="int"/>

    </class>

</hibernate-mapping>

實體表映射-使用xml

單張表映射

【提示】:

l  實體字段名,好比bookName,映射到數據庫最好映射成book_name

<property name="bookPage" column="book_page" type="int"/>

l  在實際開發中,通常不會使用Hibernate來生成表,通常的開發中,都是首先用PowerDesigner設計好數據庫,而後生成SQL語句,而後用這個SQL語句來生成表。

這樣的好處是,在開發數據庫的時候能夠爲數據庫建立索引和視圖。

l  對效率要求不高的項目適合使用Hibernate

l  對效率比較高的項目,若是使用Hibernate的話,若想要效率也高,則能夠這麼作:增刪改使用Hibernate,而查詢使用原生態的SQL

l  Hibernate語句時,好比清楚一句Hibernate代碼執行了幾條SQL語句,不然性能可能大降!

關係映射

多對一(學生和班級)

l  關係通常是由「多」的一方來維護;

<!-- inverse=true表示不在本身這一端維護關係 -->

<set name="stus" lazy="extra" inverse="true">

    <key column="cid"/>

    <one-to-many class="Student"/>

</set>

l  添加的時候,通常都是先添加「一」的一方;

l  懶加載會多發一條SQL語句(抓取策略能夠只發一條SQL語句……?)

l  關聯(cascade:不要在「多」的一方使用關聯。由於,好比,刪除多的一方的一條記錄,同時會刪除「一」的一方對應的記錄,可是若是「一」的一方的該條記錄又關聯了其餘記錄的話,就不能刪除了。使用cascade的狀況通常是:在「一」的一方刪除時使用,特殊狀況纔會在add上作級聯。

一對多(留言和評論)

        <property name="title"/>

        <property name="content"/>

<!-- 使用了lazy=extra以後會稍微智能一些,會根據去的值的不一樣來判斷是調用count和獲取投影 ,好比取出總條數通常狀況下會取出全部數據的list而後計算listsize,可是用了extra後就會直接count(*)-->

        <set name="comments" lazy="extra">

        <!-- key用來指定在對方的外鍵的名稱 -->

            <key column="mid"/>

        <!-- class用來設置列表中的對象類型 -->

            <one-to-many class="Comment"/>

        </set>

    </class>

雙向關聯(班級和學生)

(略)

實體表映射-使用註解

l  一個Hibernate項目是使用註解仍是Annotation呢?

——小項目使用註解,大項目(一兩百萬行代碼的)使用xml。由於大項目的類比較多,看xml文件更方便。大項目不用外鍵的方式????

多對一(學生和教室)

@Entity

@Table(name="t_classroom")

public class Classroom {

    private int id;

    private String name;

    private int grade;

    private Set<Student> stus;

// mappedBy表示關係由對方的classroom屬性維護,若是沒有mappedBy的話,會生

// 成中間表

    @OneToMany(mappedBy="classroom")

    // 使用extrasql語句會較智能,好比計算條數會自動使用count(*)

    @LazyCollection(LazyCollectionOption.EXTRA)

    public Set<Student> getStus() {

       return stus;

    }

    public void setStus(Set<Student> stus) {

       this.stus = stus;

    }

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }

}

@Entity

@Table(name="t_student")

public class Student {

    private int id;

    private String name;

    private String no;

    private Classroom classroom;

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }

    // fetch的默認值是EAGER,表示不使用懶加載,一條語句就能把相關的數據查詢

    // 出來;若是值設置爲LAZY的話,就會使用懶加載,會多發一條sql

    @ManyToOne(fetch=FetchType.LAZY)

    // 在本身這邊生成的外鍵名是cid,哪一方維護關係就在哪一方加JoinColumn(就是

// 外鍵的意思)通常都由「多」的一方維護關係,即在本身這方配置對方的外鍵。

    @JoinColumn(name="cid") 

    public Classroom getClassroom() {

       return classroom;

    }

}

一對多(學生和教室)

(主要內容就是使用Extra的問題,查詢屬於教室的學生的數目)。

一對一(人和身份證號)

多對多

以上都不須要中間表,而這個「多對多」關係須要中間表。

【提示】:

多對多關係開發中通常不用,而是使用兩個一對多關係來代替。

@Entity

@Table(name="t_admin")

public class Admin {

    private int id;

    private String name;

    private Set<Role> roles;

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }

    // 設置由對方維護關係

    @ManyToMany(mappedBy="admins")

    public Set<Role> getRoles() {

       return roles;

    }

}

@Entity

@Table(name="t_role")

public class Role {

    private int id;

    private String name;

    private Set<Admin> admins;

   

    public Role() {

       admins = new HashSet<Admin>();

    }

    public void add(Admin admin) {

       admins.add(admin);

    }

    @ManyToMany(mappedBy="admins")

    public Set<Role> getRoles() {

       return roles;

    }

   

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }  

    // joinColumns:用來設置本身在中間表的外鍵名;inverseJoinColumns用來設置

    // 對方在中間表的外鍵名

    @ManyToMany

    @JoinTable(name="t_role_admin",

joinColumns={@JoinColumn(name="rid")},

                      inverseJoinColumns={@JoinColumn(name="aid")})

    public Set<Admin> getAdmins() {

       return admins;

    }

    public void setAdmins(Set<Admin> admins) {

       this.admins = admins;

    }

}

專業-教室 -學生

基於Annotation

專業-教室:1對多

教室-學生:1對多

@Entity

@Table(name="t_stu")

//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)

public class Student {

    private int id;

    private String name;

    private String sex;

    private Classroom classroom;

    private int version;

   

    @Version

    public int getVersion() {

       return version;

    }

    public void setVersion(int version) {

       this.version = version;

    }

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }

    //LAZY就是XML中的select,EAGER就表示XML中的join

    @ManyToOne(fetch=FetchType.LAZY)

    @JoinColumn(name="cid")

    public Classroom getClassroom() {

       return classroom;

    }

}

@Entity

@Table(name="t_classroom")

@BatchSize(size=20)

public class Classroom {

    private int id;

    private String name;

    private int grade;

    private Set<Student> stus;

    private Special special;

   

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }  

    //由多的一方維護關係

    @OneToMany(mappedBy="classroom")

    @LazyCollection(LazyCollectionOption.EXTRA)

    @Fetch(FetchMode.SUBSELECT)

    public Set<Student> getStus() {

       return stus;

    }  

    @ManyToOne(fetch=FetchType.LAZY)

    @JoinColumn(name="spe_id")

    public Special getSpecial() {

       return special;

    }

}

@Entity

@Table(name="t_special")

public class Special {

    private int id;

    private String name;

    private String type;

    private Set<Classroom> clas;

   

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }

    @OneToMany(mappedBy="special")

    @LazyCollection(LazyCollectionOption.EXTRA)

    public Set<Classroom> getClas() {

       return clas;

    }

}

基於XML

略,重點提示:

(1)       設置<set>的時候,使用inverse,表示不在本身這邊維護關係,以下配置classroom

<many-to-one name="special" column="spe_id" fetch="join"/>

<set name="stus" inverse="true" lazy="extra" fetch="subselect">

    <key column="cid"/>

    <one-to-many class="Student"/>

</set>

基於HQL的查詢(重要)

/*基於?的條件的查詢,特別注意:jdbc設置參數的最小下標是1hibernate0*/

List<Student> stus = session

.createQuery("from Student where name like ?")

                           .setParameter(0, "%%")

                                   .list();

/*還能夠基於別名進行查詢,使用:xxx來講明別名的名稱,基於列表查詢必定要用別名*/

List<Student> stus = session

.createQuery("from Student where name like :name and sex=:sex")

                  .setParameter("name", "%%")

                       .setParameter("sex", "")

                            .list();

經常使用HQL的查詢(重要)

/*使用uniqueResult能夠返回惟一的一個值注意返回值類型(uniqueResult的返回類型是Object*/

Long stus = (Long)session

.createQuery("select count(*)

 from Student where name like :name and sex=:sex")

                         .setParameter("name", "%%")

                            .setParameter("sex", "")

                                .uniqueResult();

/*基於投影的查詢,經過在列表中存儲一個對象的數組,注意返回值類型

基於投影的查詢還能夠基於DTO——DTO使用來傳輸數據*/

List<Object[]> stus = session

.createQuery("select stu.sex,

count(*) from Student stu group by stu.sex").list();

for(Object[] obj:stus) {

    System.out.println(obj[0]+":"+obj[1]);

}

【個人總結】:

group by的做用,select後的顯示、計算是按照group by後進行的(group by後的記錄至關於一張張字表,對這些字表進行select操做)。

/*若是對象中相應的導航對象,能夠直接導航完成查詢*/

List<Student> stus = session

.createQuery("select stu from Student stu

where stu.classroom.name=? and stu.name like ?")

                           .setParameter(0, "計算機教育班")

.setParameter(1, "%%")

.list();

for(Student stu:stus) {

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

}

/*可使用in來設置基於列表的查詢,此處的查詢須要使用別名進行查詢。

特別注意:使用in的查詢必須在其餘的查詢以後*/

List<Student> stus = session

.createQuery("select stu from Student stu

where stu.name like ? and stu.classroom.id in (:clas)")

                  .setParameter(0, "%%")

.setParameterList("clas", new Integer[]{1,2})

                            .list();

for(Student stu:stus) {

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

}

【提示】:

基於列表查詢要用別名:name

/*能夠經過is null來查詢爲空的對象,sql同樣不能使用=來查詢null的對象*/

List<Student> stus = session

.createQuery("select stu from Student stu

where stu.classroom is null")

                            .setFirstResult(0)

.setMaxResults(15)

                                          .list();

for(Student stu:stus) {

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

}

/*使用對象的導航(內部使用Cross JOIN<笛卡爾積>)能夠完成鏈接,可是是基於Cross JOIN,效率不高,能夠直接使用JOINJoin = Inner Join)來完成鏈接*/

List<Student> stus = ession

.createQuery("select stu from Student stu

left join stu.classroom cla where cla.id=2")

                             .setFirstResult(0)

.setMaxResults(15).list();

for(Student stu:stus) {

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

}

HQL的鏈接查詢(重要)

鏈接(Join)有三種:

(1)      left join

如下sqlt1left)中不符合條件(t1.id=t2.cid)的也打印出來,無值的字段爲null

           select * from t_classroom t1

left join t_stu t2 on(t1.id=t2.cid)

(2)      right join:同理(1

(3)      inner join(即join):兩邊都有的才顯示。

左、右鏈接經常使用語統計數據。

// 如下sql語句顯示不正常,本意是想顯示每一個班級的學生人數,可是沒有學生的班級也會顯示人數:

select t1.name,count(*) from t_classroom t1

join t_stu t2

on(t1.id=t2.cid) group by t1.id

// 改爲如下sql

select t1.name,count(t2.cid) from t_classroom t1

left join t_stu t2

on(t1.id=t2.cid) group by t1.id

l  查詢每一個班級的男生和女生的人數:

select t1.name,t2.sex,count(t2.cid)

from t_classroom t1

left join t_stu t2

on(t1.id=t2.cid)

group by t1.id,t2.sex

【提示】:

Hibernate的導航鏈接內部使用CROSS JOIN(全鏈接),效率很低!

l  查詢2班的全部學生:

/*使用對象的導航能夠完成鏈接,可是是基於Cross JOIN,效率不高,能夠直接使用JOIN來完成鏈接*/

List<Student> stus = session

                  .createQuery("select stu from Student stu

left join stu.classroom cla where cla.id=2").setFirstResult(0)

.setMaxResults(15).list();

for(Student stu:stus) {

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

}

l  查詢每一個班的人數

/*使用對象的導航能夠完成鏈接,可是是基於Cross JOIN,效率不高,能夠直接使用JOIN來完成鏈接

思考:若是把classroom放前面呢?若是使用count(*)會如何?*/

List<Object[]> stus = session

                  .createQuery("select

cla.name,count(stu.classroom.id)

from Student stu

right join stu.classroom cla

group by cla.id").list();

for(Object[] stu:stus) {

System.out.println(stu[0]+","+stu[1]);

}

l  查詢學生的信息、包括所在班級(班級名)、專業名

SQL_1

List<Ojbet[]> stus =

session.createQuery(「select stu.id,stu.name,stu.sex,cla.name,spe.name

from Student stu

left join stu.classroom cla

left join cla.special spe」).list();

for( Object[] obj:stus ){

    ……

}

以前查詢的字段,而後獲取信息的時候,都是用Object[]接收返回值而後經過Object的索引獲取索引值的可是實際開發中若是這樣作就很不方便(如SQL_1,由於一一的取出索引值。這時候就可使用DTO了,專門用來傳輸數據,沒有任何存儲意義。

>>>使用DTO解決以上問題:

新建一個DTO對象,要從數據庫查詢的哪些字段名就添加哪些屬性名:

public class StudentDto {

    private int sid;

    private String sname;

    private String sex;

    private String cname;

    private String spename;

    get()…;

    set()…;

}

HQL查詢:

/*直接可使用new XXObject完成查詢,注意,必定要加上Object的完整包名這裏使用的new XX,必須在對象中加入相應的構造函數*/

List<StudentDto> stus = session

                  .createQuery("select

new org.zttc.itat.model.StudentDto(

stu.id as sid,stu.name as sname,stu.sex as

sex,cla.name as cname,spe.name as spename)

                    from Student stu

left join stu.classroom cla

left join cla.special spe").list();

for(StudentDto stu:stus) {        

System.out.println(stu.getSid()

+","+stu.getSname()+","+stu.getSex()+","

+stu.getCname()+","+stu.getSpename());

}

【提示】:

Criteria基本上在開發中不用!(咱們項目組居然用了……)

l  統計每一個專業的學生的人數:

List<Object[]> stus = session.createQuery("

select spe.name,count(stu.classroom.special.id)

from Student stu

right join

stu.classroom.special spe

group by spe");

l  統計人數大於150的專業:

/*having是爲group來設置條件的*/

List<Object[]> stus = session.createQuery("

select spe.name,

(count(stu.classroom.special.id))

from Student stu

right join

stu.classroom.special spe

group by spe

having count(stu.classroom.special.id)>150").list();

for(Object[] obj:stus) {

    System.out.println(obj[0]+":"+obj[1]);

}

l  統計每一個專業的男女生人數:

/*having是爲group來設置條件的*/

List<Object[]> stus = session.createQuery("

select stu.sex,spe.name

(count(stu.classroom.special.id))

from Student stu

right join

stu.classroom.special spe

group by spe,stu.sex").list();

for(Object[] obj:stus) {

    System.out.println(obj[0]+":"+obj[1]+","+obj[2]);

}

抓取策略(XML-對多的一方抓取)

基本文件

使用映射文件的狀況下:

/*

(1)默認狀況會發出3SQL語句,一條取student,一條取Classroom,一條取Special

(3)經過設置XML中的<many-to-one name="classroom" column="cid" fetch="join"/>能夠完成對抓取的設置

(4)若是使用Annotation的話,Hibernate會自動使用Join查詢,只發出一條SQL

使用Annotation默認就是基於join抓取的,因此只會發出一條sql*/

session = HibernateUtil.openSession();

Student stu = (Student)session.load(Student.class, 1);  System.out.println(stu.getName()+","+stu.getClassroom().getName()+","+stu.getClassroom().getSpecial().getName());

若是不想發出3SQL,能夠在映射文件中這麼配置,使用fetch=」join」(默認值是select

<class name="Student" table="t_stu">

    <!-- <cache usage="read-only"/> -->

    <id name="id">

        <generator class="native"/>

    </id>

     <version name="version"/>

    <property name="name"/>

    <property name="sex"/>

    <many-to-one name="classroom" column="cid" fetch="join"/>

</class>

專業是經過classroom來獲取的,因此,同時要在classroom中配置fetch=」join」

<class name="Classroom" table="t_classroom">

    <id name="id">

        <generator class="native"/>

    </id>

    <property name="name"/>

    <property name="grade"/>

    <many-to-one name="special" column="spe_id" fetch="join"/>

    <set name="stus" inverse="true" lazy="extra" fetch="subselect">

        <key column="cid"/>

        <one-to-many class="Student"/>

    </set>

</class>

【警告】:

以上使用fetch=」join」會有這樣的問題,就是,即便我query中不查詢班級和專業,發出的SQL也會去查專業和學生!也就是說,使用了fetch=」join」後,延遲加載就不生效了!

/*使用fetch=join雖然能夠將關聯對象抓取,可是若是不使用關聯對象也會一併查詢出來這樣會佔用相應的內存*/

session = HibernateUtil.openSession();

Student stu = (Student)session.load(Student.class, 1);

//使用fetch=」join」,延遲加載就失效

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

【對於Annotation的問題】:

由於在使用Annotation的狀況下,默認就是基於join的抓取策略,因此,好比,咱們只查學生的話,同時會把班級、專業信息都查出來,即把關聯的信息都查出來。

實例以及解決方案

【基於「多」的一方進行抓取:實例及解決方案】

session = HibernateUtil.openSession();

/**

 * XML中配置了fetch=join僅僅只是對load的對象有用,對HQL中查詢的對象無用,

 * 因此此時會發出查詢班級的SQL,解決的這個SQL的問題有兩種方案,

 * 1)設置對象的抓取的batch-size

 * 2)在HQL中使用fecth來指定抓取

 * 特別注意,若是使用了join fetch就沒法使用count(*)

 */

List<Student> stus = session.

createQuery("select stu from Student stu).list();

for(Student stu:stus) {

    System.out.println(stu.getName()+","+stu.getClassroom());

}

XML配置:

// batch-size:值默認爲1,這裏設置成20,即一次抓取20個班;

// 缺點是佔用內存較大,且session關閉後數據就丟失(??),這裏能夠看出

// Hibernate的一個缺點就是很容易影響性能

<class name="Classroom" table="t_classroom" batch-size="20 ">

    <id name="id">

        <generator class="native"/>

    </id>

    <property name="name"/>

    <property name="grade"/>

    <many-to-one name="special" column="spe_id" fetch="join"/>

    <set name="stus" inverse="true" lazy="extra" fetch="subselect">

        <key column="cid"/>

        <one-to-many class="Student"/>

    </set>

</class>

爲了解決batch-size的問題,咱們能夠在xml中不使用batch-size,在HQL使用fetchy來指定抓取

// 沒有fetch進行查詢會發出多條sql,有了fetch後只發一條

List<Student> stus = session.

createQuery("select stu from

Student stu join fetch stu.classroom").list();

for(Student stu:stus) {

    System.out.println(stu.getName()+","+stu.getClassroom());

}

【提示】:

由於HQL使用了join fetch,就沒法使用count(*)了,若是還想使用count(*),能夠將HQL格式化,將fetch替換成空。

【小結】:

抓取策略有兩種:

(1)       batch-size:在被抓取方的xml中配置,能夠一次抓取大量的數據;

(2)       HQL中使用fetch

抓取策略(XML-對一的一方抓取)

針對「一」的抓取通常不會使用雙向關聯,只會作「單向關聯」。

【實例1

l  取出班級和班級對應的學生

session = HibernateUtil.openSession();

Classroom cla = (Classroom)session.load(Classroom.class, 1);

/*此時會在發出一條SQLclass對象*/

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

/*取學生姓名的時候會再發一條SQL取學生對象,能夠在Classroom的配置文件中對學生屬性添加fetch=」join」,就會用一條sql完成相關查詢了*/

for(Student stu:cla.getStus()) {

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

}

classroomxml配置文件中加入fetch=join

<class name="Classroom" table="t_classroom">

    <id name="id">

        <generator class="native"/>

    </id>

    <property name="name"/>

    <property name="grade"/>

    <many-to-one name="special" column="spe_id" fetch="join"/>

    <set name="stus" inverse="true" lazy="extra" fetch="join">

        <key column="cid"/>

        <one-to-many class="Student"/>

    </set>

</class>

【實例2

l  取出一組班級

classroomxml配置仍是使用實例1中的配置內容。)

List<Classroom> clas = session.createQuery("from Classroom").list();

for(Classroom cla:clas) {

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

/*對於經過HQL取班級列表且獲取相應學生列表時,fecth=join就無效了,這裏,每次查詢一個班級就會發出一個SQL語句,這樣確定有問題,解決方法:

1)第一種方案能夠設置setbatch-size來完成批量的抓取

2)能夠設置fetch=subselect,使用subselect會完成根據查詢出來的班級進行一次對學生對象的子查詢(推薦)*/

    for(Student stu:cla.getStus()) {

       System.out.print(stu.getName());

    }

}

l  方法(1)的xml配置:

<class name="Classroom" table="t_classroom">

    <id name="id">

        <generator class="native"/>

    </id>

    <property name="name"/>

    <property name="grade"/>

    <many-to-one name="special" column="spe_id" fetch="join"/>

    <set name="stus" inverse="true" lazy="extra" fetch="join" batch-size="400">

        <key column="cid"/>

        <one-to-many class="Student"/>

    </set>

</class>

【說明】:

這裏設置batch-size=400後,雖然發出的SQL大大減小,可是,即便咱們實際使用10條,Hibernate也會查詢出400條,這樣很耗內存!(實際怎麼使用仍是要根據項目實際狀況來定)。

l  方法(2)的xml配置:

<class name="Classroom" table="t_classroom">

    <id name="id">

        <generator class="native"/>

    </id>

    <property name="name"/>

    <property name="grade"/>

    <many-to-one name="special" column="spe_id" fetch="join"/>

    <set name="stus" inverse="true" lazy="extra" fetch="subselect" >

        <key column="cid"/>

        <one-to-many class="Student"/>

    </set>

</class>

【說明】:

這中方法只發出兩條sql:查班級和查學生(subselect)。

另:這裏好像<many-to-one>fetch只有兩個選項:joinselect,而<set>fetch多一個subselect

抓取策略(註解-對多的一方抓取)

Student實體配置:

@Entity

@Table(name="t_stu")

//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)

public class Student {

    private int id;

    private String name;

    private String sex;

    private Classroom classroom;

    private int version;

   

    @Version

    public int getVersion() {

       return version;

    }

    public void setVersion(int version) {

       this.version = version;

    }

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }

   

    //LAZY就是XML中的select,EAGER就表示XML中的join

    @ManyToOne(fetch=FetchType.LAZY)

    @JoinColumn(name="cid")

    public Classroom getClassroom() {

       return classroom;

    }

}

@Entity

@Table(name="t_classroom")

@BatchSize(size=20)

public class Classroom {

    private int id;

    private String name;

    private int grade;

    private Set<Student> stus;

    private Special special;

   

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }

    @OneToMany(mappedBy="classroom")

    @LazyCollection(LazyCollectionOption.EXTRA)

    @Fetch(FetchMode.SUBSELECT)

    public Set<Student> getStus() {

       return stus;

    }

    //使用註解默認都是join查詢的,因此這裏的配置默認是:

    //fetch=FetchType.EAGER因此這裏默認會將專業信息也查詢出來

    //因此這裏要配置成LAZY

    //可是,若是配置成LAZY後,若是去班級再取專業,就會多發一條SQL查詢專業

    //這個的解決方法有有兩種:

//1)在HQL中使用fetch join cla.special

//2

    @ManyToOne(fetch=FetchType.LAZY)

    @JoinColumn(name="spe_id")

    public Special getSpecial() {

       return special;

    }

}

方法(1)的HQL

/* 基於Annotation因爲默認的many-to-one的抓取策略是EAGER的,因此當抓取classroom時會自動發出多條SQL去查詢相應的Special;

此時:

1)能夠經過join fecth繼續完成對關聯的抓取(這樣SQL很容易變得很長,尤爲是大項目中)

2)直接將關聯對象的fecth設置爲LAZY(就是說不但願查出關聯對象),可是(因此)使用LAZY所帶來的問題是在查詢關聯對象時須要發出相應的SQL,不少時候也會影響效率(要查出關聯對象的話仍是要發出SQL的)

(效率是第一重要的!)*/

List<Student> stus = session.createQuery("select stu from " +

       "Student stu join fetch stu.classroom cla join fetch cla.special").list();

for(Student stu:stus) {

    System.out.println(stu.getName()+","+stu.getClassroom());

}

抓取策略(註解-對一的一方抓取)

【實例1

session = HibernateUtil.openSession();

Classroom cla = (Classroom)session.load(Classroom.class, 1);

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

/*此時會在發出兩條SQL(取班級、取學生)取學生對象*/

for(Student stu:cla.getStus()) {

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

}

只發出一條SQL的方法,配置classroom實體:

@Entity

@Table(name="t_classroom")

@BatchSize(size=20)

public class Classroom {

    private int id;

    private String name;

    private int grade;

    private Set<Student> stus;

    private Special special;

   

    @Id

    @GeneratedValue

    public int getId() {

       return id;

    }  

    @OneToMany(mappedBy="classroom")

    @LazyCollection(LazyCollectionOption.EXTRA)

    @Fetch(FetchMode.JOIN) //方法1:使用join

@Fetch(FetchMode.SUBSELECT) //方法2:使用subselect(同時會查專業,另外,即便只查一個班級也會發出兩條sql語句進行查班級和查學生)

    public Set<Student> getStus() {

       return stus;

    }

    @ManyToOne(fetch=FetchType.LAZY) //使用lazy不關聯不查詢

    @JoinColumn(name="spe_id")

    public Special getSpecial() {

       return special;

    }

}

緩存(基於XML)(問題較多)

(一級緩存)N+1問題

實例1:基本查詢:

/*此時會發出一條sql取出全部的學生信息*/

session = HibernateUtil.openSession();

List<Student> ls = session.createQuery("from Student")

       .setFirstResult(0).setMaxResults(50).list();

Iterator<Student> stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

實例2:使用Iterator展現N+1問題:

/*若是使用iterator方法返回列表,對於hibernate而言,它僅僅只是發出取id列表的sql。在查詢相應的具體的某個學生信息時,會發出相應的SQL去取學生信息這就是典型的N+1問題。

存在iterator的緣由是,有可能會在一個session中查詢兩次數據,若是使用list每一次都會把全部的對象查詢上來,而使用iterator僅僅只會查詢id,此時全部的對象已經存儲在一級緩存(session的緩存)中,能夠直接獲取*/

session = HibernateUtil.openSession();

Iterator<Student> stus = session.createQuery("from Student")

       .setFirstResult(0).setMaxResults(50).iterate();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

實例3:用Iterator從一級緩存(session)中取值

/*此時會發出一條sql取出全部的學生信息*/

//發出查classsql

session = HibernateUtil.openSession();

List<Student> ls = session.createQuery("from Student")

       .setFirstResult(0).setMaxResults(50).list();

Iterator<Student> stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

/*使用iterate僅僅只會取Studentid,此時Student的數據已經在緩存中,因此不會在出現N+1*/

//發出查idsql

stus = session.createQuery("from Student")

       .setFirstResult(0).setMaxResults(50).iterate();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

實例4:實例3的變形(注意和實例3對比)

session = HibernateUtil.openSession();

List<Student> ls = session.createQuery("from Student")

       .setFirstResult(0).setMaxResults(50).list();

Iterator<Student> stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

/*會發出SQL取完整的學生對象,佔用內存相對較多*/

ls = session.createQuery("from Student")

       .setFirstResult(0).setMaxResults(50).list();

stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

【小結】:

(1)       屢次在session中查詢使用Iterato(此狀況不多!);

(2)       一級緩存釋義;

session = HibernateUtil.openSession();

List<Student> ls = session.createQuery("from Student")

       .setFirstResult(0).setMaxResults(50).list();

Iterator<Student> stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

/*id=1Student對象已經在session的緩存(一級緩存)中,此時就不會發sql去取Student*/

Student stu = (Student)session.load(Student.class, 1);

System.out.println(stu.getName()+",---");

(3)       一級緩存中session關閉,一級緩存就關閉。

二級緩存

【提示】:

(1)       二級緩存是sessionFactory級別的緩存;

(2)       二級緩存是優化比較好、使用比較多的緩存。

實例1:演示一級緩存

@SuppressWarnings("unchecked")

public class TestCache {

    @Test

    public void test01() {

       Session session = null;

       try {

           /*此時會發出一條sql取出全部的學生信息*/

           session = HibernateUtil.openSession();

           List<Student> ls = session.createQuery("from Student")

                  .setFirstResult(0).setMaxResults(50).list();

           Iterator<Student> stus = ls.iterator();

           for(;stus.hasNext();) {

              Student stu = stus.next();

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

           }

/*id=1Student對象已經在session的緩存(一級緩存)中,此時就不會發sql去取Student*/

           Student stu = (Student)session.load(Student.class, 1);

           System.out.println(stu.getName()+",---");

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           HibernateUtil.close(session);

       }

       try {

           session = HibernateUtil.openSession();

/*上一個Session已經關閉,此時又得從新取Student,這裏會再發一條sql*/

           Student stu = (Student)session.load(Student.class, 1);

           System.out.println(stu.getName()+",---");

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           HibernateUtil.close(session);

       }

    }

實例2:使用二級緩存

使用二級緩存步驟:

步驟1要在配置文件(Hibernate.cfg.xml)中配置,打開二級緩存。

<!-- 設置二級緩存爲true -->

<property name="hibernate.cache.use_second_level_cache">

true

</property>

步驟2加入二級緩存包(經常使用的是ehache-corteHibernate-ehcache,在Hibernateoptional目錄下);

步驟3要在配置二級緩存提供的類:

<!-- 設置二級緩存所提供的類 -->

<property name="hibernate.cache.provider_class">

net.sf.ehcache.hibernate.EhCacheProvider

</property>

步驟4Hibernate4.0中還要配置工廠(目的是爲了提升效率):

<!-- hibernate4.0以後須要設置facotory_class -->

<property name="hibernate.cache.region.factory_class">

org.hibernate.cache.ehcache.EhCacheRegionFactory

</property>

步驟5設置ehcache.xml文件,在該文件中配置二級緩存的參數:

Hibernateproject目錄中拷貝ehcache.xml配置文件到src目錄下

<ehcache>

<diskStore path="java.io.tmpdir"/>

<!—默認緩存 ->

    <defaultCache

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="120"

        timeToLiveSeconds="120"

        overflowToDisk="true"

        />

   <!-- 每個獨立的cache能夠單獨爲不一樣的對象進行設置

沒有配置的就使用默認的->

    <cache name="org.zttc.itat.model.Student"

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="300"

        timeToLiveSeconds="600"

        overflowToDisk="true"

        />

    <cache name="sampleCache2"

        maxElementsInMemory="1000"

        eternal="true"

        timeToIdleSeconds="0"

        timeToLiveSeconds="0"

        overflowToDisk="false"

        /> -->

</ehcache>

步驟6Hibernate.cfg.xml中配置ehcache.xml配置文件的路徑

<!-- 說明ehcache的配置文件路徑 -->

<propertyname="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>

步驟7開啓二級緩存

<hibernate-mapping package="org.zttc.itat.model">

    <class name="Student" table="t_stu">

        //這個使用了鎖的機制。也能夠是read-write,可是效率更低了。

<cache usage="read-only"/>

        <id name="id">

            <generator class="native"/>

        </id>

         <version name="version"/>

        <property name="name"/>

        <property name="sex"/>

        <many-to-one name="classroom" column="cid" fetch="join"/>

    </class>

</hibernate-mapping>

二級緩存使用實例:

/*此時會發出一條sql取出全部的學生信息*/

           session = HibernateUtil.openSession();

           List<Student> ls = session.createQuery("from Student")

                  .setFirstResult(0).setMaxResults(50).list();

           Iterator<Student> stus = ls.iterator();

           for(;stus.hasNext();) {

              Student stu = stus.next();

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

           }

/*id=1Student對象已經在session的緩存(一級緩存)中,此時就不會發sql去取Student*/

           Student stu = (Student)session.load(Student.class, 1);

           System.out.println(stu.getName()+",---");

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           HibernateUtil.close(session);

       }

       try {

//Student配置了二級緩存了,因此還能從緩存中取出數據

           session = HibernateUtil.openSession();

/*上一個Session已經關閉,此時又得從新取Student*/

           Student stu = (Student)session.load(Student.class, 1);

//由於二級緩存設置了read-only的話,若改變值stu.setName(「」)會報錯

           System.out.println(stu.getName()+",---");

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           HibernateUtil.close(session);

       }

l  二級緩存緩存的是對象

二級緩存是把全部的對象都緩存到內存中,是基於對象的緩存。

【實例】:

try {

/*此時會發出一條sql取出全部的學生信息*/

session = HibernateUtil.openSession();

List<Object[]> ls = session

.createQuery("select stu.id,stu.name from Student stu")

           .setFirstResult(0).setMaxResults(50).list();

} catch (Exception e) {

    e.printStackTrace();

} finally {

    HibernateUtil.close(session);

}

try {

    session = HibernateUtil.openSession();

    session.beginTransaction();

/*以上代碼僅僅取了idname,而二級緩存是緩存對象的,因此上一段代碼不會

將對象加入二級緩存此時就是發出相應的sql*/

Student stu = (Student)session.load(Student.class, 1);

//會報錯,由於二級緩存設置爲read-only

//stu.setName("abc");

System.out.println(stu.getName()+",---");

session.getTransaction().commit();

}

l  二級緩存和Iterator配合使用

Iterator的最主要做用就是在此了!

Session session = null;

try {

/*此時會發出一條sql取出全部的學生信息*/

    session = HibernateUtil.openSession();

    List<Object[]> ls = session

.createQuery("select stu from Student stu")

                     .setFirstResult(0).setMaxResults(50).list();

} catch (Exception e) {

    e.printStackTrace();

} finally {

    HibernateUtil.close(session);

}

try {

    session = HibernateUtil.openSession();

/*因爲學生的對象已經緩存在二級緩存中了,此時再使用iterate來獲取對象的時候,首先會經過一條取id的語句,而後在獲取對象時去二級緩存中,若是發現就不會再發SQL,這樣也就解決了N+1問題並且內存佔用也很少*/

    Iterator<Student> stus = session.createQuery("from Student")

           .setFirstResult(0).setMaxResults(50).iterate();

    for(;stus.hasNext();) {

       Student stu = stus.next();

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

    }

} catch (Exception e) {

}

l  若是使用list()若是想少發SQL就須要使用查詢緩存:

session = HibernateUtil.openSession();

/*這裏發出兩條如出一轍的sql,此時若是但願不發sql就須要使用查詢緩存*/

List<Student> ls = session

.createQuery("select stu from Student stu")//HQL和上面的同樣

                     .setFirstResult(0).setMaxResults(50).list();

Iterator<Student> stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

查詢緩存是針對HQL語句的緩存,查詢緩存僅僅只會緩存id而不會緩存對象。

使用查詢緩存:

Hibernate.cfg.xml中配置查詢緩存:

<!--設置相應的查詢緩存 -->

<property name="hibernate.cache.use_query_cache">true</property>

l  代碼中的用法

【實例1】:

session = HibernateUtil.openSession();

List<Student> ls = session.createQuery("from Student")

    .setCacheable(true)//開啓查詢緩存,查詢緩存也是SessionFactory級緩存

       .setFirstResult(0).setMaxResults(50).list();

Iterator<Student> stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

/*會發出SQL取完整的學生對象,佔用內存相對較多*/

ls = session.createQuery("from Student")

       .setCacheable(true)

       .setFirstResult(0).setMaxResults(50).list();

stus = ls.iterator();

for(;stus.hasNext();) {

    Student stu = stus.next();

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

}

【實例2】:

session = HibernateUtil.openSession();

List<Student> ls = session

       .createQuery("from Student where name like ?")

    .setCacheable(true)

    // 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存

    .setParameter(0, "%%").setFirstResult(0).setMaxResults(50)

           .list();

    Iterator<Student> stus = ls.iterator();

    for (; stus.hasNext();) {

       Student stu = stus.next();

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

    }

} catch (Exception e) {

    e.printStackTrace();

} finally {

    HibernateUtil.close(session);

}

session = null;

try {

/*若是兩條sql不同,就不會開啓查詢緩存,查詢緩存緩存的是HQL語句只有兩個HQL徹底一致(且參數也要一致)才能使用查詢緩存*/

session = HibernateUtil.openSession();

List<Student> ls = session

       .createQuery("from Student where name like ?")

    .setCacheable(true)

    // 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存

    .setParameter(0, "%%").setFirstResult(0).setMaxResults(50)

       .list();

Iterator<Student> stus = ls.iterator();

for (; stus.hasNext();) {

    Student stu = stus.next();

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

}

【實例3】:

/*查詢緩存緩存的不是對象而是id,關閉二級緩存後很容易發出大量的sql*/

session = HibernateUtil.openSession();

List<Student> ls = session

       .createQuery("from Student where name like ?")

       .setCacheable(true)

       // 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存

       .setParameter(0, "%%").setFirstResult(0).setMaxResults(50)

           .list();

    Iterator<Student> stus = ls.iterator();

    for (; stus.hasNext();) {

       Student stu = stus.next();

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

    }

} catch (Exception e) {

    e.printStackTrace();

} finally {

    HibernateUtil.close(session);

}

session = null;

try {

/*查詢緩存緩存的是id,此時因爲在緩存中已經存在了這樣的一組學生數據,可是僅僅只是緩存了id,因此此處會發出大量的sql語句根據id取對象,這也是發現N+1問題的第二個緣由 因此若是使用查詢緩存必須開啓二級緩存*/

session = HibernateUtil.openSession();

List<Student> ls = session

       .createQuery("from Student where name like ?")

       .setCacheable(true)

       // 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存

       .setParameter(0, "%%").setFirstResult(0).setMaxResults(50)

       .list();

Iterator<Student> stus = ls.iterator();

for (; stus.hasNext();) {

    Student stu = stus.next();

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

}

緩存(基於註解)

開啓查詢緩存

配置實體中這麼配置,其餘的查詢語句跟在xml中同樣:

@Entity

@Table(name="t_stu")

@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)

public class Student {

    private int id;

    private String name;

    private String sex;

    private Classroom classroom;

    private int version;

   

    @Version

    public int getVersion() {

       return version;

    }

【小結】:

l  常常修改的對象不用二級緩存;

l  list仍是Iterator?若是對象取出來就用Iterator沒有的話;

l  查詢緩存緩存的是id,可是隻有在hql是同樣的狀況下才使用查詢緩存,因此通常不建議使用查詢緩存;

併發

【併發情景演示】:

test01()方法中將id1的學生取出來,並將名字修改成」Jack」,在test02()方法中,也將id1的學生取出來,並將名字改成」Tom」,而且在對方commit以前完成更名操做,這樣的話,名字不會被改掉。

因此,通常狀況下,併發會致使更新丟失

解決方法有兩種:

(1)      悲觀鎖:

悲觀鎖Hibernate基於數據庫的機制實現的,但是在Hibernate3Hibernate4它們的實現機制是不同的——Hibernate3是基於同步的機制實現的(同步的大量的併發就容易致使效率的問題),因此在Hibernate4中就沒有使用同步,若是兩個同時修改同一個記錄的話,就拋出異常。解釋錯誤了,Hibernate3Hibernate4的機制是同樣的,都是同步的。

(2)      樂觀鎖:

樂觀鎖是在數據庫中增長一個version的字段來實現的,每次修改都會讓這個字段的數字增長1,在讀取的時候根據Version這個版本的值來讀取,這樣若是併發修改就會拋異常。

<class name="Student" table="t_stu">

    <cache usage="read-only"/>

    <id name="id">

        <generator class="native"/>

</id>

//數據庫會增長一個version這個字段

    <version name="version"/>

    <property name="name"/>

    <property name="sex"/>

    <many-to-one name="classroom" column="cid" fetch="join"/>

</class>

Hibernate提升效率的一種方案(重點!)

(1)      在作關係時,儘量使用單向關聯,不要使用雙向關聯;

(2)      在大項目中(數據量超過百萬條)使用Hibernate能夠考慮如下幾個原則(我的總結):

【原則1】:不要使用對象關聯,儘量使用冗餘字段來替代外鍵(由於百萬級別的數據使用跨表查速度會很是慢);

【基於冗餘的關聯-實例】:

l  上面的實例中,咱們的學生表是和班級表關聯的,可是,實際上,顯示學生的信息時,通常都只是但願顯示班級名稱便可,並不須要更多的班級信息。

【設置冗餘字段的方法:】

1) 給班級增長一個用當前時間毫秒級+隨機數做爲班級的主鍵;

2) 咱們給Student實體增長班級編號(classBh班級名(className這兩個特冗餘字段;這樣咱們在顯示班級名的時候就不須要關聯班級表了;

3) 一樣,學生還須要專業信息,咱們就再在Student實體中增長專業編號(speBh)和專業名稱(speName),同時,班級實體中也增長專業的編號和專業的名稱;

4) 使用冗餘的缺點:好比,咱們修改了班級名稱的時候,學生表裏的班級名稱也要跟着作修改(可是性能基本沒有影響)

Q:可是,若是有一個對象同時須要學生對象、班級對象、專業對象,那又怎麼辦呢?

——答案是使用DTO(數據傳輸對象)。

public class StudentDto {

  private Student stu;

  private Classroom cla;

  private Special spe;

}

【原則2】:不使用HQL,而所有使用SQL;若是須要緩存,就使用本身的緩存,而不適用Hibernate的緩存(Hibernate的緩存在SessionSessionFatory會有耗內存)  

(3)      

使用Hibernate SQL查詢

【實例1】:

public void test01() {

    Session session = null;

    try {

       session = HibernateUtil.openSession();

       List<Student> stus = session

.createSQLQuery("select * from t_stu where name like ?")

           .addEntity(Student.class)

           .setParameter(0, "%%")

           .setFirstResult(0).setMaxResults(10)

           .list();

       for(Student stu:stus) {

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

       }

    } catch (Exception e) {

       e.printStackTrace();

    } finally {

       HibernateUtil.close(session);

    }

}

【實例2】:將查詢結果映射到DTO

session = HibernateUtil.openSession();

//注意SQL語句上加了{},返回值用Object來接收,實例3中使用DTO接收

List<Object[]> stus = session

.createSQLQuery("select {stu.*},{cla.*},{spe.*} from

t_stu stu left join t_classroom cla

on(stu.cid=cla.id)

left join t_special spe on(spe.id=cla.spe_id)

where stu.name like ?")

.addEntity("stu",Student.class)

.addEntity("cla",Classroom.class)

.addEntity("spe",Special.class)

.setParameter(0, "%%")

.setFirstResult(0).setMaxResults(10)

.list();

Student stu = null;

Classroom cla = null;

Special spe = null;

List<StuDto> list = new ArrayList<StuDto>();

for(Object[] obj:stus) {

    stu = (Student)obj[0];

    cla = (Classroom)obj[1];

    spe = (Special)obj[2];

    list.add(new StuDto(stu, cla, spe));

    //這裏會將學生姓名打印3遍,因此sql語句中加了花括號,用來自動將結果映射

//到前綴對應的對象中

    System.out.println(stu.name+cla.name+spe.name);

}

【實例2】:使用DTO接收返回值

l  StudentDto實體:

public class StudentDto {

    private int sid;

    private String sname;

    private String sex;

    private String cname;

    private String spename;

    public StudentDto(int sid, String sname, String sex, String cname,

           String spename) {

       super();

       this.sid = sid;

       this.sname = sname;

       this.sex = sex;

       this.cname = cname;

       this.spename = spename;

    }

   

    public StudentDto() {

    }

}

l  查詢

session = HibernateUtil.openSession();

//這裏的sql爲了和DTO對應,

//能夠在selectnew com.zmj…StudentDto(stu.id as sid,……)

List<StudentDto> stus = session.createSQLQuery("select

stu.id as sid,

stu.name as sname,

stu.sex as sex,

cla.name as cname,

spe.name as spename

from t_stu stu

left join t_classroom cla

on(stu.cid=cla.id) "

left join t_special spe

on(spe.id=cla.spe_id)

where stu.name like ?")

//這裏第一感受可能會想到使用setEntitysetEntity的參數必須是和數據庫

//對應的,可是DTO明顯不須要和數據庫對應,因此這裏使用如下這個方法:

    .setResultTransformer(Transformers.aliasToBean(StudentDto.class))

    .setParameter(0, "%%")

    .setFirstResult(0).setMaxResults(10)

    .list();

for(StudentDto sd:stus) {

    System.out.println(sd);

}

【提示】:

這樣使用Hibernate的方法(查詢使用sql,增、刪、改使用Hibernate),效率會大大提升!

相關文章
相關標籤/搜索