一、JavaEE三層架構小說明
二、Hibernate入門
2.1、ORM(持久層)框架
ORM 對象關係映射(英語:(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程序技術,用於實現面向對象編程語言裏不同類型系統的數據之間的轉換 。從效果上說,它其實是創建了一個可在編程語言裏使用的“虛擬對象數據庫”。 面向對象是從
軟件工程
基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關係數據庫則是從數學理論發展而來的,兩套理論存在顯著的區別。爲了解決這個不匹配的現象,對象關係映射技術應運而生。 對象關係映射(Object-Relational Mapping)提供了概念性的、易於理解的模型化數據的方法。ORM方法論基於三個核心原則: 簡單:以最基本的形式建模數據。傳達性:數據庫結構被任何人都能理解的語言文檔化。精確性:基於數據模型創建正確標準化的結構。 典型地,建模者通過收集來自那些熟悉應用程序但不熟練的數據建模者的人的信息開發信息模型。建模者必須能夠用非技術企業專家可以理解的術語在概念層次上與數據結構進行通訊。建模者也必須能以簡單的單元分析信息,對樣本數據進行處理。ORM專門被設計爲改進這種聯繫。 簡單的說:ORM相當於中繼數據
。具體到產品上,例如ADO.NET Entity Framework。DLINQ中實體類的屬性[Table]就算是一種中繼數據。
Hibernate:是一個數據持久化層
的ORM框架。
Object:對象,java對象,此處特指JavaBean。
Relational:關係,二維表,數據庫中的表。
Mapping:映射|映射元數據,對象中屬性
與表的字段
存在的對應關係
。
2.2、什麼是Hibernate?
- Hibernate 是
輕量級
JavaEE應用的持久層解決方案,是一個關係數據庫
ORM框架。ORM 就是通過將Java對象映射到數據庫表,通過操作Java對象,就可以完成對數據表的操作。
- Hibernate 提供了對關係型數據庫增刪改查操作。
2.3、主流的ORM框架
- JPA: Java Persistence API,JPA通過
JDK 5.0註解
或XML描述對象--關係表的映射關係
。(只有接口規範) - Hibernate:是最流行的
全自動
ORM框架,通過對象關係--映射配置
,可以完全脫離底層SQL。(理論上來講,就是不用寫sql語句了) - MyBatis:本是apache的一個開源項目iBatis,支持
普通SQL查詢
、存儲過程
和高級映射
的優秀持久層框架。是半自動
ORM框架。 - Apache的DBCPUtil(QueryRunner)、JNDIUtil(QueryRunner)、Spring的JDBCTemplate等等。
2.4、Hibernate的優點
- Hibernate對JDBC訪問數據庫的代碼做了
封裝
,大大簡化
了數據訪問層繁瑣的重複性代碼
。 - Hibernate是一個基於jdbc的主流持久化框架,是一個優秀的orm實現,它很大程度的簡化了dao層編碼工作。
- Hibernate使用java的
反射
機制。 - Hibernate的性能非常好,因爲它是一個
輕量級
框架。映射的靈活性
很出色。它支持很多關係型數據庫,從一對一到多對多的各種複雜關係。
三、Hibernate入門案例【掌握】
3.1、編寫流程
- 新建項目
- 導入jar包
- 創建數據庫和表
- 編寫JavaBean和相應的映射文件
hibernate mapping(*.hbm.xml)
- 編寫核心配置文件(
hibernate.cfg.xml
)--> 配置獲取連接等參數 - 使用api測試
3.2、設計數據庫和表
CREATE DATABASE day29; USER day29; CREATE TABLE t_user( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(20), password VARCHAR(20) );
3.3、導入jar包
版本:3.6.10 --> hibernate 4 建議註解開發,hibernate 4 對 3 不兼容。 目錄結構:
jar介紹:
項目中的lib結構:
3.4、編寫JavaBean + 相應的映射文件Xxx.hbm.xml
User.java
package com.itheima.a_hello; public class User { private Integer id; private String name; private String password; public Integer getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
相應的映射文件位置:JavaBean同包 相應的映射文件名稱:JavaBean同名 相應的映射文件擴展名:*.hbm.xml 具體內容如下: 先添加約束
我們只要約束的代碼,爲了引入。約束代碼如下:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
3.5、編寫核心配置文件hibernate.cfg.xml
位置:類路徑(classpath、src)--> 或者WEB-INF/classes 名稱:hibernate.cfg.xml 具體內容如下: 先添加約束 我們只要約束的代碼,爲了引入。約束代碼如下:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
3.6、測試
核心配置文件hibernate.cfg.xml
中沒有配置自動提交的結果:
刷新數據庫,發現數據並沒有提交,那我們就配置上事務控制,再看看:
哈哈,添加成功了。
注意:也可以不用在覈心配置文件hibernate.cfg.xml
中添加事務控制的配置,可以在測試類代碼中直接添加事務控制代碼,如下圖所示:
哈哈,也添加成功了。
3.7、常見異常
解決方案: 將映射文件添加到核心配置文件中 hbm.xml --> hibernate.cfg.xml
四、Hibernate的api詳解【多練】
4.1、體系結構
PO:Persistent Object 持久化對象,用於與數據庫交互數據。dao層(JavaBean + hbm ) BO:Business Object 業務數據對象。service層。 VO:Value Object 值對象。web層。 開發中:直接使用 JavaBean 來描述這三個對象。
4.2、Configuration 配置對象
- Hibernate 的核心,配置文件種類:
hibernate.cfg.xml
通常使用 xml配置文件,可以配置內容更豐富。hibernate.properties
用於配置 key/value 形式的內容,key不能重複的。配置有很多的侷限性。一般不用。 參考文件所在位置:hibernate-distribution-3.6.10.Final\project\etc\hibernate.properties
提供了核心配置文件常用的配置項及選擇參數。 1、 提供構造方法new Configuration();
hibernate將自動加載hibernate.properties
文件。 hibernate.properties文件必須存放在類路徑(src)下。 2、 提供無參方法configure();
將加載src下的hibernate.cfg.xml
。 3、 擴展api,提供含參方法configure(String);
加載指定目錄下的hibernate.cfg.xml
文件 4、 手動加載配置文件
// 手動加載指定的配置文件 conf.addResource("com/itheima/a_hello/User.hbm.xml"); // 手動加載指定類對應的映射文件:User --> User.hbm.xml conf.addClass(User.class);
示例代碼如下圖所示:
開發中:將hbm.xml映射
配置到hibernate.cfg.xml
中。
學習中:可以使用手動方式 addResource
或 addClass
。
4.3、SessionFactory 工廠
- SessionFactory:相當於java web的連接池,用於管理所有session。
獲得方式:
conf.buildSessionFactory();
SessionFactory:是Hibernate緩存配置信息(數據庫配置信息、映射文件、預定義HQL語句等) SessionFactory:是線程安全,可以是成員變量,多個線程同時訪問時,不會出現線程併發訪問的問題。 提供api:
// openSession => 獲得一個全新的Session對象(新的一個),即打開一個新的會話Session factory.openSession(); // getCurrentSession => 獲得與當前線程綁定的Session對象(同一個),即獲得當前線程中綁定的會話Session factory.getCurrentSession(); Hibernate支持,將創建的session綁定到本地線程中,底層使用ThreadLocal,在程序之間共享Session。 1、調用getCurrentSession(); 必須在hibernate.cfg.xml中進行如下配置: <!-- current_session_context_class:表示將創建的Session與本地線程綁定 ,只有配置了次數,才能使用getCurrentSess();--> <property name="hibernate.current_session_context_class">thread</property> 2、如果提交或回滾事務,底層將自動關閉Session。
示例代碼如下圖所示:
4.4、Session 會話
- Session 相當於 JDBC的 Connection => 會話 通過Session操作PO對象 => 增刪改查 Session是單線程,線程不安全,不能編寫成成員變量。 Session 的api: session.save(Object); 保存 session.update(Object); 更新 session.delete(Object); 刪除 session.get(Class clazz, Serializable); 通過id查詢,如果沒有,則返回null session.load(Class clazz, Serializable); 通過id查詢,如果沒有,則拋出異常 session.createQuery("hql"); 獲得Query對象(hql語句) session.createCritieria(Class clazz); 獲得Criteria對象(類對象) session.createSqlQuery("sql"); 獲取SQLQuery對象(原生sql語句)
get()和load()的區別,如下圖所示:
小問題彙總並解答:
- 1、load方法,會返回一個代理對象,在獲得其內容(屬性)時,會查詢數據庫,是每次訪問屬性都會查詢數據庫嗎? 答:不是每次都查。代理對象中有一個標識:是否被初始化的boolean型變量,記錄着是否被初始化過,確保只會初始化一次。
- 2、代理都是要基於接口的,用load方法返回的代理,就沒有實現任何接口嗎? 答: java中的動態代理是基於接口的。而 Hibernate 是使用javassist-3.12.0.GA.jar 產生動態代理對象的。 該代理與被代理對象之間的關係是繼承關係,與我們學的動態代理不是一種。所以不需要接口。
4.5、Transaction 事務
Session對象:控制如何開啓事務 開啓事務:beginTransaction(); 獲得事務:getTransaction(); 獲取已經開啓的事務對象:即在一個Dao中獲取另一個Dao中的Transaction事務對象。(開發中很少用,因爲事務往往是統一控制的。) Transaction對象:控制如何關閉事務 提交事務:commit(); 回滾事務:rollback();
try { // 開啓事務 // session操作 // 提交事務 } catch(e) { // 回滾事務 } 擴展:不需要手動的管理事務,之後所有的事務管理都交予Spring統一管理了。
示例代碼如下圖所示:
4.6、Query 對象
- Hibernate執行hql語句 hql語句:hibernate提供面向對象查詢語句,使用對象(類)和屬性進行查詢。區分大小寫。 獲得Query對象方式:session.createQuery("hql"); Query對象的方法: list(); 查詢所有 uniqueResult(); 獲得一個結果。如果沒有查詢到就返回null,如果查詢到多條就拋出異常。 setFirstResult(int); 分頁,開始索引數startIndex。 setMaxResults(int); 分頁,每頁顯示個數pageSize。
示例代碼如下圖所示:
4.7、Criteria對象(瞭解)
QBC(query by criteria),hibernate提供純面向對象查詢語言,提供直接使用PO對象進行操作。
獲得Criteria對象方式:Criteria criteria = session.createCriteria(User.class);
條件:
criteria.add(Restrictions.eq("username", "tom")); // 查找name屬性值爲tom的記錄
Restrictions.gt(propertyName, value); 大於
Restrictions.ge(propertyName, value); 大於等於
Restrictions.lt(propertyName, value); 小於
Restrictions.le(propertyName, value); 小於等於
Restrictions.like(propertyName, value); 模糊查詢,注意:模糊查詢值需要使用 %
示例代碼如下圖所示:
4.8、工具類
HibernateUtils.java
package com.itheima.utils; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; // 完成Hibernate工具類 // 封裝配置文件讀取操作 // 封裝SessionFactroy的創建操作 // 封裝Session獲取操作 public class HibernateUtils { // 會話工廠,整個程序只有一份。 private static SessionFactory sessionFactory; // 靜態代碼塊,放進程級別的操作 static { // 1.讀取配置文件對象獲得核心配置對象 Configuration conf = new Configuration().configure(); // 2.通過配置文件對象,創建session工廠SessionFactory,相當於連接池 sessionFactory = conf.buildSessionFactory(); // Ctrl+2進行接收快捷鍵 // 4.關閉虛擬機時,釋放SessionFactory Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { // 多線程中的匿名內部類 @Override public void run() { System.out.println("虛擬機關閉!釋放資源"); sessionFactory.close(); } })); } public static org.hibernate.Session openSession() { // 3.獲得一個全新的Session對象(新的一個),即打開一個新的會話Session return sessionFactory.openSession(); } public static org.hibernate.Session getCurrentSession() { // 3.獲得與當前線程綁定的Session對象(同一個),即獲得當前線程中綁定的會話Session return sessionFactory.getCurrentSession(); } }
測試類:
package com.itheima.utils; public class Test { public static void main(String[] args) { System.out.println(HibernateUtils.openSession()); } }
輸出結果爲:
SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]]) 虛擬機關閉!釋放資源
五、核心配置文件詳解
5.1、詳細配置【多讀】
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"> <hibernate-configuration> <session-factory> <!-- 可以在hibernate.properties查詢所需要的配置 --> <!-- 數據庫基本信息配置:4項 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/day29</property> <!-- show_sql:表示操作數據庫時,會向控制檯打印sql語句 --> <property name="show_sql">true</property> <!-- format_sql:表示在向控制檯打印sql語句之前,會將sql語句先格式化 --> <property name="format_sql">true</property> <!-- hbm2ddl.auto:表示自動生成表結構的策略的配置 update(最常用的取值): 如果當前數據庫中不存在表結構,那麼會自動創建表結構。 如果存在表結構,並且表結構與實體一致,那麼不做修改。 如果存在表結構,並且表結構與實體不一致,那麼會修改表結構,即通過hbm映射文件更新表(添加)。會保留原有列。 即:會自動創建表結構和自動維護表結構。 create(很少):無論是否存在表結構。每次啓動Hibernate都會重新創建表結構(數據會丟失)。 create-drop(極少):無論是否存在表結構。每次啓動Hibernate都會重新創建表結構,每次Hibernate運行結束時,刪除表結構。 validate(很少):不會自動創建表結構。也不會自動維護表結構。Hibernate只校驗表結構,如果表結構不一致將會拋出異常。 --> <property name="hbm2ddl.auto">update</property> <!-- 方言:爲不同的數據庫,不同的版本,生成sql語句(DQL查詢語句)提供依據 --> <!-- 數據庫方言配置 :org.hibernate.dialect.MySQLDialect (有三個方言,選擇最短的) --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!--java web 6.0 存在一個問題:BeanFactory 空指針異常 異常提示:org.hibernate.HibernateException: Unable to get the default Bean Validation factory 解決方案:取消bean校驗 --> <property name="javax.persistence.validation.mode">none</property> <!-- autocommit:表示事務自動提交 --> <property name="hibernate.connection.autocommit">true</property> <!-- current_session_context_class:表示將創建的Session與本地線程綁定 ,只有配置了此處,才能使用getCurrentSess(); 方法--> <property name="hibernate.current_session_context_class">thread</property> <!-- 添加ORM映射文件 ,填寫src之後的路徑--> <mapping resource="com/itheima/a_hello/User.hbm.xml"/> </session-factory> </hibernate-configuration>
六、Hibernate 中持久化類
6.1、JavaBean的編寫規則
- 提供一個無參數的public訪問控制符的構造器。
- 提供一個標識屬性,映射數據表主鍵字段。
- 所有屬性提供public訪問控制符的set和get方法(JavaBean)。
- 標識屬性應儘量使用基本數據類型的包裝類型(因爲基本數據類型有默認值,會給數據庫造成誤會)。
- 不要用final修飾實體(否則將無法生成代理對象,進行優化)。
6.2、持久化對象的唯一標識 OID
- Java按地址區分同一個類的不同對象。
- 關係數據庫用主鍵區分同一條記錄。
- Hibernate使用OID來建立內存中的對象和數據庫中記錄的對應關係。 結論: 對象的OID和數據庫的表的主鍵對應。爲保證OID的唯一性,應該讓Hibernate來爲OID賦值。
6.3、區分自然主鍵和代理主鍵
- 主鍵需要具備: 不爲空/不能重複/不能改變 自然主鍵:在業務中,某個屬性符合主鍵的三個要求,那麼該屬性可以作爲主鍵列。 代理主鍵:在業務中,不存符合以上3個條件的屬性,那麼就增加一個沒有意義的列,作爲主鍵。
6.4、基本數據與包裝類型
- 基本數據類型和包裝類型對應hibernate的映射類型相同。
- 基本類型無法表達null、數字類型的默認值爲0。
- 包裝類默認值是null。當對於默認值有業務意義的時候需要使用包裝類。
6.5、類型對應
如下表所示:
Java數據類型 | Hibernate數據類型 | 標準SQL數據類型(對於不同的DB可能有所差異) |
---|---|---|
byte、java.lang.Byte | byte | TINYINT |
short、java.lang.Short | short | SMALLINT |
int、java.lang.Integer | integer | INGEGER |
long、java.lang.Long | long | BIGINT |
float、java.lang.Float | float | FLOAT |
double、java.lang.Double | double | DOUBLE |
java.math.BigDecimal | big_decimal | NUMERIC |
char、java.lang.Character | character | CHAR(1) |
boolean、java.lang.Boolean | boolean | BIT |
java.lang.String | string | VARCHAR |
boolean、java.lang.Boolean | yes/no | CHAR(1)('Y'或'N') |
boolean、java.lang.Boolean | true/false | CHAR(1)('Y'或'N') |
java.util.Date、java.sql.Date | date | DATE |
java.util.Date、java.sql.Time | time | TIME |
java.util.Date、java.sql.Timestamp | timestamp | TIMESTAMP |
java.util.Calendar | calendar | TIMESTAMP |
java.util.Calendar | calendar_date | DATE |
byte[] | binary | VARBINARY |
java.lang.String | text | CLOB |
java.io.Serializable | serializable | VARBINARY、BLOB |
java.sql.Clob | clob | CLOB |
java.sql.Blob | blob | BLOB |
java.lang.Class | class | VARCHAR |
java.util.Locale | locale | VARCHAR |
java.util.TimeZone | timezone | VARCHAR |
java.util.Currency | currency | VARCHAR |
6.6、普通屬性
示例代碼:
<!-- ORM元數據:對象關係(表)映射文件 --> <hibernate-mapping package="com.itheima.a_hello"> <!--package 用於配置PO類所在包 例如: package="com.itheima.a_hello" 之後配置類名的時候可以使用簡單類名了。 --> <!-- 配置 PO類 和 表 之間的對應關係 --> <!-- name:PO類全限定類名 例如:name="com.itheima.a_hello.User" 如果配置了package,name的取值可以是簡單類名 name="Person" table:數據庫對應的表名 dynamic-insert="false" 是否支持動態生成insert語句,默認值是false dynamic-update="false" 是否支持動態生成update語句,默認值是false 如果設置true,hibernate底層將判斷提供的數據是否爲null,如果爲null,insert或update語句將沒有此項。 --> <class name="User" table="t_user" dynamic-insert="true" dynamic-update="true"> <!-- 普通屬性 --> <property name="name" column="name" type="string"></property> <property name="password" column="password"></property> <!-- name PO類的屬性 column 表中的列名,默認name的值相同 length 列的長度。默認值:255 precision 小數點後的位數 scale 總位數 not-null 指定屬性的約束是否使用 非空 unique 指定屬性的約束是否使用 唯一 access 設置映射使用PO類屬性或字段 property 使用PO類屬性,必須提供setter、getter方法 field 使用PO類字段,一般很少使用。 insert (一般不用)生成insert語句時,是否使用當前字段。 update (一般不用)生成update語句時,是否使用當前字段。 默認情況:hibernate生成insert或update語句,使用配置文件所有項 type 表中列的類型。默認hibernate自己通過getter獲得類型,一般情況下不用設置 表達該屬性的類型 可以用三種方式指定屬性: java類型 數據庫類型指定 Hibernate類型指定 java.lang.String varchar string 取值1: hibernate類型 string 字符串 integer 整形 取值2: java類型 (全限定類名) java.lang.String 字符串 取值3:數據庫類型 varchar(長度) 字符串 int 整形 <property name="birthday"> <column name="birthday" sql-type="datetime"></column> </property> javabean 一般使用類型 java.util.Date jdbc規範提供3中 java類型 mysql類型 java.sql.Date date java.sql.time time java.sql.timestamp timestamp null datetime 以上三個類型都是java.util.Date子類 注意:配置文件如果使用關鍵字,列名必須使用重音符。 --> </class> </hibernate-mapping>
6.7、主鍵
示例代碼:
<!-- 配置主鍵 --> <!-- name 實體中標識主鍵的屬性名稱 access="" 設置使用屬性還是字段(強烈推薦不要用)因爲在操作屬性時,會直接操作對應的字段,而不是操作get/set方法,破壞了面向對象的封裝性(get/set方法中會有一些邏輯控制) column="" 主鍵在表中的列名 length="" 表中列的數據長度 type="" 類型 unsaved-value (不常用)指定主鍵是什麼值時,才當做null來處理 --> <id name="id" column="id" length="9" > <!--固定值:表示主鍵生成策略,如何生成主鍵 native:由數據庫來維護主鍵(數據庫中配置:主鍵自增) generator:主鍵生成策略 1.increment 數據庫自己生成主鍵,先從數據庫中查詢最大的ID值,將ID值加1作爲新的主鍵,不建議使用,存在線程併發問題 2.identity 依賴於數據庫的主鍵自增功能 3.sequence 序列,依賴於數據庫中的序列功能(在Oracle纔有序列功能) 4.hilo (純瞭解,永遠用不到)Hibernate自己實現序列的算法,自己生成主鍵(hilo算法 ) 5.native 自動根據數據庫判斷,三選一:identity|sequence|hilo 6.uuid 生成32位的不重複隨機字符串當做主鍵 以上1-6策略是代理主鍵,由hibernate維護。 7.assigned 自己指定主鍵值,當表的主鍵是自然主鍵時使用 7策略是自然主鍵,由程序自己維護。 --> <generator class="native"></generator> </id>
未完待續。。。。。。