1. 程式人生 > 實用技巧 >Hibernate框架

Hibernate框架

1.Hibernate的概述

1.1 Hibernate框架的作用

Hibernate框架是一個數據訪問框架(也叫持久層框架,可將實體物件變成持久物件,詳見第5章)。通過Hibernate框架可以對資料庫進行增刪改查操作,為業務層構建一個持久層。可以使用它替代以前的JDBC訪問資料。

1.2 Hibernate訪問資料庫的優點

1簡單,可以簡化資料庫操作程式碼。

2Hibernate可以自動生成SQL,可以將ResultSet中的記錄和實體類自動的對映(轉化)。

3Hibernate不和資料庫關聯,是一種通用的資料庫框架(支援30多種資料庫),可以方便資料庫移植。任何資料庫都可以執行它的API

。因為HibernateAPI中是不涉及SQL語句的,它會根據Hibernate的配置檔案,自動生成相應資料庫的SQL語句

1.3 JDBC訪問資料庫的缺點

1)需要編寫大量的複雜的SQL語句、表字段多時SQL也繁瑣、設定各個問號值。

2)需要編寫實體物件和記錄之間的程式碼,較為繁瑣。

3)資料庫移植時需要修改大量的SQL語句。

1.4 Hibernate的設計思想

Hibernate是基於ORMObject Relation Mapping)思想設計的,稱為物件關係對映。負責Java物件和資料庫表資料之間的對映。

Hibernate是一款主流的ORM工具,還有其他很多ORM工具,如:MyBatis

(以前叫iBatis)、JPAHibernate功能比MyBatis強大些,屬於全自動型別,MyBatis屬於半自動。但全自動會有些不可控因素,因此有些公司會用MyBatis

ORM工具在完成Java物件和資料庫之間的對映後:HQL

1)在查詢時,直接利用工具取出“物件”(不論是查詢一條記錄還是多條記錄,取出的都是一個個物件,我們不用再去轉化實體了)。

2)在增刪改操作時,直接利用工具將“物件”更新到資料庫表中(我們不用再去把物件轉成資料了)。

3)中間的SQL+JDBC細節,都被封裝在了工具底層,不需要程式設計師參與。

u注意事項:

vJava程式想訪問資料庫,只能通過JDBC的方式,而

Hibernate框架也就是基於ORM思想對JDBC的封裝。

vHibernate是以“物件”為單位進行資料庫的操作。

2.Hibernate的基本使用

2.1 Hibernate的主要結構

1)hibernate.cfg.xml(僅1個):Hibernate的主配置檔案,主要定義資料連線引數和框架設定引數。

u注意事項:就是個xml檔案,只是名字比較奇葩!

2)Entity實體類(n個,一個表一個):主要用於封裝資料庫資料。

3hbm.xml對映檔案(n個):主要描述實體類和資料表之間的對映資訊。描述表與類,欄位與屬性的對應關係。

u注意事項:hbm.xml是個字尾,如:命名可寫Cost.hbm.xml

2.2 Hibernate主要的API

1Configuration:用於載入hibernate.cfg.xml配置資訊。用於建立SessionFactory

2SessionFactory:儲存了hbm.xml中描述的資訊,內建了一些預編譯的SQL,可以建立Session物件。

3Session:負責對資料表執行增刪改查操作。表示Java程式與資料庫的一次連線會話,是對以前的Connection物件的封裝。和JSP中的session不是一回事,就是名字一樣而已。

4Query:負責對資料表執行特殊查詢操作+增刪改

5Transaction:負責Hibernate操作的事務管理。預設情況下Hibernate事務關閉了自動提交功能,需要顯式的追加事務管理(如呼叫Transaction物件中的commit();提交事務)!

u注意事項:

v這些API都是在Hibernate包下的,導包別導錯!

v第一次訪問資料庫比較慢,比較耗資源,因為載入的資訊多。

2.3 Hibernate使用步驟

step1:建立資料庫表。

step2:建立Java工程(Web工程也可),引入Hibernate開發包和資料庫驅動包。必須引入的包:hibernate3.jarcglib.jardom4j.jarcommons-collections.jarcommons-logging.jar…等,下載地址:http://hibernate.org/orm/downloads/

step3:新增hibernate.cfg.xml配置檔案,檔案內容如下:

<?xml version='1.0' encoding='UTF-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

          "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<!-- Generated by MyEclipse Hibernate Tools.                   -->

<hibernate-configuration>

<session-factory>

    <!-- 指定方言,決定Hibernate生成哪種SQL -->

<property name="dialect">

org.hibernate.dialect.MySQLDialect

</property>

<property name="connection.url">

jdbc:mysql://localhost:3306/hibdata

</property>

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

<property name="connection.password">root</property>

<property name="connection.driver_class">

com.mysql.jdbc.Driver

</property>

<property name="myeclipse.connection.profile">mysql</property>

 

<!-- 框架引數,將hibernate底層執行的SQL語句從控制檯顯示 -->

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

<!-- 格式化顯示的SQL -->

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

<!-- 指定對映描述檔案 -->

        <mapping resource="com/hxh/pojo/News.hbm.xml" />

</session-factory>

 

</hibernate-configuration>

注意事項:應該放在原始檔的src目錄下,預設為hibernate.cfg.xml。檔案內容是Hibernate工作時必須用到的基礎資訊。

step4:編寫Entity實體類(也叫POJO),例如:新聞實體類News

private Integer nid;

private String title;

private String content;

private String photo;

private Integer ntid;……getter/setter方法

注意事項:POJO類表示普通類(Plain Ordinary Old Object),沒有格式的類,只有屬性和對應的getter/setter方法,而沒有任何業務邏輯方法的類。這種類最多再加入equals()hashCode()toString()等重寫父類Object的方法。不承擔任何實現業務邏輯的責任。

step5:編寫hbm.xml對映(檔案)描述資訊:對映檔案用於指明POJO類和表之間的對映關係(xx屬性對應xx欄位)一個類對應一個對映檔案。例如:News.hbm.xml內容如下:

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<!-- 定義COST_CHANG表和Cost型別之間的對映資訊   -->

<hibernate-mapping><!-- <hibernate-mapping package="包名寫這也行"> -->

<!-- name:包名.類名,指定是哪個類;table:資料庫中哪個表;catalog:對Oracle而言為哪個資料庫,對MySQl而言為某個使用者(MySQl是在使用者下建表,Oracle是在庫中建表),

不寫也行(若用工具則會自動生成)。例如,select * from news_chang則會在hibernate.cfg配置檔案中定義的庫(或使用者)下去找表。若寫了則為select * from system.news_chang  -->

   <?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!-- 此處表示的是進行POJO類與資料表之間的對映 -->

<hibernate-mapping>

<!-- 配置的是類的相關定義,name表示類的名字,同時設定table對應的資料表,catalog表示的是資料庫的名字 -->

<class name="com.offcn.pojo.News" table="news" catalog="hibdata">

<!-- 配置主鍵,name表示的是類之中的屬性名稱,type表示的是News類的nid型別 -->

<id name="nid" type="java.lang.Integer">

<!-- News類中的nid屬性與資料表之中對應的列,列名稱是nid -->

<column name="nid" />

<!-- 主鍵的生成方式,本次使用的是資料表資料的自增長 -->

<generator class="native"></generator>

</id>

<!-- 配置類之中的其它普通屬性資訊,name表示的是News類中的屬性名稱 -->

<property name="title" type="java.lang.String">

<!-- 定義類中的屬性與表之中的對映欄位 -->

<column name="title" length="50" not-null="true" />

</property>

<property name="content" type="java.lang.String">

<column name="content" length="65535" />

</property>

<property name="photo" type="java.lang.String">

<column name="photo" length="200" />

</property>

<property name="ntid" type="java.lang.Integer">

<column name="ntid" />

</property>

</class>

</hibernate-mapping>

 

注意事項:

對映檔案預設與POJO類放在一起;命名規則為類名.hbm.xml。

hbm.xml中已寫出的屬性與欄位的對映要一一對應,若表中沒有某個欄位,卻寫了對映關係,則報錯:找不到實體類。

step6:利用Hibernate API實現DAO

1)新建HibernateUtil類,用於封裝建立Session的方法。如下:每個使用者會對應一個Session,但是SessionFactory是共享的。

