Hibernate(1)——數據訪問層的架構模式


俗話說,自己寫的代碼,6個月后也是別人的代碼……復習!復習!復習!涉及到的知識點總結如下:

  • 數據庫的概念、邏輯、數據模型概念
  • 應用程序的分層體系結構發展
  • MVC設計模式與四層結構的對應關系
  • 持久層的設計目標
  • 數據映射器架構模式
  • JDBC的缺點
  • Hibernate簡介
  • 迅速使用Hibernate開發的例子
  • Hibernate的核心類和接口,以及他們的關系
  • POJO和JavaBean的比較
  之前的一篇總結文章:

數據庫精華知識點總結(1)—數據庫的三層模式和二級映像,E-R(實體聯系圖)圖,關系模型

  簡單來說,就是在軟件開發領域,可以用模型表示真實世界的實體。針對數據庫就分為:
  • 概念模型(分析階段):一般不描述實體的行為,技術人員和非技術人員都能看懂。
  • 邏輯模型(設計階段)
  • 物理模型/數據模型(實現階段)

舉例電腦;

  在需求分析階段,我們要考慮電腦的概念模型:品牌,顏色,價格……在設計階段,需要構建邏輯模型,比如建立實體類(包含屬性和方法),在物理模型階段,我們要把實體保存到數據庫中,此時需要建立表……把數據插入到表,實體的屬性對應表的字段。他們的關系如下:

  很自然的,研發人員需要把數據模型(面向關系)和域模型(面向對象)聯系起來,因為目前主流的數據庫都是關系型數據庫,比如常見的MySQL,Oracle,SQLServer……而我們的程序(目前大多數)都是面向對象的模型。

  PS:關系型數據庫以行和列的形式存儲數據,以便於用戶理解。這一系列的行和列被稱為表,一組表組成了數據庫。表與表之間的數據記錄有關系。關系模型本質就是一張二維表。

  • 表-------實體
  • 字段-------屬性
  • 記錄--------實例
  • 視圖
  • 索引
  • 主鍵,主鍵滿足三點特征:1.唯一性,2.非空,3.不可修改
    •   還有一種主鍵叫代理主鍵:不具有業務邏輯含義。比如 MySQL 的 auto_increament,  uniqueidentify
  • 觸發器/存儲過程

  

  下面簡單看下實體和實體之間的關聯關系:

  • One to One(一對一),比如一個公民對應一個身份證號碼
  • One to Many(一對多),比如一個公民有多張銀行卡賬號
  • Many to Many(多對多),比如一個老師對應多個學生,同時一個學生對應多個老師

  

  簡單復習表的完整性

  • 實體的完整性 (表記錄的唯一性,通過主鍵解決)
  • 參照完整性   (主子表數據的一致性)
  • 域的完整性   (類型與約束檢查)
  • 用戶自定義的完整性 (用戶自定義的約束),例如:考試成績0-100是合理的,需要用戶定義 

 

  應用程序的分層體系結構發展

  
  層與層之間存在自上而下的依賴關系,即上層組件會訪問下層組件的API,而下層組件不應該依賴上層組件。例如:表述層依賴於業務邏輯層,而業務邏輯層依賴於數據庫層,每個層對上層公開API,但具體的實現細節對外透明。當某一層的實現發生變化,只要它的API不變,不會影響其他層的實現。軟件分層的優點:
  • 伸縮性:伸縮性指應用程序是否能支持更多的用戶
  • 可維護性:可維護性指的是當發生需求變化,只需修改軟件的某一部分,不會影響其他部分的代碼
  • 可擴展性:可擴展性指的是在現有系統中增加新功能的難易程度。層數越多,就可以在每個層中提供擴展點,不會打破應用的整體框架
  • 可重用性:可重用性指的是程序代碼沒有冗余,同一個程序能滿足多種需求。例如,業務邏輯層可以被多種表述層共享
  • 可管理性:可管理性指的是管理系統的難易程度。將應用程序分為多層后,可以將工作分解給不同的開發小組,從而便於管理

 

  下面復習下數據映射器架構模式:

  程序中的對象間的關系,和關系型數據庫中的表是不同的,數據庫的表可以看成是由行與列組成的格,表中的一行可以通過外鍵和另一個表(甚至同一個表)中的一行關聯,而對象的關系非常復雜:一個對象可能包含其他對象,不同的數據結構可能通過不同的方式組織相同的對象……對象和關系數據是業務實體的兩種表現形式,業務實體在內存中表現為對象,在數據庫中表現為關系數據。

  要把關系數據庫里的數據本身和處理業務邏輯的內存對象對應聯系起來,可以通過數據映射器把兩者關聯。數據映射器是分離內存對象和數據庫的中間軟件層,在保持對象和數據庫(以及映射器本身)彼此獨立的情況下,在二者之間移動數據的一個映射器層。簡單的說,數據映射器就是一個負責將數據映射到對象的類數據,由它來負責對象和關系數據庫兩者數據的轉換,從而有效地在領域模型中隱藏數據庫操作並管理數據庫轉換中不可避免的沖突。

  數據映射器架構模式最強大的地方在於消除了領域層和數據庫操作之間的耦合。數據映射器在幕后運作,可以應用於各種對象關系映射。而與之帶來的是需要創建大量具體的映射器類,不過現在的主流Web開發框架都可以自動生成並維護……比如Hibernate框架,前面提到了對象關系映射,繼續總結:

 

  對象-關系映射(Object/Relation Mapping,簡稱ORM)

     是隨着面向對象的軟件開發方法發展而產生的。面向對象的開發方法是當今企業級應用開發環境中的主流開發方法關系數據庫是企業級應用環境中永久存放數據的主流數據存儲系統。它的實質就是將關系數據(庫)中的業務數據用對象的形式表示出來,並通過面向對象(Object-Oriented)的方式將這些對象組織起來,實現系統業務邏輯的過程。

  先看看背景,JavaWeb 開發中,服務器端通常分為表示層、業務層、持久層,這就是所謂的三層架構:

  • 1、表示層負責接收用戶請求、轉發請求、顯示數據等;
  • 2、業務層負責組織業務邏輯;
  • 3、持久層負責持久化業務對象,本質上是封裝了對數據的訪問細節,為業務邏輯層提供面向對象的API,使業務層可以專注於實現業務邏輯(“持久化” 包括和數據庫相關的各種操作,保存、更新、刪除、加載、查詢);

 

  這三個分層,每一層都有不同的模式,即架構模式,如下圖。

  

  MVC設計模式與四層結構的對應關系

  

   持久層的設計目標
  • 代碼可重用性高,能夠完成對象持久化操作
  • 如果需要的話,能夠支持多種數據庫平台
  • 具有相對獨立性,當持久層發生變化時,不會影響上層實現

  之前我們是直接使用Java的JDBC API進行數據庫操作。

  JDBC編程的缺點

  • 業務邏輯和關系數據綁定,如果關系數據模型發生變化,例如修改了表的結構,那么必須手工修改程序代碼中所有相關的SQL語句,增加了軟件維護的難度
  • 在程序代碼中嵌入面向關系的SQL語句,使開發人員不能完全運用面向對象的思維來編寫程序
  • 如果程序代碼中的SQL語句包含語法錯誤,在編譯時不能檢查這種錯誤,在運行時才能發現這種錯誤,這增加了調試程序的難度

  而持久層的架構模式常用常見的就是數據映射器架構模式Hibernate就是數據映射器架構模式的一種實現,Hibernate本意(狗熊)冬眠,它是一個持久層的開源框架,解決了對象-關系映射的難題,比較龐大,也很智能(相對Ibatis來說,這個iBatis很簡單,后續再順帶總結)。

  • Hibernate是對JDBC API的封裝,是JDBC輕量級封裝框架,增強了代碼的重用性,簡化了代碼,提高了編程效率
  • 使Java程序員可以方便的運用面向對象的編程思想來操縱關系型數據庫
  • 因為Hibernate是對JDBC的輕量級封裝,必要時Java程序員還可以輕松繞過Hibernate直接訪問JDBC API。
  • Hibernate不僅可以應用在獨立的java程序中,還可以應用在java  web項目中,可以和多種web服務器集成,並支持多種數據庫平台

 

  看圖,Hibernate作為一個數據映射器架構模式的實現,位於Web開發三層架構的第三層——數據持久層。

  通過Hibernate(數據持久層)把業務邏輯里的代碼(數據)持久化到數據源里(關系數據庫)中,故Hibernate支持非常非常多的數據庫系統。下面小試牛刀,寫一個Hibernate的應用demo。簡單創建一個Java應用程序。需要引用的jar包:核心jar包是:hibernate3.jar,所有的如下:

