目錄html
目錄:java
首發日期:2018-07-31mysql
修改:sql
題外話:數據庫
什麼是ORM(對象關係映射):ORM 將數據庫中的表與面嚮對象語言中的類創建了一種對應關係,【ORM能夠說是參照映射來處理數據的模型,好比說:須要建立一個表,能夠定義一個類,而這個類存在與表相映射的屬性,那麼能夠經過操做這個類來建立一個表】apache
爲何須要ORM框架:JDBC的API是最底層的操做數據庫的API了,但明顯的是,使用它很麻煩,由於每個交互都須要你去編寫(包括編寫SQL語句之類的);而使用ORM,它幫你定義了關係和封裝了API,使得你能夠遠離底層,好比你能夠調用save(類對象),這可讓你很輕鬆的將這個對象的各個屬性插入到與類創建了關係的對應數據表中。api
1.當使用Hibernate時,Hibernate須要先去加載一個核心配置文件,讀取Hibernate的核心配置信息(數據庫鏈接信息、日誌管理、數據表映射關係等)【在讀取的過程當中,會將讀取的配置信息逐一配置給對應的組件(鏈接池、日誌等等)。】;緩存
2.載入核心配置文件的過程的最後是讀取核心配置文件中標註的「映射關係文件」。「映射關係文件」定義着持久化類與數據表之間的映射關係。(在讀取映射關係文件的過程當中,同時會判斷是否有對應的數據表,若是沒有表可能會根據類與表的映射關係來建立表,表創建以後,再創建映射);安全
3.核心配置和映射關係都讀取完畢以後。Hibernate的基本運行配置就配置完畢了。在前面的加載配置中,返回的是一個Configuration對象,Configuration對象主要是用來獲取配置文件的。markdown
4.經過Configuration對象的buildSessionFactory方法獲取一個sessionFactory對象,sessionFactory對象真正負責解析配置文件中的配置選項並配置給各個組件,在不少時候,sessionFactory對象至關於一個鏈接池,它解析了Hibernate的配置並負責分發會話鏈接。
5.經過sessionFactory對象的openSession方法來獲取一個Session對象,有了Session對象就至關於有了JDBC中的Connection對象,它至關於與數據庫的會話鏈接。咱們可使用Session對象來操做數據庫。
6.對於增刪改操做,咱們須要事務管理對象Transaction對象的協助。能夠經過Session對象獲取一個Transaction對象,Transaction對象負責數據庫的事務管理。
7.對於增刪改操做,須要開啓事務對象Transaction,但對於查詢操做,是不須要事務的。對於增刪改操做,能夠經過Session對象的調用save\delete\update方法來操做數據庫;對於查詢操做,能夠經過get\load等方法來操做數據庫。
下面的運行環境基於的是Hibernate5.3.0
【不過更建議使用低一點的版本的,好比可使用5.0.x版本的】
【聽說Hibernate5.x要求JDK版本最低要1.7?不過我沒嘗試過,若是發現報錯,能夠考慮一下多是由於這個問題。】
【若是你會maven的話,也能夠嘗試自查一下maven搭建Hibernate】
1.在官網下載Hibernate5
下載方法能夠參考一下這個:https://jingyan.baidu.com/article/7e4409530e67d82fc0e2eff9.html
2.解壓下載的壓縮包
3.在解壓出來的文件夾中,lib存放着Hibernate運行所須要的jar包。lib目錄下有好幾個文件夾,每一個文件夾下有Hibernate不一樣功能需求的jar包。
4.由於這裏僅僅作最基礎的運行,因此僅僅將required中的11個jar包導入到工程中。
若是你還須要其餘功能,能夠按照前面圖中標註不一樣功能的文件夾來選擇添加。好比想要使用C3P0,能夠將optional文件夾中c3p0文件夾下的包都導入。
5.導入Hibernate以外的包:因爲Hibernate是持久層的框架,因此它還須要數據庫驅動包。另外,Hibernate依賴slf4j日誌接口,因此也須要導入。
6.導入包以後,運行環境就基本搭建完畢了。後面都是具體需求具體配置了。
這個基礎示例做用是讓你瞭解Hibernate的使用須要作什麼?先讓你瞭解須要作什麼,後面再討論每一個流程裏面可以作哪些操做。這個示例能夠與上面的運行流程結合瞭解。
1.運行環境搭建,導入須要的依賴包。
2.建立持久類Person,持久類用於跟數據表創建映射關係,類的成員變量與數據表的字段對應。
public class Person { private Long pid; private String username; private Integer age; private Integer address; public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getAddress() { return address; } public void setAddress(Integer address) { this.address = address; } }
3.建立映射文件Person.hbm.xml,這個文件的意義是將持久類與數據表的屬性創建映射關係(具體配置先不說,只在圖內簡單介紹,下面再具體介紹),以便使用ORM操做來管理數據表。這個映射文件與持久類Person位於同一個包下。
映射文件主要包括類與表的對應,以及它們之間的成員變量與字段的對應。
4.建立核心配置文件:hibernate.cfg.xml,這個核心配置文件包含了數據庫鏈接信息、hibernate自身配置(方言,是否顯示SQL語句)、映射文件路徑等信息
【上圖有個自動建表操做:數據表也能夠交給Hibernate建立(但不建議,由於方言會影響使用什麼語句來建立數據表,有時候方言與數據庫版本之間會有一些問題。因此十分建議提早建立,否則你可能會遇到其餘問題】
5.Hibernate的核心配置文件和映射文件已經寫好了,那麼下面的重點就是獲取對象來操做了。使用Configuration來讀取核心配置文件,返回結果是Configuration對象,這個對象包含了Hibernate的配置信息(就比如工廠有了設計圖紙),下面的操做會自動讀取hibernate.cfg.xml中的配置。
6.使用前面獲取到的Configuration對象來獲取一個SessionFactory對象,SessionFactory對象是解析了Hibernate的配置信息。(能夠說SessionFactory對象就是一個獲取到圖紙以後,已經作好準備開工的工廠)
7.SessionFactory對象能夠翻譯成session工廠,可使用它來獲取Session對象,Session對象創建了與數據庫的會話鏈接,至關於JDBC中的Connection對象,咱們可使用Session對象來與數據庫交互。【Transaction是事務對象,負責管理事務,對於增刪改都須要事務對象。】
上面的過程就是經過保存一個對象來將數據存儲到數據表中的過程。
一個簡單示例:
public class Person { private Long pid;//惟一標識屬性,用來映射表中的主鍵 private String username; private Integer age; private Integer address; public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getAddress() { return address; } public void setAddress(Integer address) { this.address = address; } }
1.持久化類的屬性儘可能使用包裝類類型。【由於基礎數據類型一般存在默認值而且一般不爲null,這個默認值也會存儲到數據表中,若是不注意就會引發歧義。,null在數據表中是空的意思,在數據表中它的意義是明確的,但若是默認值是0的話,意思就是不明確了。好比某個字段用0,1表明男女,那麼默認值爲0的話就很差肯定是不是忘記設置性別仍是已經設置了。】
2.持久化類不要使用final修飾,否則沒法使用延遲加載功能(這個東西將會在後面的CRUD中load()方式的查詢中講),延遲加載功能依賴於代理對象,使用final修飾以後就沒法產生代理對象了。
核心配置文件的配置方式有使用properties和xml這兩種,咱們主要使用xml版,由於xml版能夠導入映射文件,而使用properties版的就須要本身在代碼中引入映射文件。
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<?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屬性 --> <property name="配置屬性名" >屬性值</property> <!-- 引入映射文件 --> <mapping resource="映射文件路徑"/> </session-factory> </hibernate-configuration>
基礎配置選項是必需要配置的選項
<property name="hibernate.connection.driver_class" >數據驅動</property>
<property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">url</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<property name="hibernate.connection.username">用戶名</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">密碼</property>
<property name="hibernate.connection.password">123456</property>
<property name="hibernate.dialect">方言</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<mapping resource="hbm文件的路徑"/>
<mapping resource="work/domain/Person.hbm.xml"/>
可選配置是說明下面的配置是可選的 ,能夠不配置
<property name="hibernate.show_sql">true或false</property>
【true是顯示,false是不顯示】<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true或false</property>
【true是美化,false是不美化】<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">create或creat-drop或update或validate</property>
<property name="hibernate.hbm2ddl.auto">update</property>
下面給出一個配置好了的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> <property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate2</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">123456</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 其餘 --> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <mapping resource="work/domain/Customer.hbm.xml"/> </session-factory> </hibernate-configuration>
那麼,去哪裏看能配什麼,配哪些值呢?
在hibernate解壓包的project的etc下面有一個hibernate.properties,裏面有hibernate的核心配置信息。想使用什麼功能,ctrl+f查找一下便可。
這裏提醒一下,對於Hibernate5.3以前的版本,對於mysql方言,它或許可以支持下圖中顯示的方言。但要提醒的是,若是你使用了mysql5以上的版本而且Hibernate是5.3,那麼mysql方言你必須使用Mysql5XXXX(在原來的基礎上加個5)
除了在hibernate.properties中,咱們還能夠經過依賴包來查看。好比依賴包中的org.hibernate.dialect包含的就是方言的class文件,你看名字就可以瞭解有哪些方言了。
hibernate有本身的內置鏈接池,但不少時候,咱們會考慮使用別的鏈接池,c3p0就是經常使用的選擇。要使用c3p0鏈接池,也須要在覈心配置文件中進行配置,下面介紹怎麼配置。
1.首先須要配置hibernate的鏈接提供者,改爲由C3P0提供:
2.而後配置c3p0的選項:
<!-- 配置C3P0鏈接池 --> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- 最少可用鏈接數 --> <property name="hibernate.c3p0.min_size">5</property> <!-- 最大可用鏈接數 --> <property name="hibernate.c3p0.max_size">20</property> <!--設定數據庫鏈接的過時時間,以秒爲單位 --> <property name="hibernate.c3p0.timeout">120</property>
注意,要記得導入c3p0的依賴包,它在hibernate解壓包的lib的optional的c3p0中。
映射文件的結構大概以下:
映射文件名要求:類名.hbm.xml,例如Person.hbm.xml
標準xml首部:
xml約束:hibernate的映射文件使用的是she對於映射文件,它的dtd能夠在依賴包hibernate-core-5.3.0.Final.jar下的org.hibernate下的hibernate-mapping-3.0.dtd的註釋中中獲取。(hibernate5與hibernate3的類似,一般使用hibernate-mapping-3.0.dtd的)。
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<id name="pid"> <generator class="native"/> </id>
id標籤用來配置主鍵,generator標籤是id標籤的子標籤,它是用來配置主鍵生成策略的(主鍵的值的策略),generator的class的值經常使用的有以下幾個:
SessionFactory factory = configure.buildSessionFactory()
Session session=factory.openSession()
Session session = factory.openSession();
Transaction transaction = session.beginTransaction();
【這樣就爲這個session開啓了事務】transaction.commit();
transaction.rollback();
瞭解上面對象的做用與使用以後,咱們就能夠得出如下與數據庫創建鏈接並交互的步驟:
瞭解了怎麼創建與數據庫的鏈接以後,下面就是CRUD的內容了。
【不要忘了!Hibernate的增刪改須要事務管理】
1.建立持久類對象,給持久類對象設置屬性。
2.session對象調用save(Object obj)來保存到數據庫,對象的數據會根據映射關係存儲到對應的數據表中。
查詢是一個很重要的內容,考慮到這篇博文已經很長了的,因此我留到另一篇博文去講述了。
詳情點擊下面超連接。查詢
修改有兩種方法:
1.建立一個對象,給對象的惟一標識屬性(OID)賦予一個值,給各個屬性賦值,而後session.update(這個對象)【這樣會把OID對應的數據庫記錄的全部字段都改爲對象的對應屬性,因此若是不正確賦值的話,數據庫中字段的值會出錯。】
public void test1() { Configuration cfg = new Configuration().configure(); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Person person = new Person(); person.setId(1L); person.setUsername("葫蘆娃"); session.update(person); //這樣的缺點是沒有set的屬性值都是null,並會寫到數據表中 transaction.commit(); session.close(); }
2.先查詢,再修改【推薦操做,查詢出來後,對象中存儲着原有的屬性】:先查詢出對象,修改好對象的指定屬性,而後session.update(這個對象)
【這裏給的例子使用的查詢是OID查詢,更多查詢方法請查看"查詢"那裏的用法】
public void test1() { Configuration cfg = new Configuration().configure(); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Person person = session.get(Person.class, 2L); person.setUsername("葫蘆娃"); session.update(person); transaction.commit(); session.close(); }
刪除有兩種方法:
1.建立一個對象,給對象的惟一標識屬性賦予一個值,而後session.delete(這個對象)
2.先查詢,再刪除:先查詢出對象,而後session.delete(這個對象)【推薦先查詢,再刪除,若是涉及到表與表之間的關聯(外鍵),那麼須要先查詢再刪除,否則查找不到外鍵,從而沒法級聯處理,這個後面講】
【這裏給的例子使用的查詢是OID查詢,更多查詢方法請查看"查詢"那裏的用法】
public void test2() { Configuration cfg = new Configuration().configure(); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Person person = session.get(Person.class, 2L); session.delete(person); transaction.commit(); session.close(); }
緩存:是一種優化的方式,將數據存入到內存中,內存要比硬盤快,普通的查詢是查詢存儲在硬盤中的數據庫數據,使用緩存後能夠直接從緩存中獲取以前存儲下來的數據,不須要從數據源中從新獲取。
上面瞭解了緩存區以後,有提到存儲到緩存區的一般都是持久態對象,下面瞭解一下持久類對象的三種狀態。
瞬時態:新建的持久類,惟一標識屬性(OID)沒有值的時候,沒有被session管理的時候。
持久態:惟一標識屬性有值,被session管理的時候(要麼是用session剛從數據庫中查詢出來的;要麼是已經使用過session保存到數據庫中,而且尚未被刪除的。)。持久態的對象會存儲到緩存區中,用來提升效率。
脫管態:惟一標識屬性有值,不被session管理的時候。
持久類狀態轉換圖:
開啓事務:Transaction transaction = session.beginTransaction();
事務提交:transaction.commit();
事務回滾:transaction.rollback();
public void test1() { Configuration configuration = new Configuration().configure(); SessionFactory factory = configuration.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Account account = session.get(Account.class, 1L); Account account2 = session.get(Account.class, 2L); try { account.setMoney(account.getMoney()-100); account2.setMoney(account2.getMoney()+100); transaction.commit();//不發生異常的時候,提交 }catch (Exception e) { transaction.rollback();//發生異常,回滾 }finally { session.close(); } }
事務隔離級別配置要在覈心配置文件中配置:
<property name="hibernate.connection.isolation">4</property>
值能夠爲一下四個:【左邊是值,右邊是級別,值表明的級別的意義就不說了,是sql那邊的內容。】
1:Read Uncommitted
2:Read Committed
4:Repeatable Read
8:Serializable
咱們從上面學會了怎麼使用事務--用session來開啓。但你可能遭遇兩個「動做」不在一個session中的狀況,這種狀況是很常常見的,由於分工越細效率越高。例如銀行的轉錢操做,這種須要一方加錢,一方減錢,一般來講咱們不會把加錢減錢都定義到一個函數中,而是把他們分開,這樣別的狀況須要加錢時就能夠直接調用函數了。而不一樣的session之間的事務是隔離的,這時候兩個操做處於不一樣session的話就沒辦法進行統一的事務管理了。
@Test public void test2() { Configuration configuration = new Configuration().configure(); SessionFactory factory = configuration.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Account account = session.get(Account.class, 1L); Account account2 = session.get(Account.class, 2L); reduceMondy(account,100.0); addMoney(account2,100.0); transaction.commit();//不發生異常的時候,提交 session.close() } public void reduceMondy(Account account, double d) { Configuration configuration = new Configuration().configure(); SessionFactory factory = configuration.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); account.setMoney(account.getMoney()-d); session.update(account); transaction.commit(); session.close() } public void addMoney(Account account, double d) { Configuration configuration = new Configuration().configure(); SessionFactory factory = configuration.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); account.setMoney(account.getMoney()+d); int a=10/0;//此次出一下異常,測試二者的事務是不一樣的,結果,一方錢少了,一方沒加錢 session.update(account); transaction.commit(); session.close() }
你可能會想到把某個session做爲形參傳進去,這樣兩個函數用的就是一個session了,這是一個解決方法。
但Hibernate提供了一個更好的解決方案。咱們可使用getCurrentSession來獲取session(須要在覈心配置文件中配置,下面將如何配置),getCurrentSession是經過當前線程來獲取一個session,當調用getCurrentSession來獲取session時,Hibernate會把這個session存儲到線程中,其餘函數調用getCurrentSession來獲取session時,獲取的session是同一個,因此同一個線程執行不一樣的函數依然能夠很好的進行事務管理。
@Test public void test2() { Configuration configuration = new Configuration().configure(); SessionFactory factory = configuration.buildSessionFactory(); Session session = factory.getCurrentSession(); Transaction transaction = session.beginTransaction(); Account account = session.get(Account.class, 1L); Account account2 = session.get(Account.class, 2L); try { reduceMondy(account,100.0); addMoney(account2,100.0); transaction.commit(); } catch (Exception e) { transaction.rollback();//發生異常,回滾 } } public void reduceMondy(Account account, double d) { Configuration configuration = new Configuration().configure(); SessionFactory factory = configuration.buildSessionFactory(); Session session = factory.getCurrentSession(); //當使用同一個session時,事務的開啓應該在調用這個兩個函數的地方開啓 account.setMoney(account.getMoney()-d); session.update(account); } public void addMoney(Account account, double d) { Configuration configuration = new Configuration().configure(); SessionFactory factory = configuration.buildSessionFactory(); Session session = factory.getCurrentSession();//從線程中獲取session //當使用同一個session時,事務的開啓應該在調用這個兩個函數的地方開啓 account.setMoney(account.getMoney()+d); int a=10/0;//此次出一下異常,測試二者的事務是不一樣的 session.update(account); }
因此不少時候,factory.getCurrentSession()取代了Session session = factory.openSession()。
getCurrentSession的正常使用須要核心配置文件加上一個配置選項:<property name="hibernate.current_session_context_class">thread</property>
採用getCurrentSession()建立的Session在commit或rollback後會自動關閉,而採用OpenSession()必須手動關閉。
在上面的基礎示例中,演示的是一個單表的例子。而事實上,項目中的表與表之間是有關係的。而且咱們常常須要利用這些關係來獲取數據。
【例子演示以班級與學生之間的一對多關係(假設一個班級能夠有多個學生,一個學生只能有一個班級)爲例】
在「一」的一方的持久類中須要增長一個「多」一方持久類組成的集合(這裏的集合最好提早new一下,省得後面再去賦值,new了以後,後面就能夠直接get來add了)。
建立「一」一方的映射關係文件,除了基本字段關聯以外,還使用set標籤來定義關係。在set標籤中,name屬性是集合對象的變量名,子標籤key中的column屬性是「多」一方的外鍵字段名。子標籤one-to-many的class屬性是「多」一方的類的全路徑。
在多的一方的持久類中增長一個「一」一方的持久類對象,多出來的「一」一方的類的對象是用來映射成外鍵字段的(對象跟外鍵的映射關係須要在映射文件中配置),從面向對象來講就是利用對象的包含關係來映射多對一的關係。(在數據表中咱們使用外鍵來定義一對多的關係,在類中,咱們使用對象包含對象來定義一對多的關係。這個O類相對於M類來講就是M類對應表的外鍵)
建立「多」一方的映射關係文件,除了基本字段關聯以外,還使用many-to-one標籤來定義關係。many-to-one標籤中,name屬性是那個M中存儲的O的對象的變量名,class屬性是O的類的全路徑,column屬性是「一」一方的對象要映射成的外鍵的字段名(這個字段名會出如今「多」一方的表中,並使用「一」一方的主鍵值做爲值)。
在hibernate.cfg.xml中導入映射文件
編寫測試類,將一一方的對象和多一方的對象互相創建上關係(把一一方的對象設置到多一方的對象的屬性中,把多一方的對象添加到一一方的集合中),而後使用save保存起來。
建立兩個學生(李白、杜甫)、一個班級(唐朝);給學生的grade屬性設置爲班級對象並將兩個學生加入到班級對象的學生集合中;把三個對象都保存起來。
public void test3() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //建立兩個學生一個班級 Student s1=new Student(); Student s2=new Student(); s1.setName("李白"); s2.setName("杜甫"); Grade g=new Grade(); g.setCname("唐朝"); //設置關係 s1.setGrade(g); s2.setGrade(g); g.getStudents().add(s1); g.getStudents().add(s2); //保存 session.save(s1); session.save(s2); session.save(g); transaction.commit(); }
結果:
結果顯示了它們創建的一對多的關係,操做一方對象會給外鍵字段字段賦值。
(假設一方爲A,一方爲B)
1.建立A的持久類,裏面有一個B類對象組成的集合【例子演示以一個商品能夠屬於多個購物車,一個購物車能夠有多個商品爲例。】
建立購物車類ShopCar:
2.建立B的持久類,裏面有一個A類對象組成的集合,
建立產品類Product:
3.建立A的hbm,使用property配置好基本屬性,使用set配置配置集合:set中的name的值是自身存儲的對方對象的集合的變量名,table是中間表的名稱(自動建立);key中的column的值是自身在中間表映射的外鍵字段名;many-to-many中class的值是對方類的全路徑,column的值是對方類在中間表映射的外鍵字段名。
建立ShopCar.hbm.xml:
4.建立B的hbm,像第三步同樣配置好
建立Product.hbm.xml:
5.在hernate.cfg.xml中導入映射文件
6.編寫測試類,將一個A類對象與多個B類對象創建關聯,將一個B類對象與多個A類對象創建關聯,把全部A和B的對象保存,查看中間表的數據。
編寫測試類,創建兩個Product,創建兩個ShopCar,讓他們之間相互都創建起關聯。
public void test4() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //新建兩個訂單,兩個產品 ShopCar o1=new ShopCar(); ShopCar o2=new ShopCar(); o1.setCustomer("馬雲"); o2.setCustomer("馬化騰"); Product p1=new Product(); Product p2=new Product(); p1.setProduct_name("聚寶盆"); p2.setProduct_name("招財貓"); //相互創建關係 o1.getProducts().add(p1); o2.getProducts().add(p1); o1.getProducts().add(p2); o2.getProducts().add(p2); //保存 session.save(o1); session.save(o2); session.save(p1); session.save(p2); transaction.commit(); }
結果:
<many-to-one name="grade" class="work.domain.Grade" column="gid" cascade="save-update"></many-to-one>
<set name="shopcars" table="shopcar_product" cascade="save-update"> <key column="product_id"></key> <many-to-many class="work.domain.ShopCar" column="shopcar_id"></many-to-many> </set>
在學生hbm的many-to-one中加上cascade="save-update":那麼只保存學生對象,也能夠同時保存學生對象所對應的班級對象。
<many-to-one name="grade" class="work.domain.Grade" column="gid" cascade="save-update"></many-to-one>
public void test5() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //建立兩個學生一個班級 Student s1=new Student(); s1.setName("林則徐"); Grade g=new Grade(); g.setCname("清朝"); //設置關係 s1.setGrade(g); g.getStudents().add(s1); //保存 session.save(s1);//只保存學生,能夠同時保存這個學生對象所關聯的班級對象 transaction.commit(); }
對於一對多級聯操做(假設「一」一方爲A,「多」一方爲B),可能會產生多餘SQL語句,緣由是緩存區中A,B的數據都發生了變化了,使得不與快照區的數據映像相同,Hibernate默認就會對更新了的對象進行修改處理。
但在修改外鍵的時候就會發生,好比先查詢出來一個A,兩個B(因爲處於持久態,這時候三個對象的數據會存儲到緩存區和快照區),A本來僅僅與B1關聯,如今增長了與B2的關聯,那麼這時候緩存區中的A對象發生了數據更改,B2對象也發生了更改。在提交的時候,校驗緩存區和快照區的數據發現緩存區的A和B2與快照區的數據不一樣了,這時候hibernate會發起對A的數據更新(外鍵新增)以及對B2的數據更新(外鍵新增)。因此發起了兩次外鍵新增,這就產生了多餘的SQL語句。【而多對多中,Hibernate考慮到若是兩個都有的話,會在中間表插入重複的,因此多對多不會發生這個問題(測試過!)】
解決方案:inverse是Hibernate定義的一種維護外鍵的屬性,當這個屬性設置爲true時,那麼設置的一方會放棄外鍵維護權(表明外鍵更新了它也不會去管理),對於一對多,一般都是在set上配置inverse=」true」,讓多的一方去維護外鍵 。
若是你已經導入前面說講的那幾個日誌包了的話,那麼你還須要一個log4j.properties就能夠配置好你的日誌功能了。Hibernate須要日誌來打印一些數據。
日誌包:
至於log4j.properties怎麼寫,project/etc中有一個log4j.properties,能夠參照那個來寫。
# # Hibernate, Relational Persistence for Idiomatic Java # # License: GNU Lesser General Public License (LGPL), version 2.1 or later. # See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. # ### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### direct messages to file hibernate.log ### #log4j.appender.file=org.apache.log4j.FileAppender #log4j.appender.file.File=hibernate.log #log4j.appender.file.layout=org.apache.log4j.PatternLayout #log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### set log levels - for more verbose logging change 'info' to 'debug' ### log4j.rootLogger=warn, stdout #log4j.logger.org.hibernate=info log4j.logger.org.hibernate=debug ### log HQL query parser activity #log4j.logger.org.hibernate.hql.ast.AST=debug ### log just the SQL #log4j.logger.org.hibernate.SQL=debug ### log JDBC bind parameters ### log4j.logger.org.hibernate.type=info #log4j.logger.org.hibernate.type=debug ### log schema export/update ### log4j.logger.org.hibernate.tool.hbm2ddl=debug ### log HQL parse trees #log4j.logger.org.hibernate.hql=debug ### log cache activity ### #log4j.logger.org.hibernate.cache=debug ### log transaction activity #log4j.logger.org.hibernate.transaction=debug ### log JDBC resource acquisition #log4j.logger.org.hibernate.jdbc=debug ### enable the following line if you want to track down connection ### ### leakages when using DriverManagerConnectionProvider ### #log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
寫在最後:Hibernate畢竟是一個框架,市面上的書寫它都能寫它幾百頁,在一篇博文內不可能把內容寫得詳盡。因此這篇博文很大程度上主要是你讓你瞭解Hibernate,剩下的就要你本身去了解了。獲取百度纔是你最好的老師? 這篇博文斷斷續續寫了三天,不少時候都是在想以什麼一種方式來說述才能更好地讓你們理解。最終成了這個樣子,但願能幫到你們。