public class HibernateUtil {

private static SessionFactory sf;

static{//不用每次都載入配置資訊,所以放static塊中,否則每次都載入會耗費資源

Configuration conf=new Configuration();//載入主配置hibernate.cfg.xml

conf.configure("/hibernate.cfg.xml");

sf=conf.buildSessionFactory();//獲取SessionFactory }

public static Session getSession(){//獲取Session

Session session =sf.openSession(); return session; }     }

2)新建NewsDao介面

package com.hxh.dao;

 

import java.util.List;

 

import com.hxh.pojo.News;

 

public interface NewsDao {

public News findById(int id);

public void save(News news);

public void delete(int id);

public void update(News news);

  public List<News> findAll();

 

}

 

3)新建NewsDaoImpl類,用於實現NewsDao介面

package com.hxh.dao;

 

import java.util.List;

 

import org.hibernate.Query;

import org.hibernate.Session;

import org.hibernate.Transaction;

 

import com.hxh.pojo.News;

import com.hxh.utils.HibernateUtil;

 

public class NewsDaoImpl implements NewsDao {

    private Session session;

public NewsDaoImpl(){

session=HibernateUtil.getSession();

}

 

 

/*

 * @see com.offcn.dao.NewsDao#findById(int)

 * /** get方法執行查詢,按主鍵當條件查詢,如何判斷是主鍵,是根據寫的描述檔案來定,

 * get方法就是findById,就是按主鍵去查,需指定:操作哪個類和id(主鍵)條件值即可,其他條件查詢做不了

 */

@Override

public News findById(int id) {

// TODO Auto-generated method stub

News news=(News) session.get(News.class, id);

session.close();

return news;

}

 

/** save方法執行增加操作,注意1:獲取事務並開啟,增刪改要注意,查詢可以不管事務,因為沒對資料庫進行修改;注意2:主鍵值根據hbm.xml中的<generator>定義生成,執行後,會先獲取序列值,再去做insert操作。

即先:select COST_SEQ_CHANG.nextval from dual; 然後:insert into ……  */

@Override

public void save(News news) {

// TODO Auto-generated method stub

News vo = new News() ;

vo.setTitle("今天是週五啦,高興不?");

vo.setPhoto("nophoto.jpg");

vo.setContent("據說今天有中到大雨,小心被淹死哦,哈哈哈,可以免費游泳!");

// 6、開啟事務

Transaction tran = session.beginTransaction() ;

// 7、執行更新

session.save(vo);

// 8、提交事務

tran.commit();

// 9、關閉連線

session.close() ;

}

 

/** delete方法執行刪除操作,由於Hibernate以“物件”為單位進行資料庫操作,

 * 所以這裡要傳進去一個物件,雖然是個物件,但還是按主鍵做條件刪除,只要把主鍵值設定上就行,其他非主鍵值不用管。也可先通過id查再刪 */

@Override

public void delete(int id) {

// TODO Auto-generated method stub

   Transaction tx=session.beginTransaction();

   News news=new News();

   news.setContent("ijikij即可立即");

       session.delete(news);

       tx.commit();

       session.close();

}

 

/*

 * @see com.offcn.dao.NewsDao#update(com.offcn.pojo.News)

 */

@Override

public void update(News news) {

// TODO Auto-generated method stub

session=HibernateUtil.getSession();

Transaction tx=session.beginTransaction();

session.update(news);//將cost物件更新到資料庫

tx.commit();

session.close();

 

}

 

/*

 * ** 特殊查詢,SQL語句:String sql="select * from COST_CHANG";

       HQL語句:String hql="from Cost"; (Hibernate Query Language)是面向物件的查詢語句。

       from後寫對映的類名,它是Hibernate中特有的查詢語句,根據對映的類去查詢。 */

@Override

public List<News> findAll() {

session=HibernateUtil.getSession();

String hql="from News";//HQL語句

Query query=session.createQuery(hql);

List<News> list=query.list();//執行查詢,返回List集合

session.close();

return list;

}

 

}

4)新建TestCostDAO類,使用junit測試

package hxh.offcn.test;

 

import java.sql.Date;

import java.util.List;

 

import org.junit.Test;

 

import com.hxh.dao.NewsDao;

import com.hxh.dao.NewsDaoImpl;

import com.hxh.pojo.News;

 