還有MySQL數據庫連接的jar包。

  編寫Hibernate應用的一般步驟無外乎以下4步:

  • 創建Hibernate的配置文件:Hibernate需要從配置文件中讀取數據庫配置信息,一般位於項目根路徑。Hibernate配置文件兩種方式。
    • hibernate.properties (鍵=值方式)
    • hibernate.cfg.xml(必須在src的根目錄下)
  • 創建持久化類Xxx.java:指其實例需要被Hibernate持久化到數據庫中的類,即實體類
  • 創建持久化類的對象-關系映射文件:xxx.hbn.xml,必須放在實體類對應的包內
  • 通過Hibernate API編寫訪問數據庫的代碼,完成對象的持久化

  so easy!

  針對配置文件,我們可以去jar包的示例去找,看官方的例子,或者隨便網上一搜一堆……沒有必要去記!!!關鍵是思路和原理的掌握。

  Hibernate配置文件是典型的 XML 文件,我們只保留 session 工廠部分,里面的內容不要。

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

<hibernate-configuration>
    <session-factory name="foo">
            
    </session-factory>
</hibernate-configuration>
View Code

  創建一個數據庫bbs,一張表user

  持久化類和我們的數據庫的表是有對應關系的,故前提是,得有一張數據庫的表User二維表(至少1張)和對應的實體類User類,我在vo包(數據對象value Object:頁面與頁面之間的傳遞值時保存值的對象)建立:

 1 package dashuai.vo;
 2 
 3 /**
 4  * User 實體類
 5  *
 6  * @author Wang Yishuai.
 7  * @date 2016/2/2 0002.
 8  * @Copyright(c) 2016 Wang Yishuai,USTC,SSE.
 9  */
