1 使用hibernate步驟 2 1.建立Hibernate的配置文件 3 2.建立持久化類 4 3.建立對象-關係映射文件 5 4.經過Hiberante api 編寫訪問數據庫的代碼 6 SessionFactory: 7 一個SessionFactory實例對應一個數據存儲源,應用從SessionFactory中得到Session實例,SessionFactory有如下特色:它是線程安全,這意味着它一個實例能夠被多個線程共享,這是重量級的。若是應用只訪問一個數據庫,只須要建立一個SessionFactory實例,在應用初始化時候建立一個該實例,若是應用同時訪問多個數據庫,則須要爲每一個數據庫建立一個單獨的SessionFactory實例。主要用來建立session 8 9 Session接口: 10 Session接口是Hibernate應用使用最普遍的接口 11 session也被稱爲持久化管理器,它提供了和持久化相關的操做,如添加,更新,刪除,加載和查詢對象 12 它有如下特色:不是線程安全的,應該避免多個線程共享同一個Session實例 13 Session實例是輕量級的。session至關於一個數據庫鏈接 14 注意:此session非彼Session(HttpSession) 15 java類型,Hibernate映射類型及sql類型之間的對應關係 16 17 java類型 Hibernate類型 Sql類型 18 java.lang.String string Varchar 19 int int int 20 char character char(1) 21 boolean boolean bit 22 java.lang.String text text 23 byte[] binary blob 24 java.sql.Date date date 25 java.sql.Timestamp timestamp timestamp 26 27 28 如下爲構建SessionFactory及Session支持類 29 public class hibernateUtil { 30 static SessionFactory factory=null; 31 static Session session=null; 32 static{ 33 try { 34 //讀取配置文件(hibernate.cfg.xml) 35 Configuration cfg=new Configuration().configure(); 36 //根據配置文件取得SessionFactory工廠 37 factory=cfg.buildSessionFactory(); 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 42 } 43 44 45 //取得Session 46 public static Session getSession(){ 47 48 session=factory.openSession(); 49 return session; 50 } 51 public static void closeSession(Session session){ 52 if(session!=null){ 53 if(session.isOpen()) 54 session.close(); 55 } 56 57 } 58 59 } 60 hibernate的事務控制示例以下: 61 public void savePerson(Person person) 62 { 63 Session session = HibernateUtil.getSession(); 64 Transaction tx = session.beginTransaction(); 65 try 66 { 67 session.save(person); 68 tx.commit(); 69 }catch(Exception ex) 70 { 71 if(null!=tx) 72 { 73 tx.rollback(); 74 } 75 } 76 finally 77 { 78 HibernateUtil.close(session); 79 } 80 } 81 82 使用hql查詢語句 83 Query query = session.createQuery("HQL語句"); 84 List<People> list = (List<People>)query.list(); 85 若是加入分頁則能夠用以下: 86 Query query = session.createQuery("HQL語句").setFirstResult("開始記錄數").setMaxResult("每頁多少條記錄"); 87 把二進制文件從數據庫裏讀出來: 88 byte[] buffer = people.getFile();//得到從查出來的屬性 89 OutputStream os = new FileOutputStream("c:/"+people.getId+".pdf"); 90 os.write(buffer); 91 os.close(); 92 93 對於分頁操做來講,須要知道以下信息:當前正在操做的是第幾頁,每一頁顯示多少條記錄。 94 95 對於query接口的list()方法與iterator()方法來講,均可以實現獲取查詢的對象,可是list()方法返回的每一個對象都是完整的(對象中的每一個屬性都被表中的字段填充上了),而iterator()方法所返回的對象中僅包含了主鍵值(標識符),只有當你對iterator()中的對象進行操做時,Hiberante纔會向數據庫再次發送SQL語句來獲取該對象的屬性值。實際上就是延遲加載 96 97 理解Session的緩存 98 1.當Session的save()方法持久化一個Customer對象時,Customer對象被加入到Session的緩存中,之後即便應用程序中的引用變量再也不引用Customer對象,只要Session的緩存尚未被清空,Customer對象仍然處於生命週期中。 99 2.當Session的load()方法試圖從數據庫中加載一個Customer對象時,Session先判斷緩存中是否已經存在這個Customer對象,若是存在,就不須要再到數據庫中檢索。 100 3.減小訪問數據庫的頻率,應用程序從內存中讀取持久化對象的速度顯然比到數據庫中查詢數據的速度快多了,所以Session的緩存能夠提升數據訪問的性能。 101 4.保證緩存中的對象與數據庫中的相關記錄保持同步。當緩存中持久化對象的狀態發生了變化,Session並不會當即執行相關的SQL語句,這使得Session可以把幾條相關的Sql語句合併成一條SQL語句,以便減小訪問數據庫的次數,從而提升應用程序的性能。 102 清理緩存(關閉session)是指按照緩存中對象的狀態的變化同步更新數據庫 103 104 Session會如下面的時間點清理緩存: 105 1.當應用程序調用org.hibernate.Transaction的commit()方法的時候,commit()方法先清理緩存,而後再向數據庫提交事務 106 2.當應用程序顯式調用Session的flush()方法的時候 107 108 hibernate自身一對多雙向關係,能夠表示無限的層級關係(任何孩子只有一個父母),在實體類裏有一個對父對象的引用,還有一個集合表示它下面的孩子 109 110 Session級別的緩存叫作一級緩存;SessionFactory級別的緩存叫作二級緩存。一級緩存是沒法改變的,二級緩存咱們能夠本身去改變 111 112 Hibernate的二級緩存結構 113 1.Hibernate提供了兩級緩存,第一級緩存是Session的緩存。因爲Session對象的生命週期一般對應一個數據庫事務或者一個應用事務,所以它的緩存是事務範圍的緩存。第一級緩存是必須的,不容許並且事實上也沒法被卸除。在第一級緩存中,持久化類的每個實例都具備惟一的OID. 114 2.第二級緩存是一個可插拔插件,它由SesssionFactory負責管理。因爲SessionFactory對象的生命週期和應用程序的整個進程對應,所以第二級緩存是進程範圍的緩存。這個緩存中存放的是對象的散裝數據。第二級緩存是可選的,能夠在每一個類或每一個集合的粒度上配置第二級緩存。 115 116 二級緩存EhCache 117 在sessionFactory中配置 118 <property name="hibernate.cache.user_second_level_cache">true</property> 119 <property name="hibernate.cache.provider_class">org.hibernate.EhCacheProvider</property> 120 二級緩存一對多,多的一方,在set中加<cache usage="read-only"/>在多的對方配置文件裏也要加<cache usage="read-only"/> 121 在src目錄下加一個ehcache.xml,常見配置以下 122 <ehcache> 123 <diskStore path="c:/ehcache"/>//在磁盤的存放目錄 124 //默認的緩存策略 125 <defaultCache maxElementsInMemory="200"//內存最多存放多少個對象 126 eternal="false"//是否永恆 127 timeToIdleSeconds="50"// 128 timeToLiveSeconds="60"// 129 overflowToDisk="true" 130 /> 131 132 <cache name="com.hibernate.Student"/>//針對具體類的配置,它繼承上面默認的也能夠在裏面設置覆蓋。 133 134 </ehcache> 135 二級緩存: 136 1.transactional:必須在受管的環境下使用,保證可重複讀的事務隔離級別,對於讀/寫比例大,不多更新的數據一般能夠採起這種方式。 137 2.read-write:使用timestamp機制維護已提交事務隔離級別,對於讀/寫比例大,不多更新的數據一般能夠採起這種方式。 138 3.nonstrict-read-write:二級緩存與數據庫中的數據可能會出現不一致的狀況。在使用這種策略的時候,應該設置足夠短的緩存過時時間,不然就有可能從緩存中讀取到髒數據。當一些數據不多改變(一天,兩天都不改變的數據),而且這些數據若是出現數據庫與緩存不一致的狀況下影響並不大的時候,那麼能夠採起這種緩存策略。 139 4.read-only:當肯定數據不會被改變時,咱們可使用這種緩存策略 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 在Hibernate應用中Java對象的狀態 160 1.臨時狀態(transient):剛剛用new語句建立,尚未被持久化,不處於Session的緩存中。處於臨時狀態的Java對象被稱爲臨時對象 161 2.持久化狀態(persistent):已經被持久化,加入到Session的緩存中。處於持久化狀態的Java對象被稱爲持久化對象 162 3.遊離狀態(detached):已經被持久化,但再也不處於Session的緩存中。處於遊離狀態的Java對象被稱爲遊離對象 163 164 Session的方法主要做用是把對象從遊離狀態轉化爲持久化狀態 165 1.把customer對象從新加入到Session緩存中,使它變爲持久化對象 166 2.計劃執行一個update語句,值得注意的是,Session只有在清理緩存的時候纔會執行update語句,而且在執行時纔會把Customer對象當前的屬性值組裝到update語句中。所以,即便程序中屢次修改了Customer對象的屬性,在清理緩存時只會執行一次update語句。 167 168 hibernate的檢索策略: 169 1.當即檢索策略 170 2.延遲檢索策略 171 3.左外鏈接檢索策略 172 173 在多對一關聯級別使用左外鏈接檢索策略 174 1.默認狀況下,多對一關聯使用左外鏈接檢索策略 175 2.若是把Order.hbm.xml文件的<many-to-one>元素的outer-join屬性高爲true,老是使用左外鏈接檢索策略,即在查詢的時候就會把關聯一的那一方查詢出來,只用一條sql語句,從而節省了sql語句。 176 優勢:對應用程序徹底透明,無論對象處於持久化狀態,仍是遊離狀態,應用程序均可以方便的從一個對象導航到與它關聯的對象。 使用了外鏈接,select語句數目減小。 177 缺點:可能會加載應用程序不須要訪問的對象,白白浪費了許多內存空間。 複雜的數據庫錶鏈接也會影響檢索性能。 178 適用範圍: 179 1.多對一或才一對一關聯 180 2.應用程序須要當即訪問的對象 181 3.數據庫系統具備良好的錶鏈接性能 182 183 在程序中顯式指定左外鏈接檢索策略 184 1.在映射文件中設定的檢索策略是固定的,要麼是延遲檢索,要麼是當即檢索,要麼是外鏈接檢索。 185 2.但應用邏輯是多種多樣的,有些狀況下須要延遲檢索,而有些狀況下須要外鏈接檢索。 186 3.Hibernate容許在應用程序中覆蓋映射文件中設定的檢索策略,由應用程序在運行時決定檢索對象力的深度。 187 如下Session的方法都用於檢索OID爲1的Customer對象: 188 session.createQuery("from Customer as c where c.id=1"); 189 session.createQuery("from Customer as c left join fetch c.orders where c.id =1"); 190 在執行第一個方法時,將使用映射文件配置的檢索策略 191 在執行第二個方法時,在HQL語句中顯式指定左外鏈接檢索關聯的Oreder對象,所以覆蓋映射文件配置的檢索策略。無論在Customer.hbm.xml文件中<set>元素的lazy屬性是true仍是false,Hibernate都會執行如下select語句: 192 select*from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID = ORDERS.CUSTOMER_ID where CUTROMERS.ID =1; 193 194 195 一對一映射: 196 1.主鍵關聯(主鍵值是同樣的) 197 <id name="id" column="id" type="string"> 198 <generator class="foreign">//表示做爲外鍵來使用,使用別的表的主鍵,只在一方這樣配置,另外一方正常 199 <param name="property">student</param>//使用那個表的主鍵,使用本類中student對象的表的id 200 </generator> 201 </id>後面還要加上one-to-one配置,再配置上級聯 202 一對一默認使用的是當即加載,如須要使用延遲加載,那麼須要在one-to-one元素中將constrained屬性設爲true,而且將待加載的一方的class元素中的lazy屬性高爲true(或者不去設置,由於該屬性默認值是true).一對一加載時默認使用左外鏈接,能夠經過修改fetch屬性爲select修改爲每次發送一條select語句的形式 203 204 fetch屬性能夠改變查詢語句的方式,join表示鏈接查詢,select表示單獨select查詢,one-to-one默認的是join鏈接查詢 205 206 2.外鍵關聯(特殊的一對多) 207 在一方使用many-to-one加上屬性unique="true"表示惟一就變成了one-to-eon。 208 209 210 211 根據實體類配置生成數據庫表結構 212 SchemaExport export = new SchemaExport(new Configuration().configuer()); 213 export.create(true,true);第一個參數打印出sql語句,第二個參數把sql語句導入到數據庫 214 215 多對多關係相似於一對多,只是要在set中指定中間表,並在key與one-to-many中指定相關外鍵 216 217 map映射採用map標籤,其實是採用另一張表的形式存儲它 218 <map name="students" table="student"> 219 <key column="team_id"></key>//關聯的表中的外鍵 220 <index column="name" type="string"></index>//指定map中的鍵存儲 221 <element column="description" type="string"></element>//指定map中的值 222 </map> 223 若是map的值是另外一個實體 224 <map name="students" table="student"> 225 <key column="team_id"></key>//關聯的表中的外鍵 226 <index column="name" type="string"></index>//指定map中的鍵存儲 227 <one-to-many class="com.hibernate.Student"/>//必須有student映射文件 228 </map> 229 230 set集合中存放的全是簡單屬性(一個類對應兩張表) 231 <set name="student" talbe="student"> 232 <key column="team_id"></key> 233 <element column="name" type="string"></element> 234 </set> 235 236 map與set標籤中的element子標籤映射的是原子類型(string,date,int,long...),即便可以直接映射到數據庫表字段上的類型,而one-to-many映射的則是實體類型,指的是沒法映射到表的某個字段,而是要映射到整張表的類型 237 238 list的元素是能夠重複的,並且是有順序的。set中的元素是不容許重複的 239 <list name="student" table="student" cascade="all"> 240 <key column="team_id"></key> 241 <index column="index_"></index>//數據庫中存放順序的字符(否則沒法排序) 242 <one-to-many class="com.hibernate.Student"/> 243 </list>不能加inverse="true"由於多的一方是沒法知道排序的,因此要在一的一方進行維護 244 245 Bag(結合了List與Set),能夠重複且沒有順序的一種集合,是Hiberante提供的,能夠用list來模擬bag 246 <bag name="student" table="student" cascade="all" inverse="true"> 247 <key column="team_id"></key> 248 <one-to-many class="com.hibernate.Student"/> 249 </bag> 250 251 查詢排序(內存排序及數據庫排序) 252 數據庫排序使用order-by="name asc"(name是數據庫裏的字段),在set,list,bag,map標籤上使用,order-by的內容能夠指定多個字段排序,先按第一個字段排序,若是相等則按第二個字段排序 253 內存排序用sort="natural"按天然排序,它有三個屬性(nusorted,natural),其中的natural指的是按照天然的升序排序。第三個屬性值是咱們自定義的排序規則類(實現Comparator接口裏的comparat方法),在sort中指定咱們排序規則類的全稱 254 255 數據庫聯合主鍵 256 1.類中的每一個屬性都對應到數據表中的每一個主鍵列 257 一個類有聯合主鍵,則它必須實現serializable接口,而且重寫equls和hashcode方法(兩個方法中關聯聯合主鍵涉及到的屬性)。hibernate要求具備聯合主鍵的實體類實現Serializable接口,而且重寫hashCode與equals方法,重寫這兩個方法的緣由在於Hibernate要根據數據庫的聯合主鍵來判斷某兩行記錄是不是同樣的,若是同樣認爲是同一個對象,若是不同,那麼認爲是不一樣的對象,這反映到程序領域中就是根據hashCode與equals方法來判斷某兩個對象是否可以放到諸如Set這樣的集合當中。 258 聯合主鍵映射: 259 <composite-id> 260 <key-property name="cardId" column="cardId" type="string"></key-property> 261 <key-property name="name" column="name" type="string"></key-property> 262 </composite-id> 263 聯合主鍵的實體類實現Serializable接口的緣由在於使用get或load方法的時候須要先構建出該實體的對象,而且將查詢依據(聯合主鍵)設置進去,而後做爲get或load訪求的第二個參數傳進去便可 264 2.將主鍵所對應的屬性提取出來一個類(稱之爲主鍵類),而且主鍵類須要實現Seriliable接口而且重寫hashCode()與equlas接口,在主類中引用這個主鍵類 265 映射文件以下: 266 <composite-id name="stuentPrimaryKey" class="com.hibernate.StudentPrimaryKey"> 267 <key-property name="cardId" column="cardId" type="string"></key-property> 268 <key-property name="name" column="name" type="string"></key-property> 269 </composite-id> 270 這兩種方式生成的表結構是同樣的 271 272 組件映射(把一對一關聯映射到一張表中也只是針對單向的如學生的家庭地址和學校地址組成一個地址類在學生表中的映射) 273 <component name="address" class="com.hibernate.Address"> 274 <property name="homeaddress" type="string"></property> 275 <property name="schooladdress" type="string"></property> 276 </component> 277 在set標籤中也能夠用組件映射,適用於單向 278 279 3.繼承映射 280 1》每一個子類一張表(映射方式沒有特別,把父類的屬性映射到子類文件中) 281 Query query = session.createQuery("from com.hibernate.Person");//多態查詢,會查詢出它全部子類及它自己.若是沒有映射文件,則必須加上包的名字 282 2》一張表存儲體系中全部類的信息(數據庫表實體上是繼承體系中全部類的屬性的並集構成的字段),映射用繼承的父類來命名,須要在hbm文件中增長以下一行用來區分不一樣子類 283 <discriminator column="personType" type="string"></discriminatro> 284 285 子類信息配置 286 <subclass name="com.hibernate.Student" discriminator-value="student">//類名 287 <property name="cardId" type="string"/>//屬性 288 </subclass> 289 <subclass name="com.hibernate.Teacher" discriminator-value="teacher">//類名 290 <property name="salary" type="int"/>//屬性 291 </subclass> 292 293 查詢能夠得到全部信息,經過instanceof比較子類,能夠得到特定子類的信息 294 3》每一個類一張表,公共信息放在父類表中,獨有信息放在子類表中,每一個子類對應一張表 295 其中有關聯父類表的id 296 <joined-subclass name="com.hibernate.Student" table="student"> 297 <key column="id"></key>//主鍵關聯父類 298 <property name="cardId" type="string"></property> 299 </joined-subclass> 300 301 <joined-subclass name="com.hibernate.Teacher" table="teacher"> 302 <key column="id"></key>//主鍵關聯父類 303 <property name="salary" type="int"></property> 304 </joined-subclass> 305 306 Hibernate的查詢 307 1.導航對象圖檢索方式:根據已經加載的對象,導航到其餘對象,如,對於已經加載的Customer對象,調用它的getOrders().iterator()方法能夠導航到全部關聯的Order對象,假如在關聯級別使用了延遲加載檢索策略,那麼首先執行此方法時,Hibernate會從數據庫中加載關聯的Order對象,不然就從緩存中取得Order對象 308 2.OID檢索方式:按照對象的OID來檢索對象。Session的get()和load()方法提供了這種功能。若是在應用程序中事先知道了OID,就可使用這種檢索對象的方式 309 3.HQL檢索方式: Hibernate提供了Query接口,這是Hibernate提供的專門的HQL查詢接口,可以執行各類複雜的HQL查詢語句 310 4.QBC查詢方式:使用QBC(Query By Criteria)api來檢索對象。這種api封裝了基於字符串形式的查詢語句,提供了更加面向對象的接口。 311 312 hql檢索步驟: 313 //建立一個Query對象 314 Query query = session.createQuery("from Customer as c where 315 + "c.name=:customerName" 316 + "and c.age=:customerAge"); 317 //動態綁定參數 318 query.setString("customerName","tom"); 319 query.setInteger("customerAge",21); 320 //執行查詢語句,返回查詢結果 321 List result = query.list(); 322 323 qbc檢索方式 324 採用hql檢索方式時,在應用程序中須要定義基於字符串形式的hql查詢語句 325 qbc api提供了檢索對象的另外一種方式,它主要由Criteria接口,Criterion接口和Expression類組成,它支持在運行時動態生成查詢語句 326 327 //建立一個Criteria對象 328 Criteria criteria = session.createCriteria(Custome.class); 329 //設定查詢條件,而後把查詢條件加入到Criteria中 330 Criterion creiterion1 = Expression.like("name","T%"); 331 Criterion creiterion2 = Expression.eq("age",new Interger(21)); 332 criteria=criteria.add(criterion1); 333 criteria=criteria.add(criterion2); 334 //執行查詢語句,返回查詢結果 335 List result = criteria.list(); 336 337 查詢示例 338 339 查詢部分屬性 340 Query query = session.createQuery("select s.name,s.age from Student s");//查詢部分屬性 341 List list = query.list(); 342 for(int i=0;i<list.size();i++) 343 { 344 Object[] obj = (Object[])list.get(i);//查詢出來的每一行數據都是以object[]形式返回 345 System.out.println(obj[0]+","+obj[1]);//第一個是name,第二個是age 346 } 347 348 Query query = session.createQuery("select new Student(s.name,s.age) from Student s"); 349 此時返回的是包含部分信息的student對象集合(但必須提供以上形式的構造函數) 350 351 鏈接操做 352 353 內鏈接,返回的是兩個對象的數組的集合 354 Query query = session.createQuery("from Team t join t.students");//根據team與student內鏈接 355 List list = query.list(); 356 for(int i=0;i<list.size();i++) 357 { 358 Object[] obj =(Object[])list.get(i); 359 Team team = (Team)obj[0];//鏈接的第一張表 360 Student student =(Student)obj[1];//鏈接的第二張表 361 System.out.println(team.getName()); 362 } 363 364 進行表的鏈接查詢,在hql中其實是覆蓋了延遲加載,都變成了直接加載了,配置文件中的不起使用。 365 366 367 對實體進行參數綁定查詢方法 368 Team team = (Team)session.get(Team.class,"id值"); 369 Query query = session.createQuery("from Student s where s.team=:team and s.age >20"); 370 371 query.setParameter("team",team,Hibernate.entity(Team.class));//1.命名參數名字去掉冒號2.參數所賦值對象3.對象轉化成type類型 372 373 //以上也能夠用這個簡便方法 query.setEntity("team",team); 374 List<Student> list = query.list(); 375 376 377 Query query = session.createFillter("參數1","hql查詢條件(where age>20) ");參數1爲得到的持久化對象的集合,這就是hibernate的過濾查詢 378 Query query = session.createFillter(tema.getStudents(),"where age>20"); 379 380 Criteria criteria = session.createCriteria(Student.class).add(Restrictions.between("age",new Integer(20),new Integer(30));查詢年齡在20到30之間的學生 381 List<Student> list = criteria.list(); 382 between是包含小的也包含大的 383 384 Criteria criteria = session.createCriteria(Student.class).addOrder(Order.asc("age")).addOrder(Order.desc("cardId")); 385 hibernate官方推薦使用hql查詢 386 387 數據庫的事務與併發處理 388 事務transation是一組相互依賴的操做行爲,如銀行交易,股票交易或網上購物,事務的成功取決於這些相互依賴的操做行爲是否都能執行成功,只要有一個操做行爲失敗,就意味着整個事務的失敗。 389 數據庫事務是對現實生活的模擬,它由一組在業務邏輯上相互依賴的SQL語句組成。 390 391 在實際系統中除了查詢操做外,絕對不容許設計爲自動提交 392 393 JTA java事務接口 394 395 每啓動一個mysql.exe程序,就會獲得一個單獨的數據庫鏈接,每一個數據庫鏈接都有個全局變量@@autocommit,表示當前的事務模式,它有兩個值: 396 0--:表示手工提交模式 397 1--:默認值,表示自動提交模式 398 若是要察看當前的事務模式,可以使用以下sql命令:select @@autocommit 399 若是要把當前的事務模式改成手工提交模式,可使用sql命令:set autocommit=0 400 在自動提交模式下,每一個sql語句都是一個獨立的事務,mysql會自動提交這個事務 401 402 數據庫事務的4個特性acid: 403 1.原子性atom 404 2.持久性consistence 405 3.隔離性isolation 406 4.持久性duration 407 在手工模式下,必須顯式指定事務開始邊界和結束邊界: 408 事務的開始邊界:begin 409 提交事務:commit 410 撤銷事務:rollback 411 412 jdbc Connection提供瞭如下用於控制事務的方法 413 setAutoCommit(boolean autoCommit)設置是否提交事務 414 commit()提交事務 415 rollback()撤銷事務 416 417 try{ 418 con=java.sql.DriveManager.getConnection(dbUrl,dbUser,dbPwd); 419 //設置手工提交模式 420 con.setAutoCommit(false); 421 stmt=con.createStatement(); 422 //數據庫更新操做1 423 stmt.executeUpdate("更新語句"); 424 //數據庫更新操做2 425 stmt.executeUpdate("更新語句"); 426 con.commit();//提交事務 427 }catch(Exception e){ 428 tyr{ 429 con.rollback();//操做不成功則撤銷事務 430 }catch(Exception ex){ 431 //處理異常 432 。。。 433 } 434 //處理異常 435 }finally{...} 436 437 多個事務併發運行時的併發問題 438 1.丟失更新:撤銷一個事務時,把其餘事務已提交的更新數據覆蓋 439 2.髒讀:一個事務讀到另外一個事務未提交的更新數據 440 3.虛讀:一個事務讀到另外一個事務已提交的新插入的數據 441 4.不可重複讀:一個事務讀到另外一個事務已提交的更新數據 442 5.第二類丟失更新:這是不可重複讀中的特例,一個事務覆蓋另外一個事務已提交的更新數據 443 444 數據庫的事務隔離級別: 445 ReadUncommited,ReadCommited,Repeatable Read,Serializable 446 查看當前的隔離級別: 447 select @@tx_isolation 448 設置隔離級別 449 set transaction isolation level read committed 450 451 在Hibernate的配置文件中中能夠顯式設置隔離級別。每一種隔離級別都對應一個整數: 452 1.Read Uncommitted 453 2.Read Committed 454 4.Repeatable Read 455 8.Serializable 456 在hibernate.cfg.xml文件中 hibernate.connection.isolation=2 457 458 使用悲觀鎖 459 Account account = (Account)session.get(Account.class,new Long(1),LockMode.UPGRADE); 460 hibernate執行的select語句爲: 461 select * from ACCOUNTS where ID=1 for update;//多了個for update 462 463 樂觀鎖是由應用程序提供的一種機制,這種機制既能保證多個事務併發訪問數據,又能防止第二類丟失更新問題 464 在應用程序中,能夠利用Hibernate提供的版本控制功能來實現樂觀鎖。對象-關係映射文件中的<version>元素和<timestamp>元素都具備版本控制功能 465 -<version>元素利用一個遞增的整數來跟蹤數據庫表中記錄的版本 466 -<timestamp>元素用時間戳來跟蹤數據庫表中記錄的版本 467 468 使用樂觀鎖 469 1.在實體類中加一個version屬性,做爲版本控制,映射該屬性的時候用<version name="version" column="version" type="integer"> 470 時間戳方式控制 471 在實體類中另外一個日期類型的屬性做爲時間戳,配置文件中<timestamp name="lastDate" column="lastDate"/> 472 473 悲觀鎖是由數據庫底層實現的,樂觀鎖是由應用程序來實現的 474 475 實現樂觀鎖的其餘方法: 476 若是應用程序是基於已有的數據庫,而數據庫表中不包含表明版本或時間戳的字段,Hibernate提供了其餘實現樂觀鎖的辦法。把<class>元素的optimistic-lock屬性設爲「all」: 477 <class name="Account" table="ACCOUNTS" optimistic-lock="all" dynamic-update="true"> 478 hibernate會在update語句的where子句中包含Account對象被加載時的全部屬性: 479 update ACCOUNTS set balanec=900 where id=1 and name="tom" and balance="1000"; 480 481 hibernate攔截器與事務 482 應用程序可以響應hibernate內部產生的特定事件是很是有用的。這樣就容許實現某些通用的功能以及容許對hibernate功能進行擴展(監聽事件) 483 持久層框架底層的攔截器機制是對諸如spring等業務管理容器攔截機制的有益的補充,使得咱們能夠在更低層次,更廣的對象範圍上進行AOP操做(Spring雖然將hibernate歸入到其容器管理的範圍內,可是並無途徑實現對其實體對象的管理)。這樣就容許實現某些通知的功能,以及容許對hibernate功能進行擴展。 484 485 Interceptor接口提供了從會話(session)回調(callback)應用程序(application)的機制,這種回調機制能夠容許應用程序在持久化對象被保存,更新,刪除或是加載以前,檢查並(或)修改其屬性。其子類是EmptyInterceptor 486 487 你能夠直接實現Interceptor接口,也能夠(最好)繼承自EmptyInterceptor. 488 攔截器有兩種: 489 -session範圍內的 490 -sessionFactory範圍內的 491 492 使用攔截器按以下步驟進行: 493 1.定義實現Interceptor接口的攔截器 494 2.經過session啓用攔截器,或者經過configuration啓用全局攔截器 495 496 當使用某個重載的SessionFactory.openSession()使用Interceptor做爲參數調用打開一個session的時候,就指定了Session範圍內的攔截器。 497 Session session = factory.openSession(new AuditInterceptor()); 498 499 SessionFactory範圍內的攔截器要經過configuration中註冊,而這必須在建立SessionFactory以前。在這種狀況下,給出的攔截器會被這個SessionFactory所打開的全部session使用了,除非session打開時明確指明瞭使用的攔截器。SessionFactory範圍內的攔截器,必須是線程安全的,由於多個session可能併發使用這個攔截器,要所以當心不要保存與session相關的狀態 500 501 事件系統(Event system) 502 若是須要響應持久層的某些特殊事件,你也可使用Hibernate3的事件框架。該事件系統能夠用來替代攔截器,也能夠做爲攔截器的補充來使用。 503 基本上,Session接口的每一個方法都有相應的事件。如LoadEvent,FlushEvent等等(能夠查詢xml配置文件的dtd以及org.hibernate.event包來得到全部已定義的事件的列表) 504 505 當某個方法被調用時,Hibernate Session會生成一個相應的事件並激活全部配置好的事件監聽器(觀察者模式)。被監聽的方法所作的其實僅僅是激活監聽器,實際的工做是由監聽器完成的。你能夠自由地選擇實現一個本身定製的監聽器:好比,實現並註冊用來處理LoadEvent的LoadEventListener接口,來處理全部的調用Session的load()方法的請求。 506 507 監聽器在運行時被實例化爲單例,也就是說,全部同類型的事件的處理共享同一個監聽器實例,所以監聽器不該該保存任何與請求相關的狀態。 508 用戶定製的監聽器須要實現相關的事件監聽器接口,或者從一個合適的基類繼承(甚至是從Higbernate自帶的默認事件監聽器繼承)。 509 510 用戶定製的監聽器能夠經過編程使用Configuration對象來註冊,也能夠在Hibernate的xml格式的配置文件中進行聲明。 511 512 你還須要修改一處配置,來告訴Hibernate,除了默認的監聽器,還要附加選定的監聽器。 513 <hibernate-configuration> 514 <session-factory> 515 .. 516 <event type="load"> 517 <listener class="com.eg.MyLoadListener"/> 518 <listener class="org.hibernate.event.def.DefaultLoadEventListener"> 519 </event> 520 </session-factory> 521 </hiberante-configuration> 522 523 經過編程的方式註冊 524 Configuration cfg = new Configuration(); 525 LoadEventListener[] stack={new MyLoadListener(),new DefaultLoadEvnetListener()}; 526 cfg.getEventListeners().setLoadEventListeners(stack); 527 528 經過在xml配置文件聲明而註冊的監聽器不能共享實例。若是在多個<listener/>節點中使用了相同類的名字,則每個引用都將會產生一個獨立的實例。若是你須要在多個監聽器類型之間共享監聽器的實例,則你必須使用編程的方式來進行註冊。 529 530 數據庫鏈接池(connection pool),c3p0,apache的dbcp,hibernate內置支持c3p0鏈接池 531 能夠主文件中用<property name="hibernate.c3p0.屬性"></property>來設置鏈接池 532 533 hibernate tool與ant實現代碼自動生成 534 在src目錄下建build.xml與build.properties 535 build.properties內容 536 src=src 537 dbschema=dbschema//schema存儲目錄 538 libs=libs//ant所須要的架文件,hibernate.tool架包 539 bin=bin 540 541 build.xml文件內容 542 <project name="hibernate_tools" basedir=".">(.表示當前目錄) 543 <property file="build.properties"></property>(指定屬性文件位置) 544 <target name="init"> 545 <path id ="lib.path"> 546 <pathelement path="${bin}"/> 547 <fileset dir="${libs}"> 548 <include name="**/*.jar"/>(引用目錄下全部文件) 549 </fileset> 550 </path> 551 </target> 552 553 <taskdef name="hibernatetools" classname="org.hibernate.tool.ant.HibernateTooTask" classpathref="lib.path">(定義任務) 554 </taskdef> 555 556 <target name="dbschema"> 557 <hibernatetools> 558 <configuration configurationfile="${src}/hibernate.cfg.xml"/> 559 <hbm2ddl destdir="${dbschema}" export="true" outputfilename="dbschema.sql"/> 560 <hbm2java jdk5="true" destdir="${src}"/> 561 </hibernatetools> 562 </target> 563 564 565 </project>