public class TestDao {

@Test

public void testFindById(){//當get方法沒有記錄時,返回null

NewsDao NewsDao = new NewsDaoImpl();

News News = NewsDao.findById(1);

System.out.println(News);

}

@Test

public void testSave(){//id主鍵列由Hibernate管理,這裡不用設定

News vo = new News() ;

vo.setTitle("w今天是週五啦,高興不?");

vo.setPhoto("nophoto.jpg");

vo.setContent("據說今天有中到大雨,小心被淹死哦,哈哈哈,可以免費游泳!");

NewsDao NewsDao = new NewsDaoImpl();

NewsDao.save(vo);

  }

@Test

public void testUpdate(){

NewsDao NewsDAO=new NewsDaoImpl();

/** 注意事項:更新部分欄位,不能和實現類中的刪除那樣,做一個物件出來!否則沒設定的欄位將被改為空! 即不能:News News=new News();  News.setId(90);  

   News.setStatus("1");   News.setStartTime(new Date(System.currentTimeMillis()));   */

News news=NewsDAO.findById(1);//只能先通過id找到帶有所有值的物件

news.setContent("我跟發新股夠了");

NewsDAO.update(news);

}

@Test

public void testDelete(){

NewsDao NewsDAO=new NewsDaoImpl();  

NewsDAO.delete(1);

}

@Test

public void testFindAll(){

NewsDao NewsDAO=new NewsDaoImpl();

List<News> list=NewsDAO.findAll();

for(News c:list){

System.out.println(c);

}

}

 

}

3.關聯對映

3.1一對多關係one-to-many

step1:新建專案,匯入Hibernate開發包,借用學生班級實體:StudentGrade,配置hibernate.cfg.xml和兩個實體的hbm.xml對映檔案。

step2:一個年級對應多個學生,所以為一對多關係。因此為OneGrade實體類新增Set集合屬性,以及對應的get/set方法。

    //追加屬性,用於儲存相關聯的學生資訊

private Set<Student> students = new HashSet<Student>();

step3:在OneGrade.hbm.xml對映檔案中,加入Set節點的對映

簡單說明:

 <set name="屬性名">

<!-- 關聯條件,column寫外來鍵欄位,會預設的與其表的主鍵相關聯 -->

<key column="指定關聯條件的外來鍵欄位"></key>

<!-- 指定採用一對多關係,class指定關聯的型別 -->

<one-to-many class="要關聯的另一方(N方)"/>

</set>

具體實現:

<!-- 描述services屬性,採用一對多關係載入service記錄 -->

<!-- 是list集合用<list name=""></list>  set集合用<set name=""></set>  -->

<set name="students" cascade="all" lazy="false">

      <key column="gid"/>        

      <one-to-many class="com.hxh.pojo.Student"/>

</set>

step4:借用5.174)中的step1中的HibernateUtil

step5:新建TestOneToMany.java類用於測試

/**

 * 功能1:查詢一個班級的所有學生資訊

 */

@Test

public  void findById(){

Session session = HibernateUtil.getSessionFactory().openSession();

Grade g = (Grade)session.get(Grade.class, 1);

System.out.println(g.getGid()+" "+ g.getGname()+" "+ g.getGdesc());

Set <Student>students =g.getStudents();

for(Student stu:students){

System.out.println(stu.getSid()+" "+stu.getSname()+" "+stu.getSex());

}

session.close();

}

/**

 * 功能2:一名新生加入到一個現有的班中

 */

@Test

public  void update() {

Student stu = new Student();

stu.setSid(3);

stu.setSname("李婷3");

stu.setSex("女");

 

Session session = HibernateUtil.getSessionFactory().openSession();

Transaction tr = session.beginTransaction();

 

Grade g = (Grade)session.get(Grade.class, 1);

 

/*Set studentset=g.getStudents();

System.out.println(studentset);*/

g.getStudents().add(stu);

 

session.save(g);

session.save(stu);

 

tr.commit();

session.close();

}

 

/**

 * 功能3:刪除現有班級中一名學生

 */

@Test

public  void deleteStudent(){

Session session = HibernateUtil.getSessionFactory().openSession();

Transaction tr = session.beginTransaction();

 

Grade g = (Grade)session.get(Grade.class, 1);

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

    

g.getStudents().remove(stu);

 

session.update(g);

session.update(stu);

tr.commit();

session.close();

}