10 public class User {
11     private int userId;
12 
13     private String username;
14 
15     private String password;
16 
17     public String getPassword() {
18         return password;
19     }
20 
21     public void setPassword(String password) {
22         this.password = password;
23     }
24 
25     public int getUserId() {
26         return userId;
27     }
28 
29     public void setUserId(int userId) {
30         this.userId = userId;
31     }
32 
33     public String getUsername() {
34         return username;
35     }
36 
37     public void setUsername(String username) {
38         this.username = username;
39     }
40 }
View Code

同時它也是一個POJO類——POJO全稱是Plain Ordinary Java Object / Plain Old Java Object,中文可以翻譯成:普通Java類,具有一部分getter/setter方法的那種類就可以稱作POJO類。

 

  創建對象-關系映射的XML文件,必須放在實體類對應的包(我的是vo)下,名字嚴格對應為:類名.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <!-- class 里面的屬性必須和對應的類名,表名保持一致,實現映射-->
    <class name="dashuai.vo.User" table="user">
        <!-- id代表數據庫表的主鍵, name="userId",對應數據庫表里的字段column="userId"-->
        <id name="userId" column="userId" type="int"/>
        <!-- 數據庫里其他普通字段和實體屬性的映射,屬性的類型需要小寫-->
        <property name="username" column="username" type="string"/>
        <property name="password" column="password" type="string"/>
    </class>
</hibernate-mapping>
View Code

  <class>元素用於指定類和表之間的映射,name屬性設定類名(包含路徑),table屬性設定表名,默認以類名作表名

  id子元素設定持久化類的OID和表的主鍵的映射,id的子元素< generator>元素指定OID的生成器。
  property子元素設定類的屬性和表的字段的映射
  • name – 對應類的屬性名稱
  • type – 指定屬性的類型
  • column – 指定表字段的名稱
  • not-null –指定屬性是否允許為空

 

  順便把Hibernate的主配置文件hibernate.cfg.xml(需放在src根目錄下)補充好,千萬別忘了把之前對象關系映射配置文件引入到主配置文件內,mapping……

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

<hibernate-configuration>
        <!-- 起一個名字 ,我這里叫 MySQL -->
    <session-factory name="MySQL">
            <!-- 關於數據庫連接的配置屬性-->
            <!-- 連接MySQL數據庫的驅動配置-->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
            <!-- 配置MySQL數據庫的連接地址,我的數據庫叫bbs,端口一般都是3306  -->
        <property name="connection.url">jdbc:mysql://localhost:3306/bbs</property>
            <!-- 配置數據庫的用戶名和密碼-->
        <property name="connection.username">root</property>
        <property name="connection.password">123</property>
            <!-- 配置數據庫的方言,設置MySQL5.0版本的方言,MySQL5Dialect類的名稱要寫完整 -->
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
            <!-- 方便調試,show_sql = true 設置顯示自動生成的SQL腳本-->
        <property name="show_sql">true</property>

        <!-- 關鍵:把對象關系映射配置文件加到hibernate 主配置 文件,否則程序找不到 -->
        <mapping resource="dashuai/vo/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>
View Code

  創建dao包,並通過Hibernate API 編寫訪問數據庫的代碼,這里必須要知道hibernate的一些核心的類和接口。如下:

 

  Hibernate 框架的核心類和接口

  1. SessionFactory:是一個核心的接口,相當於hibernate和數據庫連接的一個大管家,它緩存了Hibernate的配置信息和映射的元數據信息,屬於重量級的,故實例化它的實現類只需要實例化一次,且不能輕易的關閉,如果關閉了,和數據庫就斷開了,此外,連接不同的數據庫,需要建立不同的SessionFactory對象,而實例化該類,需要用到一個該接口的實現類——配置類Configuration,用Configuration的對象實例化之后,才能創建Session的實例。
    1. SessionFactory的創建,configuartion.buildSessionFactory()。
    2. SessionFactory是線程安全的,可以安全的讓多個線程進行共享,一般整個應用只有唯一的一個SessionFactory實例。
    3. SessionFactory緩存
      1. 內置緩存,存儲Hibernate配置信息和映射元數據信息,物理介質是內存
      2. 外置緩存,是一個可配置的緩存插件,可以存放大量的數據庫數據的拷貝,物理介質可以是內存或者是硬盤
  2. Configuration:是SessionFactory接口的實現類。
    1. 該實現類兩個功能
      1. 負責管理Hibernate的運行的底層配置信息,包括:數據庫的URL、數據庫用戶名和密碼、JDBC驅動,數據庫Dialect,數據庫連接池等。
      2. 生成SessionFactory的對象,創建的SessionFactory對象能可以創建session
    2. 使用的兩種方法
      1. 屬性文件(hibernate.properties),調用代碼:Configuration cfg = new Configuration();
      2. xml文件(hibernate.cfg.xml),調用代碼:Configuration cfg = new Configuration().configure();
  3. Session:也是一個接口,繼承了SessionFactory,Session是Hibernate持久操作的基礎核心,可以簡單的理解為之前jdbc里的connection對象。
    1. Session的創建(依靠SessionFactory,sessionFactory對象來自configuartion.buildSessionFactory();,而configuartion這個實例化對象來自new Configuration().configure();),Session session = sessionFactory.openSession();代表了一個數據庫的連接,有了該session連接,就可對數據庫進行操作(crud),故也叫session為持久化管理器。而對數據庫的操作,我們需要封裝到事務當中,也就是Hibernate的Transaction
    2. Session是一個輕量級對象,是非線程安全的,通常和一個數據庫事務綁定。
  4. Transaction:一個接口,它將應用代碼從底層的事務實現中抽象出來,是一個JDBC事務或一個JTA事務,可以在配置文件中指定,默認是JDBC事務
    1. 調用代碼:Transaction tx = session.beginTransaction();
    2. 使用Hibernate進行操作時,必須顯式的調用Transaction(默認:autoCommit=false)

  類圖:

  

  再介紹下Query與Criteria接口

  Query接口,允許程序員在數據庫上執行查詢並控制查詢如何執行,查詢語句使用Hibernate的HQL或本地數據庫的SQL,調用代碼:Query query = session.createQuery(“from User”);

  Criteria接口,是傳統SQL的對象化表示,調用代碼:Criteria criteria = session.createCriteria(Tuser.class);

  

  結構如圖:

  代碼如下:

 1 package dashuai.dao;
 2 
 3 import dashuai.vo.User;
 4 import org.hibernate.Session;
 5 import org.hibernate.SessionFactory;
 6 import org.hibernate.Transaction;
 7 import org.hibernate.cfg.Configuration;
 8 import org.slf4j.Logger;
 9 import org.slf4j.LoggerFactory;