/**

 * 功能4:查詢一個學生的班級資訊

 */

@Test

public  void findById2(){

Session session = HibernateUtil.getSessionFactory().openSession();

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

System.out.println(stu.getSid() + " " + stu.getSname());

Grade grade = stu.getGrade();

System.out.println(grade.getGname());

System.out.println(grade.getGdesc());

session.close();

}

 

/**

 * 功能5:一名現有學生轉入到另一個現有班級中

 */

@Test

public  void update2() {

 

Session session = HibernateUtil.getSessionFactory().openSession();

Transaction tr = session.beginTransaction();

 

Grade g = (Grade)session.get(Grade.class, 2);

Student stu = (Student)session.get(Student.class, 3);

    

g.getStudents().add(stu);

 

session.update(g);

//session.update(stu);

 

tr.commit();

session.close();

}

 

}

6.2一對一關係one-to-one

step1:新建專案,匯入Hibernate開發包,借用學生和考卷兩個實體:StudentPaper,配置hibernate.cfg.xml和兩個實體的hbm.xml對映檔案。

step2:可一個學生對應一份考卷,所以為一對一關係。因此為Student實體類新增Paper屬性,以及對應的get/set方法,同時為Paper實體類新增Student屬性,以及對應的get/set方法。

//追加屬性,用於儲存關聯的Paper資訊

private Paper paper;//

Paper實體類

package com.hxh.pojo;

 

public class Paper {

private int pid;

private String pdesc;

private Student student;

 

public int getPid() {

return pid;

}

public void setPid(int pid) {

this.pid = pid;

}

public String getPdesc() {

return pdesc;

}

public void setPdesc(String pdesc) {

this.pdesc = pdesc;

}

public Student getStudent() {

return student;

}

public void setStudent(Student student) {

this.student = student;

}

 

}


step3:配置Student.hbm.xml對映檔案和Paper.hbm.xml

簡單說明:

<one-to-one name="屬性名" class="要關聯的另一方型別Account"

column="關聯條件的外來鍵欄位"/>   <!-- 指明外來鍵欄位,不寫主鍵 -->

注意事項:此時沒有<set name="屬性名"></set>標籤。

具體實現:

<one-to-one name="paper" class="com.hxh.pojo.Paper" lazy="false" cascade="all"/>


 <?xml version="1.0" encoding="utf-8"?> 

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

 

<hibernate-mapping>

    <class name="com.hxh.pojo.Paper" table="PAPER" >

        <id name="pid" type="java.lang.Integer">

            <column name="PID" />

            <generator class="assigned" />

        </id>

        <property name="pdesc" type="java.lang.String">

            <column name="PDESC" length="50" not-null="true" />

        </property>

        <many-to-one name="student" class="com.hxh.pojo.Student" unique="true" >

         <column name="sid"/>

        </many-to-one>      

    </class>

</hibernate-mapping>

step4:借用5.174)中的step1中的HibernateUtil

step5:新建TestOne2one .java類用於測試

package com.hxh.test;

 

 

import org.hibernate.Session;

import org.hibernate.Transaction;

 

import com.hxh.pojo.Paper;

import com.hxh.pojo.Student;

import com.hxh.utils.HibernateUtil;

 

public class TestOne2one {

public static void main(String[] args) {

//save();

findById2();

}

/**

 * 功能1:新增一個學生及其學生證資訊

 */

public static void save() {

Student  stu = new Student();

stu.setSid(11070130);

stu.setSname("趙飛");

stu.setSex("男");

 

Paper paper = new Paper();

paper.setPid(123456);

paper.setPdesc("北京大學學生證");

 

paper.setStudent(stu);

stu.setPaper(paper);

 

Session session = HibernateUtil.getSessionFactory().openSession();

Transaction tr = session.beginTransaction();

 

session.save(stu);

 

tr.commit();

session.close();

}

 

 

/**

 * 功能2:根據學生證查詢相應學生資訊 

 */

public static void findById2() {

Session session = HibernateUtil.getSessionFactory().openSession();

Paper paper= (Paper)session.get(Paper.class, 123456);

System.out.println(paper.getPdesc());

System.out.println(paper.getStudent().getSname());

session.close();

}


}