10 
11 /**
12  * UserDao
13  *
14  * @author Wang Yishuai.
15  * @date 2016/2/2 0002.
16  * @Copyright(c) 2016 Wang Yishuai,USTC,SSE.
17  */
18 public class UserDao {
19     /**
20      * 使用 hibernate 連接 MySQL數據庫,並操作
21      * @param args String[]
22      */
23     public static void main(String[] args) {
24         final Logger LOG = LoggerFactory.getLogger(UserDao.class);
25 
26         // 通過new一個Configuration實例,然后用該實例去調用configure返回一個配置實例
27         Configuration configuration = new Configuration().configure();
28         // 通過 配置實例的buildSessionFactory方法 生成一個 sessionFactory 對象
29         // buildSessionFactory方法會默認的去尋找配置文件hibernate.cfg.xml並解析xml文件
30         // 解析完畢生成sessionFactory,負責連接數據庫
31         SessionFactory sessionFactory = configuration.buildSessionFactory();
32         // 通過 sessionFactory 獲得一個數據庫連接 session,可以操作數據庫
33         Session session = sessionFactory.openSession();
34         // 把操作封裝到數據庫的事務,則需要開啟一個事務
35         Transaction transaction = session.beginTransaction();
36 
37         // 一般把對實體類和數據庫的操作,放到try-catch-finally塊
38         try {
39             User user = new User();
40             user.setUserId(22);
41             user.setUsername("dashuai");
42             user.setPassword("123456");
43             // 把user對象插入到數據庫
44             session.save(user);
45             // 提交操作事務
46             transaction.commit();
47             LOG.info("transaction.commit(); ok");
48         } catch (Exception e) {
49             // 提交事務失敗,必須要回滾
50             transaction.rollback();
51             // 打印日志
52             LOG.error("save user error......", e);
53         } finally {
54             // 不能丟這一步,要釋放資源
55             if (session != null) {
56                 session.close();
57                 LOG.info("session.close(); ok");
58             }
59         }
60     }
61 }
View Code

    

  整個項目結構

 

  運行結果正確,數據成功插入;Hibernate: insert into user (username, password, userId) values (?, ?, ?)

2016-02-02 23:22:30,202 | INFO  | main | dashuai.dao.UserDao.main(UserDao.java:47)                                                            | transaction.commit(); ok
2016-02-02 23:22:30,207 | INFO  | main | dashuai.dao.UserDao.main(UserDao.java:57)                                                            | session.close(); ok

 

  小結:持久化類和關系數據庫的映射

  說說POJO和JavaBean的區別?

  前面簡單說了POJO是什么——普通Java類,具有一部分getter/setter方法的那種類就可以稱作POJO,理想地講,一個POJO是一個不受任何限制的Java對象(除了Java語言規范)。例如一個POJO不應該是

  1. 擴展預定的類
  2. 實現預定的接口
  3. 包含預定的標注,如   @javax.ejb.Entity public class Baz{ ...

  但是JavaBean則比 POJO復雜很多,JavaBean 是可復用的組件,JavaBean 並沒有嚴格的規范,理論上講,任何一個 Java 類都可以是一個 Bean 。但通常情況下,由於 JavaBean 是被容器所創建(如 Tomcat) 的,所以 JavaBean 應具有一個無參的構造器,另外,通常 JavaBean 還要實現 Serializable 接口用於實現 Bean 的持久性。JavaBean 是不能被跨進程訪問的。

 

  那么為什么需要JavaBean?

  因為Java欠缺屬性、事件、多重繼承功能。所以在Java中實現一些面向對象編程的常見需求,只能手寫大量膠水代碼。JavaBean正是編寫這套膠水代碼的慣用模式或約定。對它最簡單的理解是數據包,這個數據包里包含了一些信息(屬性),比如名稱,性別等,包含了可以給這些屬性賦值和取值的方法(get和set方法)。通過實例化后的賦值操作,可在別的地方通過get方法把值取出來。這就是javabean,或者叫vo。如果在方法中含有了一些邏輯,比如getName的時候,要給name前面加上公司名稱……通常情況下,就叫做bo。而數據庫的表對應的持久化類一般叫POJO,這些東西都可以統稱為javaBean,核心就是賦值(set)和取值(get)。如果需要用到讀寫硬盤的緩存,需要網絡傳輸……則需要序列化這個javaBean.實現Serializable接口。

  《spring in action》書里詳細說到:

  JavaBean :是公共Java類,但是為了編輯工具識別,需要滿足至少三個條件

  1. 有一個public默認構造器(例如無參構造器)
  2. 屬性使用public 的get,set方法訪問,也就是說屬性設置成private,同時get,set方法與屬性名的大小也需要對應。例如屬性name,get方法就要寫成,public String getName(){},N大寫。
  3. 需要序列化。這個是框架,工具跨平台反映狀態必須的
  4. 重寫hashcode和equals方法(可選)……

  ……

  這里插播一下 EJB的概念:
  • 在java1996年發布,當年12月即發布了java bean1.00-A,有什么用呢?這樣java對象可以重用和統一操作,例如原來說的awt組件(例如一個點point(x,y),IDE可以自動展現它的狀態量x,y給你配置,).這就是javaBean的來歷,
  • 在實際企業開發中,需要實現事務,安全,分布式,javabean就不好用了.sun公司就開始往上面堆功能,堆成了一個加強版的javaBean,這就是EJB的來歷;
  • EJB功能強大,但是太重了,很多時候都是殺雞用牛刀!此時編程技術有了一定進展,就是DI(依賴注入),AOP(面向切面),開發者現在可以通過很簡單的javaBean也能完成EJB的事情了。
  • Spring誕生了.
  總的來說,在企業開發中,需要可伸縮的性能和事務、安全機制,這樣能保證企業系統平滑發展,而不是發展到一種規模重新更換一套軟件系統。 然后就提高了協議要求,就出現了 Enterprise Bean。EJB在javaBean基礎上又提了一些要求,當然更復雜了。
 
  POJO :有個叫Josh MacKenzie人覺得,EJB太復雜了,完全沒必要每次都用,所以發明了個POJO, POJO是普通的javabean,什么是普通,就是和EJB對應的。總之,區別就是,你先判斷是否滿足javaBean的條件,然后如果再實現一些要求,滿足EJB條件就是EJB,否則就是POJO。
  
   《Think in Java》一書在最后一章GUI里面講到:J avaBean最初是為Java GUI的可視化編程實現的,拖動IDE構建工具創建一個GUI 組件(如多選框)其實是工具給你創建Java類,並把類的屬性暴露出來給你修改調整,將事件監聽器暴露出來。

 

 

歡迎關注

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM