- 架構部分
1.1 Hibernate
1.1.1說一下Hibernate開發流程
- Hibernate開發環境搭建
- 導入Hibernate相關jar包即資料庫的驅動包
- 建立資料庫
- 在資料庫中建立一個表用來做測試
- 編寫POJO類
- 配置檔案
- Hibernate.cfg.xml(hibernate核心配置檔案),主要配置資料庫連接配接資訊,連接配接池資訊,方言,緩存,映射檔案等。
- User.hbm.xml(映射檔案),主要是配置對象和表中的映射資訊
- 編寫SessionFactory工具類
- 通過工具類擷取SessionFactory
- 通過SessionFactory建立Session
- 擷取到Session意味着和資料庫建立連接配接
- 開啟事務
- 操作資料庫
- 送出事務
- 關閉session
- Session關閉後連接配接釋放到連接配接池
- Hibernate中對象的三種狀态
- Session關閉後連接配接釋放到連接配接池
- 瞬時狀态(Transient)/臨時狀态/自由狀态:
對于剛建立的一個對象,如果session中和資料庫中都不存在該對象,那麼該對象就是瞬時對象。
- 持久(Persistent)
已經持久化(資料庫中有)并且已經交個session托管(session緩存中有),持久化對象在事務送出的時會把session中托管的對象和持久化對象進行比較,如果不同就發送一條update語句,否則不會發送update語句。
- 脫管(Detached)
資料庫存在該對象,但是該對象又沒有被session所托管。
-
-
- Hibernate緩存機制
-
Hibernate緩存有3中,一級緩存,二級緩存,查詢緩存。
- 一級緩存
- Hibernate的一級緩存是Session的緩存,Session内置的緩存是不能被解除安裝的
- 的表示号)
- 一級緩存存放的是資料庫資料的拷貝。
- 二級緩存
- Hibernate的二級緩存是SessionFactory級别的緩存,所有session共享一個二級緩存。
- Hibernate對二級緩存隻是提供了一個接口,具體的需要第三方來實作,比如Ehcache
- Hibernate的二級緩存預設是不啟用的,二級緩存存放中繼資料和預定義的SQL。
- 查詢緩存
- 查詢緩存主要是針對HQL的查詢
- 查詢緩存要依賴于二級緩存
- 查詢緩存中緩存的是預定義的SQL語句
-
- Hibernate中的查詢方式有哪些?
-
Hibernate中有四中查詢方式,分别是主鍵查詢,HQL,QBC,原生态SQL查詢。
- 主鍵查詢
User user = (User) session.get(User.class, userId); // 立即加載 User user1 = (User) session.load(User.class, userId); // 支援懶加載 |
- HQL查詢
Query query2 = session.createQuery("from User u where u.id = :id"); |
需要注意的是HQL是面向對象的查詢方式,裡面寫的都是對象或屬性是區分大小寫的。
- 原生态的SQL查詢
SQLQuery sqlQuery = session.createSQLQuery("select * from t_user u where u.id = :id") |
原生态的SQL查詢,适合使用複雜的查詢,或者不想使用HQL或者criteria查詢,可以使用本地SQL查詢,缺點是不能跨越資料庫,一般不适用,除非遇到複雜的sql語句才使用。
- Criteria查詢
Criteria criteria = session.createCriteria(User.class); List<User> list = criteria.list(); |
Criteria查詢叫做條件查詢也稱QBC(Query By Criteria)查詢,它完全面向對象的查詢方式,裡面把排序和分組都封裝了方法。
-
-
- mybatis和hibernate的差別
-
- MyBatis
- 優點
- 可以由開發者靈活的控制SQL語句,減少查詢字段。
- 所有的SQL語句都放在統一配置檔案中友善維護,管理,減少SQL和程式的耦合
- MyBatis是一個半自動化的ORM架構,對象資料以及對象實際關系仍然需要通過手寫sql來實 現和管理
- 支援動态編寫SQL
- 缺點
- 資料庫移植性很弱,不同的資料庫要編寫不同的SQL語句
- DAO層的方法不支援方法的重載
- 不支援級聯更新和删除
- Hibernate
- 優點
-
- Hibernate中的方言可以友善的做到資料庫的移植
- Hibernate中提供了一級緩存,二級緩存,查詢緩存來提高查詢效率,MyBatis中的緩存不佳
- Hibernate是個全自動化科技,Dao層的開發比較簡單,直接調用save方法即可。
-
- 缺點
-
- 多表關聯的時候比較複雜,使用成本不低
- SQL語句都是自動生成,對于SQL的優化,修改比較困難
-
- 總結
兩個架構各有千秋,如果項目中很少存在多表關聯查詢(比如10張表以上)那可以選擇使用Hiberate,否則還是選擇MyBatis,具體選擇哪個架構還是要根據項目來定。
mybatis:小巧、友善、高效、簡單、直接、半自動
hibernate:強大、友善、高效、複雜、繞彎子、全自動
-
-
- Hibernate和 JDBC優缺點對比
-
- 相同點
- 兩者都是JAVA的資料庫操作中間件。
- 兩者對于資料庫進行直接操作的對象都不是線程安全的,都需要及時關閉。
- 兩者都可以對資料庫的更新操作進行顯式的事務處理。
- 不同點
- 使用的SQL語言不同:JDBC使用的是基于關系型資料庫的标準SQL語言,Hibernate使用的是HQL(Hibernate query language)語言
- 操作的對象不同:JDBC操作的是資料,将資料通過SQL語句直接傳送到資料庫中執行,Hibernate操作的是持久化對象,由底層持久化對象的資料更新到資料庫中。
- 資料狀态不同:JDBC操作的資料是“瞬時”的,變量的值無法與資料庫中的值保持一緻,而Hibernate操作的資料是可持久的,即持久化對象的資料屬性的值是可以跟資料庫中的值保持一緻的。
- JDBC與Hibernate讀取性能
- JDBC仍然是最快的通路方式,不論是Write還是Read操作,都是JDBC快。
- Hibernate使用uuid.hex構造主鍵,性能稍微有點損失,但是不大。
- Create操作,JDBC在使用批處理的方式下速度比Hibernate快,使用批處理方式耗用JVM記憶體比不使用批處理方式要多得多。
- 讀取資料,Hibernate的Iterator速度非常緩慢,因為他是每次next的時候才去資料庫取資料,這一點從觀察任務管理器的java程序占用記憶體的變化也可以看得很清楚,記憶體是幾十K幾十K的增加。
- 讀取資料,Hibernate的List速度很快,因為他是一次性把資料取完,這一點從觀察任務管理器的java程序占用記憶體的變化也可以看得很清楚,記憶體幾乎是10M的10M的增加。
- JDBC讀取資料的方式和Hibernate的List方式是一樣的(這跟JDBC驅動有很大關系,不同的JDBC驅動,結果會很不一樣),這 從觀察java程序記憶體變化可以判斷出來,由于JDBC不需要像Hibernate那樣構造一堆Cat對象執行個體,是以占用JVM記憶體要比 Hibernate的List方式大概少一半左右。
- Hibernate的Iterator方式并非一無是處,它适合于從大的結果集中選取少量的資料,即不需要占用很多記憶體,又可以迅速得到結果。另外Iterator适合于使用JCS緩沖。
-
- Hibernate的ORM原理和實作
-
- Hibernate的ORM原理和實作
ORM的全稱是Object Relational Mapping,即對象關系映射。它的實作思想就是将關系資料庫中表的資料映射成為對象,以對象的形式展現,這樣開發人員就可以把對資料庫的操作轉化為對這些對象的操作。是以它的目的是為了友善開發人員以面向對象的思想來實作對資料庫的操作。
- Hibernate是如何實作映射的
在使用Hibernate實作ORM功能的時候,主要的檔案有:映射類(*.java)、映射檔案(*.hbm.xml)以及資料庫配置檔案(*.cfg.xml),它們各自的作用如下:
- 映射類
它的作用是描述資料庫表的結構,表中的字段在類中被描述成屬性,将來就可以實作把表中的記錄映射成為該類的對象。
- 映射檔案
它的作用是指定資料庫表和映射類之間的關系,包括映射類和資料庫表的對應關系、表字段和類屬性類型的對應關系以及表字段和類屬性名稱的對應關系等。
- 資料庫配置檔案
它的作用是指定與資料庫連接配接時需要的連接配接資訊,比如連接配接哪中資料庫、登入使用者名、登入密碼以及連接配接字元串等。
-
-
- get和load的差別
-
- get
- 調用後立即發送SQL語句查詢,傳回的實體對象。
- 查詢一個不存在的資料傳回null
- get的查詢順序是先到一級緩存à二級緩存à資料庫
- load
- 是懶加載,傳回的是個代理對象
- 查詢一個不存在的資料抛出異常
- 調用load後不會發送sql語句,傳回的對象是個代理的,這個對象中隻有id屬性有值,隻有調用該對象的其他屬性時才會發送sql語句查詢。
- Load預設是懶加載的機制,這種機制是可以通過設定class節點中的lazy屬性修改。
-
- 如何進行Hibernate優化
-
很多時候我們是在效率與安全/準确性上找一個平衡點,無論如何,優化都不是一個純技術的問題,你應該對你的應用和業務特征有足夠的了解,一般的,優化方案應在架構設計期就基本确定,否則可能導緻沒必要的返工,緻使項目延期,而作為架構師和用Hibernate與用Jdbc性能相差十幾倍很正常,如果不及早調整,很可能影響整個項目的進度。大體上,對于Hibernate性能調優的主要考慮點如下:
- 資料庫設計
- 降低關聯的複雜性
- 盡量不使用聯合主鍵
- ID的生成機制,不同的資料庫所提供的機制并不完全一樣
- 适當的備援資料,不過分追求高範式
- HQL優化
HQL如果抛開它同Hibernate本身一些緩存機制的關聯,HQL的優化技巧同普通的SQL優化技巧一樣。
- 主配置
- 查詢緩存,同下面講的緩存不太一樣,它是針對HQL語句的緩存,即完全一樣的語句再次執行時可以利用緩存資料。但是,查詢緩存在一個交易系統(資料變更頻繁,查詢條件相同的機率并不大)中可能會起反作用:它會白白耗費大量的系統資源但卻難以派上用場。
- batch_size,同JDBC的相關參數作用類似,參數并不是越大越好,而應根據業務特征去設定
- 生産系統中,切記要關掉SQL語句列印。
- 緩存
- SESSION緩存:在一個Hibernate Session有效,這級緩存的可幹預性不強,大多于HIBERNATE自動管理,但它提供清除緩存的方法,這在大批量增加/更新操作是有效的。比如,同時增加十萬條記錄,按正常方式進行,很可能會發現OutofMemeroy的異常,這時可能需要手動清除這一級緩存:Session.evict以及Session.clear
- 緩存有幾種形式,可以在映射檔案中配置:read-only(隻讀,适用于很少變更的靜态資料/曆史資料),nonstrict-read-write,read-write(比較普遍的形式,效率一般),transactional(JTA中,且支援的緩存産品較少)
- 延遲加載
- 實體延遲加載:通過使用動态代理實作
- 集合延遲加載:通過實作自有的SET/LIST
- 屬性延遲加載:通過load
- 事務控制
- 如果不涉及多個事務管理器事務的話,不需要使用JTA,隻有Jdbc的事務控制就可以。
- 使用标準的SQL事務隔離級别
- 批量操作
- 批量操作的時候直接使用JDBC
項目經理,還要面對開發人員可能的抱怨,必竟,我們對使用者需求更改的控制力不大,但技術/架構風險是應該在初期意識到并制定好相關的對策。應用層的緩存隻是錦上添花,永遠不要把它當救命稻草,應用的根基(資料庫設計,算法,高效的操作語句,恰當API的選擇等)才是最重要的。
-
-
- 什麼是Hibernate延遲加載
-
延遲加載,也叫懶加載,它是Hibernate為提高程式執行效率而提供的一種機制,即隻有真正使用該對象的資料時才會建立。
Hibernate中主要是通過代理機制來實作延遲加載。它的具體過程:Hibernate叢資料庫擷取某一個對象資料時、擷取某一個對象的集合屬性值時,或擷取某一個對象所關聯的另一個對象時,由于沒有使用該對象的資料,hibernate并不是資料庫加載真正的資料,而隻是為該對象建立一個代理對象(cglib生成)來代表這個對象,這個對象上的所有屬性都是預設值;隻有在真正需要使用該對象的資料時才建立這個真實對象,真正從資料庫中加載它的資料,這樣在某些情況下,就可以提高查詢效率。
舉個例子:加載某對象X時,并不會立即從資料庫中傳回該對象所有的屬性值,而是采用代理機制生成X對象的代理,當通路該代理對象的屬性時才從資料庫中加載該屬性值。
Hibernate的延遲加載提供了三大方面其中分别是:實體關聯、集合類、屬性的延遲加載。
-
-
- NoSession的問題原因及解決方案
-
- 問題産生的原因
因為Hibernate中調用load懶加載的查詢,當調完load後session就關閉了,因為我們的session隻是配置到了dao層,表現層擷取不到,是以在表現層調用的時候session就已經關閉,就爬出了NoSession異常。
- 解決方案
- 可以配置關聯關系時設定lazy屬性=false,立即加載方法,也可以提前使用資料,使其自動加載。
- 利用Spring提供的OpenSessionInViewFilter解決no session問題
- 說說session緩存的作用
- 減少通路資料庫的頻率。應用程式從緩存中讀取持久化對象的速度顯然要比資料庫中檢索資料的速度塊。進而session緩存可以提高查詢效率。
- 事務送出的時候會隐式調用flush(),保證了緩存中的資料和資料庫資料同步。
- 當緩存中持久化對象的狀态發生改變時,Session不會立即執行相關的SQL語句,這使得Session能夠把幾條相關的SQL語句合并為一條SQL語句,以減少資料庫的通路次數,提高通路效率。
-
- session的清理和清空有什麼差別
-
- 清理緩存
- 清理緩存調用的是session.flush()方法
- 清理緩存是指按照緩存中對象的狀态的變化來同步更新資料庫,但不清空緩存
- 清空緩存
- 清空緩存調用的是session.clear()方法
- 清空是把session的緩存置空,但不同步更新資料庫。
- 清空緩存後也就意味着關閉session
- Relish
- 根據資料庫的變化來同步更新session緩存
- Hibernate中session的特點有哪些?
- 根據資料庫的變化來同步更新session緩存
- 線程不安全,每個線程都應該從一個SessionFactory擷取自己的session執行個體。
- Session的主要功能是提供對映射的實體類執行個體的建立,讀取和删除操作。
- 如果其持久化對象類是可序列化的,則Session執行個體也是可序列化的。
-
- Hibernate三種檢索政策的優缺點對比
-
- 立即檢索
采用立即檢索政策,會将被檢索的對象,以及和這個對象關聯的一對多對象都加載到緩存中。Session的get方法就使用的立即檢索政策。
- 優點:頻繁使用的關聯對象能夠被加載到緩存中。
- 缺點
-
- 占用記憶體。
- Select語句過多。
-
- 延遲檢索
采用延遲檢索政策,就不會加載關聯對象的内容。直到第一次調用關聯對象時,才去加載關聯對象。在不涉及關聯類操作時,延遲檢索政策隻适用于Session的load方法。
在類級别操作時,延遲檢索政策,隻加載類的OID不加載類的其他屬性,隻用當第一次通路其他屬性時,才回通路資料庫去加載内容。(這裡使用了CGLIB生成了類的代理類)
在關聯級别操作時,延遲檢索政策,隻加載類本身,不加載關聯類,直到第一次調用關聯對象時,才去加載關聯對象程式模式都是用延遲加載政策。如果需要指定使用延遲加載政策。在配置檔案中設定<class>的lazy=true,<set>的lazy=true或extra(增強延遲)<many-to-one>的lazy=proxy和no-proxy。
- 優點:由程式決定加載哪些類和内容,避免了大量無用的sql語句和記憶體消耗。
- 缺點:在Session關閉後,就不能通路關聯類對象了。需要確定在Session.close方法前,調用關聯對象。
- 迫切左外連接配接檢索
采用左外連接配接檢索,能夠使用Sql的外連接配接查詢,将需要加載的關聯對象加載在緩存中。
<set>fetch設定為join,<many-to-one>的fetch設定為 join。
- 優點:
- 對應用程式完全透明,不管對象處于持久化狀态,還是遊離狀态,應用程式都可以友善的從一個對象導航到與它關聯的對象。
- 使用了外連接配接,select語句數目少。
- 缺點:
-
- 可能會加載應用程式不需要通路的對象,白白浪費許多記憶體空間。
- 複雜的資料庫表連接配接也會影響檢索性能。
-
-
-
- 什麼是悲觀鎖和樂觀鎖?
-
- 什麼是鎖
為什麼需要鎖?在多使用者環境中,在同一時間可能會有多個使用者更新相同的記錄,這會産生沖突,這就是著名的并發性問題。而鎖就可以解決這個問題,即給我們標明的目标資料上鎖,使其無法被其他程式修改。
- 悲觀鎖
悲觀鎖是指假設并發更新沖突會發生,是以不管沖突是否真的發生,都會使用鎖機制。悲觀鎖會鎖住讀取的記錄,防止其它事務讀取和更新這些記錄。其它事務會一直阻塞,直到這個事務結束。悲觀鎖是在使用了資料庫的事務隔離功能的基礎上,獨享占用的資源,以此保證讀取資料一緻性,避免修改丢失。
資料庫悲觀鎖實作
select * from t_user where id = 1 for update |
這條 sql語句鎖定了 t_user表中id為1的記錄。本次事務送出之前(事務送出時會釋放事務過程中的鎖),外界無法修改這些記錄。
Hibernate的悲觀鎖,也是基于資料庫的鎖機制實作。下面的代碼實作了對查詢記錄的加鎖。
主鍵查詢加鎖:
User user= (User)session.get(User.class, 1, LockOptions.UPGRADE); |
Hql查詢加鎖:
Query query = session.createQuery("from User u where u.id = 1"); query.setLockOptions(LockOptions.UPGRADE); |
- 樂觀鎖
總是認為不會産生并發問題,每次去取資料的時候總認為不會有其他線程對資料進行修改,是以不會上鎖,但是在更新時會判斷其他線程在這之前有沒有對資料進行修改,一般會使用版本号機制或CAS操作實作。
樂觀鎖的工作原理:讀取出資料時,将此版本号一同讀出,之後更新時,對此版本号加一。此時,将送出資料的版本資料與資料庫表對應記錄的目前版本資訊進行比對,如果送出的資料版本号大于資料庫表目前版本号,則予以更新,否則認為是過期資料。
Hibernate中實作:表中添加一個version字段,對象中添加一個version屬性,在映射檔案中建立它們之間的關系就可以實作,這個version字段由hibernate幫我們維護。
-
-
- 什麼ORM?
-
ORM概念
ORM,即Object-Relational Mapping(對象關系映射),它的作用是在關系型資料庫和業務實體對象之間作一個映射,這樣,我們在具體的操作業務對象的時候,就不需要再去和複雜的SQL語句打交道,隻需簡單的操作對象的屬性和方法。
先從項目中資料流存儲形式這個角度說起.簡單拿MVC這種分層模式.來說. Model作為資料承載實體. 在使用者界面層和業務邏輯層之間資料實作面向對象的形式傳遞. 當我們需要通過Controller層分發請求把資料持久化時我們會發現,記憶體中的面向對象如何持久化成關系型資料中存儲一條實際資料記錄呢?面向對象是從軟體工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關系資料庫則是從數學理論發展而來的. 兩者之間是不比對的.而ORM作為項目中間件形式實作資料在不同場景下資料關系映射. 對象關系映射(Object Relational Mapping,簡稱ORM)是一種為了解決面向對象與關系資料庫存在的互不比對的現象的技術.ORM就是這樣而來的。
ORM優缺點
優點:
第一:隐藏了資料通路細節,“封閉”的通用資料庫互動,ORM的核心。他使得我們的通用資料庫互動變得簡單易行,并且完全不用考慮該死的SQL語句。快速開發,由此而來。
第二:ORM使我們構造固化資料結構變得簡單易行。在ORM年表的史前時代,我們需要将我們的對象模型轉化為一條一條的SQL語句,通過直連或是DB helper在關系資料庫構造我們的資料庫體系。而現在,基本上所有的ORM架構都提供了通過對象模型構造關系資料庫結構的功能。這,相當不錯。
缺點:
第一:無可避免的,自動化意味着映射和關聯管理,代價是犧牲性能。現在的各種ORM架構都在嘗試使用各種方法來減輕這塊(LazyLoad,Cache),效果還是很顯著的。
第二:面向對象的查詢語言(X-QL)作為一種資料庫與對象之間的過渡,雖然隐藏了資料層面的業務抽象,但并不能完全的屏蔽掉資料庫層的設計,并且無疑将增加學習成本.
第三:對于複雜查詢,ORM仍然力不從心。
世上沒有驢是不吃草的(又想好又想巧,買個老驢不吃草),任何優勢的背後都隐藏着缺點,這是不可避免的。問題在于,我們是否能容忍缺點。
常用的ORM架構
-
- Hibernate全自動需要些hql語句
- MyBatis半自動自己寫sql語句,可操作性強,小巧
- EclipseLink 一個可擴充的支援JPA的ORM架構,供強大的緩存功能,緩存支援叢集。
- Apache OJB
1.2 MyBatis
1.2.1 MyBatis中#和$符号差別
- #{name}
- 被解析為jdbc的預編譯語句每個#{}代表一個占位符,比如 where name =?
- 這種方式不會出現SQL注入的問題
- 比較常用
舉個例子:
<select id="getUserById" resultMap="userMap"> select * from t_User where id = #{id} </select> |
解析後的SQL如下:
- ${name}
- 将内容直接寫到SQL語句中,比如:where name = admin
- 這種方式無法防止SLQ注入的問題
- 一般用于動态表或者動态字段排序
舉個列子:
<select id="getUserById" resultMap="userMap"> select * from t_User where id = ${id} </select> |
解析後的SQL如下:
1.2.2 MyBatis開發流程
- 導入Mybatis相關jar包
- 配置檔案
- MyBatis核心配置檔案:主要配置連接配接資料庫參數,連接配接池,插件,Mapper檔案等。
- Mapper檔案:主要配置調用的sql
- 建立SqlSessionFactoryBuilder
- 通過SqlSessionFactoryBuilder來建立SqlSessionFactory
- 通過SqlSessionFactory建立SqlSession
- 通過SqlSession操作資料庫
- 調用sqlSession.commit()送出事務
- 關閉sqlSession.close()關閉session
1.2.3 JDBC和MyBatis的對比?
JDBC是Java提供的一個操作資料庫的API,我們平時使用jdbc進行程式設計,需要如下幾個步驟。
- 使用jdbc程式設計需要連接配接資料庫,注冊驅動和資料庫資訊
- 操作Connection,打開Statement對象
- 通過Statement對象執行SQL,傳回結果到ResultSet對象
- 使用ResultSet讀取資料,然後通過代碼轉化為具體的POJO對象
- 關閉資料庫相關的資源
- JDBC缺點
- 工作量比較大,需要連接配接資料庫,然後處理jdbc底層事務,處理資料類型,還需要操作Connection,Statement對象和ResultSet對象最後還需要關閉它們。
- 頻繁的建立和釋放連接配接造成資源浪費進而影響性能
- SQL語句寫在代碼中造成代碼不易維護,修改SQL需要改變Java代碼。
- 對結果集的解析麻煩,需要手動轉化為POJO對象
- 向preparedStatement中設定參數,對占位符号位置和設定參數值,寫死在java代碼中
因為JDBC實在不好用,于是出現了ORM對Jdbc進行封裝,MyBatis對JDBC的封裝很好,幾乎可以取代Jdbc。
- MyBatis解決JDBC的以下缺點
- MyBatis使用SqlSessionFactoryBuilder來連接配接完成JDBC需要代碼完成的資料庫擷取和連接配接,減少了代碼的重複。
- MyBatis可以将SQL代碼寫入xml中,易于修改和維護。
- MyBatis的mapper會自動将執行後的結果映射到對應的Java對象中
- MyBatis的編碼風格統一優雅、性能高,靈活性好。
- jdbc,mybatis,hibernate的差別
-
- 從層次上看
JDBC是較底層的持久層操作方式,而Hibernate和MyBatis都是在JDBC的基礎上進行了封裝使其更加友善程式員對持久層的操作。
-
- 從功能上看
JDBC就是簡單的建立資料庫連接配接,然後建立statement,将sql語句傳給statement去執行,如果是有傳回結果的查詢語句,會将查詢結果放到ResultSet對象中,通過對ResultSet對象的周遊操作來擷取資料;Hibernate是将資料庫中的資料表映射為持久層的Java對象,對sql語句進行修改和優化比較困難;MyBatis是将sql語句中的輸入參數和輸出參數映射為java對象,sql修改和優化比較友善.
-
- 從使用上看
如果進行底層程式設計,而且對性能要求極高的話,應該采用JDBC的方式;如果要對資料庫進行完整性控制的話建議使用Hibernate;如果要靈活使用sql語句的話建議采用MyBatis架構。
1.2.3使用MyBatis中Mapper接口使用?
- Mapper接口方法名和mapper.xml中定義的每個sql的id相同
- Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
- Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
- Mapper.xml檔案中的namespace即是mapper接口的類路徑。
- Mapper檔案中的slq标簽的id盡量不要重複。
1.2.4 MyBatis中的一級緩存和二級緩存
-
- 一級緩存
一級緩存是SqlSession級别的緩存。在操作資料庫時需要構造sqlSession對象,在對象中有一個資料結構(HashMap)用于存儲緩存資料。不同的sqlSession之間的緩存資料區域(HashMap)是互相不影響的,一級緩存預設是開啟的。
一級緩存有如下特點:
- 第一次發起查詢先去找緩存中是否有,如果沒有,從資料庫查詢,然後将查詢結果存儲到一級緩存中。
- 如果中間sqlSession去執行commit操作(執行插入、更新、删除),則會清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的資訊,避免髒讀。
- 第二次發起查詢資料,先去找緩存中是否,緩存中有,直接從緩存中擷取資料傳回。
- 二級緩存
二級緩存的原理和一級緩存原理一樣,第一次查詢,會将資料放入緩存中,然後第二次查詢則會直接去緩存中取。但是一級緩存是基于 sqlSession 的,而 二級緩存是基于 mapper檔案的namespace的,也就是說多個sqlSession可以共享一個mapper中的二級緩存區域,并且如果兩個mapper的namespace相同,即使是兩個mapper,那麼這兩個mapper中執行sql查詢到的資料也将存在相同的二級緩存區域中。
-
-
-
- 二級緩存的特點如下:
-
-
- 二級緩存需要手動開啟
- 放在二級緩存中的對象需要實作Serializable接口(因為二級緩存資料存儲媒體多種多樣,不一定隻存在記憶體中,有可能存在硬碟中,如果我們要再取這個緩存的話,就需要反序列化了。是以mybatis中的pojo都去實作Serializable接口)。
- 執行commit操作二級緩存資料将會清空。
- 預設事務送出的時候會清空二級緩存資料
-
-
-
- useCache和flushCache
-
-
userCache是用來設定是否禁用二級緩存的,在statement中設定useCache=false可以禁用目前select語句的二級緩存,即每次查詢都會發出sql去查詢,預設情況是true,即該sql使用二級緩存。
<select id="getUserById" resultMap="userMap" useCache="true"> Select * from t_User where id = #{id} </select |
在mapper的同一個namespace中,如果有其它insert、update、delete操作資料後需要重新整理緩存,如果不執行重新整理緩存會出現髒讀。設定statement配置中的flushCache=”true” 屬性,預設情況下為true,即重新整理緩存,如果改成false則不會重新整理。使用緩存時如果手動修改資料庫表中的查詢資料會出現髒讀。
<update id="updateUser" flushCache="true"> update t_user set name = #{username} where id = #{id} </update> |
1.2.5 MyBatis插入如何傳回主鍵?
-
- MySQl用法
<insert id="addUser" useGeneratedKeys="true" keyProperty="userId"> insert into t_user (username,password) values(#{username},#{password}); </insert> |
useGeneratedKeys:取值範圍true|false(預設值),設定是否使用JDBC的getGenereatedKeys方法擷取主鍵并指派到keyProperty設定的領域模型屬性中。
keyProperty :使用者設定主鍵回填的到那個屬性。以上述例子為列,如果調用addUser方法時傳遞的是user對象則把主鍵設定到user對象的userId屬性中(沒有屬性則不設定),如果傳遞的是個Map則給Map中添加一個鍵值對,key是userId,value是主鍵值。
-
- Oracle用法
<insert id="updateUser" > <selectKey resultType="INTEGER" order="BEFORE" keyProperty="userId"> SELECT SEQ_USER.NEXTVAL as userId from DUAL </selectKey> insert into t_user (user_id, user_name) values (#{userId}, #{userName}) </insert> |
由于Oracle沒有自增長一說法,隻有序列這種模仿自增的形式,是以不能再使用“useGeneratedKeys”屬性。
1.2.6 MyBatis參數的傳遞有幾種?常用有哪些
- 采用map的方式傳遞
- 采用注解的方式
- 采用對象的方式
- 采用索引擷取
如果方法的形參超過3個建議使用Map或者封裝成對象,如果形參少于3個就使用注解,按照索引擷取不推薦使用,因為可讀性不強。
1.2.7說說MyBatis中ResultMap都是怎麼用的?
ResultMap标簽一般用在三種地方,
第一種:對象屬性和表中字段不一緻可以用ResultMap建立映射關系。
第二種:級聯查詢。
第三種:多個ResultMap可以繼承。
1.3 SpringMVC
1.3.1 SpringMVC工作原理
SpringMVC原理:
SpringMVC執行流程:
- 使用者發送請求至前端控制器DispatcherServlet。
- DispatcherServlet收到請求調用HandlerMapping映射器。
- 映射器找到具體的處理器(可以根據xml配置、注解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一并傳回給DispatcherServlet。
- DispatcherServlet調用HandlerAdapter處理器。
- HandlerAdapter經過适配調用具體的處理器(Controller,也叫後端控制器)。
- Controller執行完成傳回ModelAndView。
- HandlerAdapter将controller執行結果ModelAndView傳回給DispatcherServlet。
- DispatcherServlet将ModelAndView傳給ViewReslover視圖解析器。
- ViewReslover解析後傳回具體View。
- DispatcherServlet根據View進行渲染視圖(即将模型資料填充至視圖中)。
- DispatcherServlet響應使用者。
SpringMVC中的重要元件:
- 前端控制器DispatcherServlet:作用是:接收請求、響應結果,相當于轉發器;
DispatcherServlet相當于中央處理器,有了它就減少了其他元件之間的耦合。
- 處理器映射器HandlerMapping:作用是:根據請求的url查找Handler。
- 處理器擴充卡HandlerAdapter:作用是:按照特定的規則(HandlerAdapter要求的規則)去執行Handler。
- 視圖解析器ViewResolver:作用是:根據邏輯視圖名解析真正的視圖(View)。
- 視圖 View:View是一個接口,實作類支援不同的View類型(jsp、freemarker等)。
1.3.2 SpringMVC的常用注解有哪些?
- 元件行型注解
注解 | 說明 |
@Component | 類定義之前添加@Component注解,他會被spring容器識别,并轉為bean |
@Repository | 對Dao實作類進行注解(特殊的@Component) |
@Service | 用于對業務邏輯層進行注解(特殊的@Component) |
@Controller | 用于控制層注解(特殊的@Component) |
以上四種注解都是注解在類上的,被注解的類将被spring初始話為一個bean,然後統一管理。
- 請求和參數型注解
- @RequestMapping:用于處理請求位址映射,可以作用于類和方法上。
-
- value:定義request請求的映射位址
- method:請求的方式,預設接受get請求,如果請求方式和定義的方式不一樣則請求無法成功。
- params:定義request請求中必須包含的參數值。
-
headers:定義request請求中必須包含某些指定的請求頭,如:RequestMapping(value = "/something", headers = "content-type=text
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1.系統服務
tm.start();
tx.beign();
// 2.調用真實業務主體
Object result = method.invoke(target, args);
// 2.系統服務結束
tx.commit();
tm.end();
return result;
}
}
測試
@Test
public void testAdd2() {
// 1.建立目标對象
IUserService userService = new UserServiceImpl();
// 2.增強
TransactionManager tx = new TransactionManager();
TimeManager tm = new TimeManager();
// 3.建立代理
ClassLoader classLoader = userService.getClass().getClassLoader(); // 目标對象類加載器
Class<?>[] interfaces = userService.getClass().getInterfaces(); // 目标對象接口
ProxyHanlder proxyHanlder = new ProxyHanlder(tx, tm, userService); // InvocationHandler
IUserService userServiceProxy = (IUserService)Proxy.newProxyInstance(classLoader, interfaces, proxyHanlder);
// 3.調用方法
userServiceProxy.add();
}
注意: JDK動态代理有一個限制,即它隻能為接口建立代理執行個體。
CGLIB實作動态代理
CGLib采用非常底層的位元組碼技術,可以在運作時為一個類建立子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用。
以上面的例子為例,目标對象的接口和實作類不變,隻需要改變動态代理的handler即可,需改代碼如下:
測試public class ProxyHanlder implements MethodInterceptor {
// 1.系統服務
private TransactionManager tx;
private TimeManager tm;
public ProxyHanlder(TransactionManager tx, TimeManager tm) {
this.tx = tx;
this.tm = tm;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 1.系統服務
tm.start();
tx.beign();
// 2.調用目标對象
Object result = proxy.invokeSuper(obj, args); // 調用父類的方法
tx.commit();
tm.end();
return result;
}
}
@Test
public void testAdd2() {
// 1.建立目标對象
IUserService userService = new UserServiceImpl();
// 2.增強
TransactionManager tx = new TransactionManager();
TimeManager tm = new TimeManager();
// 3.建立代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(userService.getClass()); // 設定父類(目标對象)
enhancer.setCallback(new ProxyHanlder(tx, tm));
IUserService userServiceProxy =(IUserService) enhancer.create(); // 建立代理類
// 3.調用方法
userServiceProxy.add();
}
注意: CGLIB動态代理有一個限制,不能給final修飾的類建立代理。
之後對兩者做了一個效率對比,在自己本機上通過System.nanoTime()對兩者做了記錄,結果如下。
No JDK動态代理 CGLiib 建立代理對象時間 720 1394 1 3473 7007 代理對象執行方法時間 97 7322 15 2080 一個建立花費時間長,一個執行時間長。
AOP的引用場景
事務,日志,緩存,權限,異常處理等。
1.4.25 在 Spring AOP 中,關注點和橫切關注的差別是什麼?
關注點是系統中的一個行為,也就是真實業務主體,橫切關注就是系統服務。1.4.26 Spring哪幾種類型?
- before:前置通知,在一個方法執行前被調用。
- after: 在方法執行之後調用的通知,無論方法執行是否成功。
- after-returning: 僅當方法成功完成後執行的通知。
- after-throwing: 在方法抛出異常退出時執行的通知。
- around: 在方法執行之前和之後調用的通知。
- Shiro
1.5.1簡單介紹一下 Shiro 架構
Apache Shiro 是 Java 的一個安全(權限)架構。 Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在 JavaSE 環境,也可以用在 JavaEE 環境。使用Shiro的人越來越多,因為它相當簡單,對比Spring Security,可能沒有Spring Security做的功能強大,但是在實際工作時可能并不需要那麼複雜的東西,是以使用小而簡單的Shiro就足夠了。
Shiro 可以完成:認證、授權、加密、會話管理、與Web 內建、緩存 等。
1.5.2 Shiro三個核心元件
Subject :正與系統進行互動的人,或某一個第三方服務。所有 Subject 執行個體都被綁定到(且這是必須的)一個SecurityManager 上。
SecurityManager:Shiro 架構的心髒,用來協調内部各安全元件,管理内部元件執行個體,并通過它來提供安全管理的各種服務。當 Shiro 與一個 Subject 進行互動時,實質上是幕後的 SecurityManager 處理所有繁重的 Subject 安全操作。
Realms :本質上是一個特定安全的 DAO。當配置 Shiro 時,必須指定至少一個 Realm 用來進行身份驗證和/或授權。Shiro 提供了多種可用的 Realms 來擷取安全相關的資料。如關系資料庫(JDBC),INI 及屬性檔案等。可以定義自己 Realm 實作來代表自定義的資料源
1.5.3 Shiro 的四大核心部分
Authentication(身份驗證):簡稱為“登入”,即證明使用者是誰。
Authorization(授權):通路控制的過程,即決定是否有權限去通路受保護的資源。
Session Management(會話管理):管理使用者特定的會話,即使在非 Web 或 EJB 應用程式。
Cryptography(加密):通過使用加密算法保持資料安全
1.5.4 shiro能解決什麼問題?
- 認證:驗證使用者的身份
- 授權:對使用者執行通路控制:判斷使用者是否被允許做某事
- 會話管理:在任何環境下使用 Session API,即使沒有 Web 或EJB 容器。
- 加密:以更簡潔易用的方式使用加密功能,保護或隐藏資料防止被偷窺
- Realms:聚集一個或多個使用者安全資料的資料源
1.5.5 Shiro 的四種權限控制方式
1.程式設計式
通過寫if/else授權代碼塊完成:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")) {
//有權限
} else {
//無權限
}
2.注解式
通過在執行的Java方法上放置相應的注解完成:
@RequiresRoles("admin") // 擁有admin角色才能通路該方法
public void hello() {
//有權限
}
// 擁有user:update權限才能方法該方法
@RequiresPermissions("user:update")
@RequestMapping(value = "/update")
public String update() {
System.out.println("UserController.update()");
return "ok";
}
如果要使用注解使用,首先要在配置檔案中開啟注解(SpringMVC配置檔案中)
<!-- 啟動shiro注解 -->
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
3. 頁面标簽權限控制
在JSP/GSP頁面通過相應的标簽完成:
4.url 級别權限控制<shiro:hasRole name="admin">
<!— 有權限 —>
</shiro:hasRole>
測試<property name="filterChainDefinitions">
<value>
/adminhasRole*接口,其會委托給SecurityManager,而SecurityManager接着會委托給Authorizer;
- Authorizer是真正的授權者,如果我們調用如isPermitted(“user:view”),其首先會通過PermissionResolver把字元串轉換成相應的Permission執行個體;
- 在進行授權之前,其會調用相應的Realm擷取Subject相應的角色/權限用于比對傳入的角色/權限;
- Authorizer會判斷Realm的角色/權限是否和傳入的比對,如果有多個Realm,會委托給ModularRealmAuthorizer進行循環判斷,如果比對如isPermitted*/hasRole*會傳回true,否則傳回false表示授權失敗。
ModularRealmAuthorizer進行多Realm比對流程:
1、首先檢查相應的Realm是否實作了實作了Authorizer;
2、如果實作了Authorizer,那麼接着調用其相應的isPermitted*/hasRole*接口進行比對;
3、如果有一個Realm比對那麼将傳回true,否則傳回false。
1.5.7 Shiro認證流程
在shiro中,使用者需要提供principals (身份)和credentials(證明)給shiro,進而應用能驗證使用者身份。
principals:身份,即主體的辨別屬性,可以是任何東西,如使用者名、郵箱等,唯一即可。一個主體可以有多個principals,但隻有一個Primary principals,一般是使用者名/密碼/手機号。
credentials:證明/憑證,即隻有主體知道的安全值,如密碼/數字證書等。
最常見的principals和credentials組合就是使用者名/密碼了。接下來先進行一個基本的身份認證。
認證流程如下:
- 首先調用Subject.login(token)進行登入,其會自動委托給Security Manager,調用之前必須通過SecurityUtils. setSecurityManager()設定;
- SecurityManager負責真正的身份驗證邏輯;它會委托給Authenticator進行身份驗證;
- Authenticator才是真正的身份驗證者,Shiro API中核心的身份認證入口點,此處可以自定義插入自己的實作;
- Authenticator可能會委托給相應的AuthenticationStrategy進行多Realm身份驗證,預設ModularRealmAuthenticator會調用AuthenticationStrategy進行多Realm身份驗證;
- Authenticator會把相應的token傳入Realm,從Realm擷取身份驗證資訊,如果沒有傳回/抛出異常表示身份驗證失敗了。此處可以配置多個Realm,将按照相應的順序及政策進行通路。
- 架構部分
-
1.1 Hibernate
1.1.1說一下Hibernate開發流程
- Hibernate開發環境搭建
- 導入Hibernate相關jar包即資料庫的驅動包
- 建立資料庫
- 在資料庫中建立一個表用來做測試
- 編寫POJO類
- 配置檔案
- Hibernate.cfg.xml(hibernate核心配置檔案),主要配置資料庫連接配接資訊,連接配接池資訊,方言,緩存,映射檔案等。
- User.hbm.xml(映射檔案),主要是配置對象和表中的映射資訊
- 編寫SessionFactory工具類
- 通過工具類擷取SessionFactory
- 通過SessionFactory建立Session
- 擷取到Session意味着和資料庫建立連接配接
- 開啟事務
- 操作資料庫
- 送出事務
- 關閉session
- Session關閉後連接配接釋放到連接配接池
- Hibernate中對象的三種狀态
- Session關閉後連接配接釋放到連接配接池
- 瞬時狀态(Transient)/臨時狀态/自由狀态:
- 對于剛建立的一個對象,如果session中和資料庫中都不存在該對象,那麼該對象就是瞬時對象。
- 持久(Persistent)
- 已經持久化(資料庫中有)并且已經交個session托管(session緩存中有),持久化對象在事務送出的時會把session中托管的對象和持久化對象進行比較,如果不同就發送一條update語句,否則不會發送update語句。
- 脫管(Detached)
- 資料庫存在該對象,但是該對象又沒有被session所托管。
-
-
- Hibernate緩存機制
-
- Hibernate緩存有3中,一級緩存,二級緩存,查詢緩存。
- 一級緩存
- Hibernate的一級緩存是Session的緩存,Session内置的緩存是不能被解除安裝的
- 的表示号)
- 一級緩存存放的是資料庫資料的拷貝。
- 二級緩存
- Hibernate的二級緩存是SessionFactory級别的緩存,所有session共享一個二級緩存。
- Hibernate對二級緩存隻是提供了一個接口,具體的需要第三方來實作,比如Ehcache
- Hibernate的二級緩存預設是不啟用的,二級緩存存放中繼資料和預定義的SQL。
- 查詢緩存
- 查詢緩存主要是針對HQL的查詢
- 查詢緩存要依賴于二級緩存
- 查詢緩存中緩存的是預定義的SQL語句
-
- Hibernate中的查詢方式有哪些?
-
- Hibernate中有四中查詢方式,分别是主鍵查詢,HQL,QBC,原生态SQL查詢。
- 主鍵查詢
-
User user = (User) session.get(User.class, userId); // 立即加載
User user1 = (User) session.load(User.class, userId); // 支援懶加載
- HQL查詢
- 需要注意的是HQL是面向對象的查詢方式,裡面寫的都是對象或屬性是區分大小寫的。
Query query2 = session.createQuery("from User u where u.id = :id"); - 原生态的SQL查詢
- 原生态的SQL查詢,适合使用複雜的查詢,或者不想使用HQL或者criteria查詢,可以使用本地SQL查詢,缺點是不能跨越資料庫,一般不适用,除非遇到複雜的sql語句才使用。
SQLQuery sqlQuery = session.createSQLQuery("select * from t_user u where u.id = :id") - Criteria查詢
- Criteria查詢叫做條件查詢也稱QBC(Query By Criteria)查詢,它完全面向對象的查詢方式,裡面把排序和分組都封裝了方法。
Criteria criteria = session.createCriteria(User.class);
List<User> list = criteria.list();
-
-
- mybatis和hibernate的差別
-
- MyBatis
- 優點
- 可以由開發者靈活的控制SQL語句,減少查詢字段。
- 所有的SQL語句都放在統一配置檔案中友善維護,管理,減少SQL和程式的耦合
- MyBatis是一個半自動化的ORM架構,對象資料以及對象實際關系仍然需要通過手寫sql來實 現和管理
- 支援動态編寫SQL
- 缺點
- 資料庫移植性很弱,不同的資料庫要編寫不同的SQL語句
- DAO層的方法不支援方法的重載
- 不支援級聯更新和删除
- Hibernate
- 優點
-
- Hibernate中的方言可以友善的做到資料庫的移植
- Hibernate中提供了一級緩存,二級緩存,查詢緩存來提高查詢效率,MyBatis中的緩存不佳
- Hibernate是個全自動化科技,Dao層的開發比較簡單,直接調用save方法即可。
-
- 缺點
-
- 多表關聯的時候比較複雜,使用成本不低
- SQL語句都是自動生成,對于SQL的優化,修改比較困難
-
- 總結
-
兩個架構各有千秋,如果項目中很少存在多表關聯查詢(比如10張表以上)那可以選擇使用Hiberate,否則還是選擇MyBatis,具體選擇哪個架構還是要根據項目來定。
mybatis:小巧、友善、高效、簡單、直接、半自動
hibernate:強大、友善、高效、複雜、繞彎子、全自動
-
-
- Hibernate和 JDBC優缺點對比
-
- 相同點
- 兩者都是JAVA的資料庫操作中間件。
- 兩者對于資料庫進行直接操作的對象都不是線程安全的,都需要及時關閉。
- 兩者都可以對資料庫的更新操作進行顯式的事務處理。
- 不同點
- 使用的SQL語言不同:JDBC使用的是基于關系型資料庫的标準SQL語言,Hibernate使用的是HQL(Hibernate query language)語言
- 操作的對象不同:JDBC操作的是資料,将資料通過SQL語句直接傳送到資料庫中執行,Hibernate操作的是持久化對象,由底層持久化對象的資料更新到資料庫中。
- 資料狀态不同:JDBC操作的資料是“瞬時”的,變量的值無法與資料庫中的值保持一緻,而Hibernate操作的資料是可持久的,即持久化對象的資料屬性的值是可以跟資料庫中的值保持一緻的。
- JDBC與Hibernate讀取性能
- JDBC仍然是最快的通路方式,不論是Write還是Read操作,都是JDBC快。
- Hibernate使用uuid.hex構造主鍵,性能稍微有點損失,但是不大。
- Create操作,JDBC在使用批處理的方式下速度比Hibernate快,使用批處理方式耗用JVM記憶體比不使用批處理方式要多得多。
- 讀取資料,Hibernate的Iterator速度非常緩慢,因為他是每次next的時候才去資料庫取資料,這一點從觀察任務管理器的java程序占用記憶體的變化也可以看得很清楚,記憶體是幾十K幾十K的增加。
- 讀取資料,Hibernate的List速度很快,因為他是一次性把資料取完,這一點從觀察任務管理器的java程序占用記憶體的變化也可以看得很清楚,記憶體幾乎是10M的10M的增加。
- JDBC讀取資料的方式和Hibernate的List方式是一樣的(這跟JDBC驅動有很大關系,不同的JDBC驅動,結果會很不一樣),這 從觀察java程序記憶體變化可以判斷出來,由于JDBC不需要像Hibernate那樣構造一堆Cat對象執行個體,是以占用JVM記憶體要比 Hibernate的List方式大概少一半左右。
- Hibernate的Iterator方式并非一無是處,它适合于從大的結果集中選取少量的資料,即不需要占用很多記憶體,又可以迅速得到結果。另外Iterator适合于使用JCS緩沖。
-
- Hibernate的ORM原理和實作
-
- Hibernate的ORM原理和實作
- ORM的全稱是Object Relational Mapping,即對象關系映射。它的實作思想就是将關系資料庫中表的資料映射成為對象,以對象的形式展現,這樣開發人員就可以把對資料庫的操作轉化為對這些對象的操作。是以它的目的是為了友善開發人員以面向對象的思想來實作對資料庫的操作。
- Hibernate是如何實作映射的
- 在使用Hibernate實作ORM功能的時候,主要的檔案有:映射類(*.java)、映射檔案(*.hbm.xml)以及資料庫配置檔案(*.cfg.xml),它們各自的作用如下:
- 映射類
- 它的作用是描述資料庫表的結構,表中的字段在類中被描述成屬性,将來就可以實作把表中的記錄映射成為該類的對象。
- 映射檔案
- 它的作用是指定資料庫表和映射類之間的關系,包括映射類和資料庫表的對應關系、表字段和類屬性類型的對應關系以及表字段和類屬性名稱的對應關系等。
- 資料庫配置檔案
- 它的作用是指定與資料庫連接配接時需要的連接配接資訊,比如連接配接哪中資料庫、登入使用者名、登入密碼以及連接配接字元串等。
-
-
- get和load的差別
-
- get
- 調用後立即發送SQL語句查詢,傳回的實體對象。
- 查詢一個不存在的資料傳回null
- get的查詢順序是先到一級緩存à二級緩存à資料庫
- load
- 是懶加載,傳回的是個代理對象
- 查詢一個不存在的資料抛出異常
- 調用load後不會發送sql語句,傳回的對象是個代理的,這個對象中隻有id屬性有值,隻有調用該對象的其他屬性時才會發送sql語句查詢。
- Load預設是懶加載的機制,這種機制是可以通過設定class節點中的lazy屬性修改。
-
- 如何進行Hibernate優化
-
- 很多時候我們是在效率與安全/準确性上找一個平衡點,無論如何,優化都不是一個純技術的問題,你應該對你的應用和業務特征有足夠的了解,一般的,優化方案應在架構設計期就基本确定,否則可能導緻沒必要的返工,緻使項目延期,而作為架構師和用Hibernate與用Jdbc性能相差十幾倍很正常,如果不及早調整,很可能影響整個項目的進度。大體上,對于Hibernate性能調優的主要考慮點如下:
- 資料庫設計
- 降低關聯的複雜性
- 盡量不使用聯合主鍵
- ID的生成機制,不同的資料庫所提供的機制并不完全一樣
- 适當的備援資料,不過分追求高範式
- HQL優化
- HQL如果抛開它同Hibernate本身一些緩存機制的關聯,HQL的優化技巧同普通的SQL優化技巧一樣。
- 主配置
- 查詢緩存,同下面講的緩存不太一樣,它是針對HQL語句的緩存,即完全一樣的語句再次執行時可以利用緩存資料。但是,查詢緩存在一個交易系統(資料變更頻繁,查詢條件相同的機率并不大)中可能會起反作用:它會白白耗費大量的系統資源但卻難以派上用場。
- batch_size,同JDBC的相關參數作用類似,參數并不是越大越好,而應根據業務特征去設定
- 生産系統中,切記要關掉SQL語句列印。
- 緩存
- SESSION緩存:在一個Hibernate Session有效,這級緩存的可幹預性不強,大多于HIBERNATE自動管理,但它提供清除緩存的方法,這在大批量增加/更新操作是有效的。比如,同時增加十萬條記錄,按正常方式進行,很可能會發現OutofMemeroy的異常,這時可能需要手動清除這一級緩存:Session.evict以及Session.clear
- 緩存有幾種形式,可以在映射檔案中配置:read-only(隻讀,适用于很少變更的靜态資料/曆史資料),nonstrict-read-write,read-write(比較普遍的形式,效率一般),transactional(JTA中,且支援的緩存産品較少)
- 延遲加載
- 實體延遲加載:通過使用動态代理實作
- 集合延遲加載:通過實作自有的SET/LIST
- 屬性延遲加載:通過load
- 事務控制
- 如果不涉及多個事務管理器事務的話,不需要使用JTA,隻有Jdbc的事務控制就可以。
- 使用标準的SQL事務隔離級别
- 批量操作
- 批量操作的時候直接使用JDBC
- 項目經理,還要面對開發人員可能的抱怨,必竟,我們對使用者需求更改的控制力不大,但技術/架構風險是應該在初期意識到并制定好相關的對策。應用層的緩存隻是錦上添花,永遠不要把它當救命稻草,應用的根基(資料庫設計,算法,高效的操作語句,恰當API的選擇等)才是最重要的。
-
-
- 什麼是Hibernate延遲加載
-
-
延遲加載,也叫懶加載,它是Hibernate為提高程式執行效率而提供的一種機制,即隻有真正使用該對象的資料時才會建立。
Hibernate中主要是通過代理機制來實作延遲加載。它的具體過程:Hibernate叢資料庫擷取某一個對象資料時、擷取某一個對象的集合屬性值時,或擷取某一個對象所關聯的另一個對象時,由于沒有使用該對象的資料,hibernate并不是資料庫加載真正的資料,而隻是為該對象建立一個代理對象(cglib生成)來代表這個對象,這個對象上的所有屬性都是預設值;隻有在真正需要使用該對象的資料時才建立這個真實對象,真正從資料庫中加載它的資料,這樣在某些情況下,就可以提高查詢效率。
舉個例子:加載某對象X時,并不會立即從資料庫中傳回該對象所有的屬性值,而是采用代理機制生成X對象的代理,當通路該代理對象的屬性時才從資料庫中加載該屬性值。
Hibernate的延遲加載提供了三大方面其中分别是:實體關聯、集合類、屬性的延遲加載。
-
-
- NoSession的問題原因及解決方案
-
- 問題産生的原因
- 因為Hibernate中調用load懶加載的查詢,當調完load後session就關閉了,因為我們的session隻是配置到了dao層,表現層擷取不到,是以在表現層調用的時候session就已經關閉,就爬出了NoSession異常。
- 解決方案
- 可以配置關聯關系時設定lazy屬性=false,立即加載方法,也可以提前使用資料,使其自動加載。
- 利用Spring提供的OpenSessionInViewFilter解決no session問題
- 說說session緩存的作用
- 減少通路資料庫的頻率。應用程式從緩存中讀取持久化對象的速度顯然要比資料庫中檢索資料的速度塊。進而session緩存可以提高查詢效率。
- 事務送出的時候會隐式調用flush(),保證了緩存中的資料和資料庫資料同步。
- 當緩存中持久化對象的狀态發生改變時,Session不會立即執行相關的SQL語句,這使得Session能夠把幾條相關的SQL語句合并為一條SQL語句,以減少資料庫的通路次數,提高通路效率。
-
- session的清理和清空有什麼差別
-
- 清理緩存
- 清理緩存調用的是session.flush()方法
- 清理緩存是指按照緩存中對象的狀态的變化來同步更新資料庫,但不清空緩存
- 清空緩存
- 清空緩存調用的是session.clear()方法
- 清空是把session的緩存置空,但不同步更新資料庫。
- 清空緩存後也就意味着關閉session
- Relish
- 根據資料庫的變化來同步更新session緩存
- Hibernate中session的特點有哪些?
- 根據資料庫的變化來同步更新session緩存
- 線程不安全,每個線程都應該從一個SessionFactory擷取自己的session執行個體。
- Session的主要功能是提供對映射的實體類執行個體的建立,讀取和删除操作。
- 如果其持久化對象類是可序列化的,則Session執行個體也是可序列化的。
-
- Hibernate三種檢索政策的優缺點對比
-
- 立即檢索
- 采用立即檢索政策,會将被檢索的對象,以及和這個對象關聯的一對多對象都加載到緩存中。Session的get方法就使用的立即檢索政策。
- 優點:頻繁使用的關聯對象能夠被加載到緩存中。
- 缺點
-
- 占用記憶體。
- Select語句過多。
-
- 延遲檢索
-
采用延遲檢索政策,就不會加載關聯對象的内容。直到第一次調用關聯對象時,才去加載關聯對象。在不涉及關聯類操作時,延遲檢索政策隻适用于Session的load方法。
在類級别操作時,延遲檢索政策,隻加載類的OID不加載類的其他屬性,隻用當第一次通路其他屬性時,才回通路資料庫去加載内容。(這裡使用了CGLIB生成了類的代理類)
在關聯級别操作時,延遲檢索政策,隻加載類本身,不加載關聯類,直到第一次調用關聯對象時,才去加載關聯對象程式模式都是用延遲加載政策。如果需要指定使用延遲加載政策。在配置檔案中設定<class>的lazy=true,<set>的lazy=true或extra(增強延遲)<many-to-one>的lazy=proxy和no-proxy。
- 優點:由程式決定加載哪些類和内容,避免了大量無用的sql語句和記憶體消耗。
- 缺點:在Session關閉後,就不能通路關聯類對象了。需要確定在Session.close方法前,調用關聯對象。
- 迫切左外連接配接檢索
-
采用左外連接配接檢索,能夠使用Sql的外連接配接查詢,将需要加載的關聯對象加載在緩存中。
<set>fetch設定為join,<many-to-one>的fetch設定為 join。
- 優點:
- 對應用程式完全透明,不管對象處于持久化狀态,還是遊離狀态,應用程式都可以友善的從一個對象導航到與它關聯的對象。
- 使用了外連接配接,select語句數目少。
- 缺點:
-
- 可能會加載應用程式不需要通路的對象,白白浪費許多記憶體空間。
- 複雜的資料庫表連接配接也會影響檢索性能。
-
-
-
- 什麼是悲觀鎖和樂觀鎖?
-
- 什麼是鎖
- 為什麼需要鎖?在多使用者環境中,在同一時間可能會有多個使用者更新相同的記錄,這會産生沖突,這就是著名的并發性問題。而鎖就可以解決這個問題,即給我們標明的目标資料上鎖,使其無法被其他程式修改。
- 悲觀鎖
-
悲觀鎖是指假設并發更新沖突會發生,是以不管沖突是否真的發生,都會使用鎖機制。悲觀鎖會鎖住讀取的記錄,防止其它事務讀取和更新這些記錄。其它事務會一直阻塞,直到這個事務結束。悲觀鎖是在使用了資料庫的事務隔離功能的基礎上,獨享占用的資源,以此保證讀取資料一緻性,避免修改丢失。
資料庫悲觀鎖實作
select * from t_user where id = 1 for update 這條 sql語句鎖定了 t_user表中id為1的記錄。本次事務送出之前(事務送出時會釋放事務過程中的鎖),外界無法修改這些記錄。
Hibernate的悲觀鎖,也是基于資料庫的鎖機制實作。下面的代碼實作了對查詢記錄的加鎖。
主鍵查詢加鎖:
Hql查詢加鎖:User user= (User)session.get(User.class, 1, LockOptions.UPGRADE); Query query = session.createQuery("from User u where u.id = 1");
query.setLockOptions(LockOptions.UPGRADE);
- 樂觀鎖
-
總是認為不會産生并發問題,每次去取資料的時候總認為不會有其他線程對資料進行修改,是以不會上鎖,但是在更新時會判斷其他線程在這之前有沒有對資料進行修改,一般會使用版本号機制或CAS操作實作。
樂觀鎖的工作原理:讀取出資料時,将此版本号一同讀出,之後更新時,對此版本号加一。此時,将送出資料的版本資料與資料庫表對應記錄的目前版本資訊進行比對,如果送出的資料版本号大于資料庫表目前版本号,則予以更新,否則認為是過期資料。
Hibernate中實作:表中添加一個version字段,對象中添加一個version屬性,在映射檔案中建立它們之間的關系就可以實作,這個version字段由hibernate幫我們維護。
-
-
- 什麼ORM?
-
-
ORM概念
ORM,即Object-Relational Mapping(對象關系映射),它的作用是在關系型資料庫和業務實體對象之間作一個映射,這樣,我們在具體的操作業務對象的時候,就不需要再去和複雜的SQL語句打交道,隻需簡單的操作對象的屬性和方法。
先從項目中資料流存儲形式這個角度說起.簡單拿MVC這種分層模式.來說. Model作為資料承載實體. 在使用者界面層和業務邏輯層之間資料實作面向對象的形式傳遞. 當我們需要通過Controller層分發請求把資料持久化時我們會發現,記憶體中的面向對象如何持久化成關系型資料中存儲一條實際資料記錄呢?面向對象是從軟體工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關系資料庫則是從數學理論發展而來的. 兩者之間是不比對的.而ORM作為項目中間件形式實作資料在不同場景下資料關系映射. 對象關系映射(Object Relational Mapping,簡稱ORM)是一種為了解決面向對象與關系資料庫存在的互不比對的現象的技術.ORM就是這樣而來的。
ORM優缺點
優點:
第一:隐藏了資料通路細節,“封閉”的通用資料庫互動,ORM的核心。他使得我們的通用資料庫互動變得簡單易行,并且完全不用考慮該死的SQL語句。快速開發,由此而來。
第二:ORM使我們構造固化資料結構變得簡單易行。在ORM年表的史前時代,我們需要将我們的對象模型轉化為一條一條的SQL語句,通過直連或是DB helper在關系資料庫構造我們的資料庫體系。而現在,基本上所有的ORM架構都提供了通過對象模型構造關系資料庫結構的功能。這,相當不錯。
缺點:
第一:無可避免的,自動化意味着映射和關聯管理,代價是犧牲性能。現在的各種ORM架構都在嘗試使用各種方法來減輕這塊(LazyLoad,Cache),效果還是很顯著的。
第二:面向對象的查詢語言(X-QL)作為一種資料庫與對象之間的過渡,雖然隐藏了資料層面的業務抽象,但并不能完全的屏蔽掉資料庫層的設計,并且無疑将增加學習成本.
第三:對于複雜查詢,ORM仍然力不從心。
世上沒有驢是不吃草的(又想好又想巧,買個老驢不吃草),任何優勢的背後都隐藏着缺點,這是不可避免的。問題在于,我們是否能容忍缺點。
常用的ORM架構
-
- Hibernate全自動需要些hql語句
- MyBatis半自動自己寫sql語句,可操作性強,小巧
- EclipseLink 一個可擴充的支援JPA的ORM架構,供強大的緩存功能,緩存支援叢集。
- Apache OJB
-
1.2 MyBatis
1.2.1 MyBatis中#和$符号差別
- #{name}
- 被解析為jdbc的預編譯語句每個#{}代表一個占位符,比如 where name =?
- 這種方式不會出現SQL注入的問題
- 比較常用
- 舉個例子: 解析後的SQL如下:
<select id="getUserById" resultMap="userMap">
select * from t_User where id = #{id}
</select>
- ${name}
- 将内容直接寫到SQL語句中,比如:where name = admin
- 這種方式無法防止SLQ注入的問題
- 一般用于動态表或者動态字段排序
- 舉個列子: 解析後的SQL如下:
<select id="getUserById" resultMap="userMap">
select * from t_User where id = ${id}
</select>
1.2.2 MyBatis開發流程
- 導入Mybatis相關jar包
- 配置檔案
- MyBatis核心配置檔案:主要配置連接配接資料庫參數,連接配接池,插件,Mapper檔案等。
- Mapper檔案:主要配置調用的sql
- 建立SqlSessionFactoryBuilder
- 通過SqlSessionFactoryBuilder來建立SqlSessionFactory
- 通過SqlSessionFactory建立SqlSession
- 通過SqlSession操作資料庫
- 調用sqlSession.commit()送出事務
- 關閉sqlSession.close()關閉session
-
1.2.3 JDBC和MyBatis的對比?
JDBC是Java提供的一個操作資料庫的API,我們平時使用jdbc進行程式設計,需要如下幾個步驟。 - 使用jdbc程式設計需要連接配接資料庫,注冊驅動和資料庫資訊
- 操作Connection,打開Statement對象
- 通過Statement對象執行SQL,傳回結果到ResultSet對象
- 使用ResultSet讀取資料,然後通過代碼轉化為具體的POJO對象
- 關閉資料庫相關的資源
- JDBC缺點
- 工作量比較大,需要連接配接資料庫,然後處理jdbc底層事務,處理資料類型,還需要操作Connection,Statement對象和ResultSet對象最後還需要關閉它們。
- 頻繁的建立和釋放連接配接造成資源浪費進而影響性能
- SQL語句寫在代碼中造成代碼不易維護,修改SQL需要改變Java代碼。
- 對結果集的解析麻煩,需要手動轉化為POJO對象
- 向preparedStatement中設定參數,對占位符号位置和設定參數值,寫死在java代碼中
- 因為JDBC實在不好用,于是出現了ORM對Jdbc進行封裝,MyBatis對JDBC的封裝很好,幾乎可以取代Jdbc。
- MyBatis解決JDBC的以下缺點
- MyBatis使用SqlSessionFactoryBuilder來連接配接完成JDBC需要代碼完成的資料庫擷取和連接配接,減少了代碼的重複。
- MyBatis可以将SQL代碼寫入xml中,易于修改和維護。
- MyBatis的mapper會自動将執行後的結果映射到對應的Java對象中
- MyBatis的編碼風格統一優雅、性能高,靈活性好。
- jdbc,mybatis,hibernate的差別
-
- 從層次上看
- JDBC是較底層的持久層操作方式,而Hibernate和MyBatis都是在JDBC的基礎上進行了封裝使其更加友善程式員對持久層的操作。
-
- 從功能上看
- JDBC就是簡單的建立資料庫連接配接,然後建立statement,将sql語句傳給statement去執行,如果是有傳回結果的查詢語句,會将查詢結果放到ResultSet對象中,通過對ResultSet對象的周遊操作來擷取資料;Hibernate是将資料庫中的資料表映射為持久層的Java對象,對sql語句進行修改和優化比較困難;MyBatis是将sql語句中的輸入參數和輸出參數映射為java對象,sql修改和優化比較友善.
-
- 從使用上看
- 如果進行底層程式設計,而且對性能要求極高的話,應該采用JDBC的方式;如果要對資料庫進行完整性控制的話建議使用Hibernate;如果要靈活使用sql語句的話建議采用MyBatis架構。
1.2.3使用MyBatis中Mapper接口使用?
- Mapper接口方法名和mapper.xml中定義的每個sql的id相同
- Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
- Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
- Mapper.xml檔案中的namespace即是mapper接口的類路徑。
- Mapper檔案中的slq标簽的id盡量不要重複。
-
1.2.4 MyBatis中的一級緩存和二級緩存
-
- 一級緩存
-
一級緩存是SqlSession級别的緩存。在操作資料庫時需要構造sqlSession對象,在對象中有一個資料結構(HashMap)用于存儲緩存資料。不同的sqlSession之間的緩存資料區域(HashMap)是互相不影響的,一級緩存預設是開啟的。
一級緩存有如下特點:
- 第一次發起查詢先去找緩存中是否有,如果沒有,從資料庫查詢,然後将查詢結果存儲到一級緩存中。
- 如果中間sqlSession去執行commit操作(執行插入、更新、删除),則會清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的資訊,避免髒讀。
- 第二次發起查詢資料,先去找緩存中是否,緩存中有,直接從緩存中擷取資料傳回。
- 二級緩存
- 二級緩存的原理和一級緩存原理一樣,第一次查詢,會将資料放入緩存中,然後第二次查詢則會直接去緩存中取。但是一級緩存是基于 sqlSession 的,而 二級緩存是基于 mapper檔案的namespace的,也就是說多個sqlSession可以共享一個mapper中的二級緩存區域,并且如果兩個mapper的namespace相同,即使是兩個mapper,那麼這兩個mapper中執行sql查詢到的資料也将存在相同的二級緩存區域中。
-
-
-
- 二級緩存的特點如下:
-
-
- 二級緩存需要手動開啟
- 放在二級緩存中的對象需要實作Serializable接口(因為二級緩存資料存儲媒體多種多樣,不一定隻存在記憶體中,有可能存在硬碟中,如果我們要再取這個緩存的話,就需要反序列化了。是以mybatis中的pojo都去實作Serializable接口)。
- 執行commit操作二級緩存資料将會清空。
- 預設事務送出的時候會清空二級緩存資料
-
-
-
- useCache和flushCache
-
-
- userCache是用來設定是否禁用二級緩存的,在statement中設定useCache=false可以禁用目前select語句的二級緩存,即每次查詢都會發出sql去查詢,預設情況是true,即該sql使用二級緩存。 在mapper的同一個namespace中,如果有其它insert、update、delete操作資料後需要重新整理緩存,如果不執行重新整理緩存會出現髒讀。設定statement配置中的flushCache=”true” 屬性,預設情況下為true,即重新整理緩存,如果改成false則不會重新整理。使用緩存時如果手動修改資料庫表中的查詢資料會出現髒讀。
<select id="getUserById" resultMap="userMap" useCache="true">
Select * from t_User where id = #{id}
</select
<update id="updateUser" flushCache="true">
update t_user set name = #{username} where id = #{id}
</update>
1.2.5 MyBatis插入如何傳回主鍵?
-
- MySQl用法
-
<insert id="addUser" useGeneratedKeys="true" keyProperty="userId">
insert into t_user (username,password) values(#{username},#{password});
</insert>
useGeneratedKeys:取值範圍true|false(預設值),設定是否使用JDBC的getGenereatedKeys方法擷取主鍵并指派到keyProperty設定的領域模型屬性中。
keyProperty :使用者設定主鍵回填的到那個屬性。以上述例子為列,如果調用addUser方法時傳遞的是user對象則把主鍵設定到user對象的userId屬性中(沒有屬性則不設定),如果傳遞的是個Map則給Map中添加一個鍵值對,key是userId,value是主鍵值。
-
- Oracle用法
- 由于Oracle沒有自增長一說法,隻有序列這種模仿自增的形式,是以不能再使用“useGeneratedKeys”屬性。
<insert id="updateUser" >
<selectKey resultType="INTEGER" order="BEFORE" keyProperty="userId">
SELECT SEQ_USER.NEXTVAL as userId from DUAL
</selectKey>
insert into t_user (user_id, user_name) values (#{userId}, #{userName})
</insert>
1.2.6 MyBatis參數的傳遞有幾種?常用有哪些
- 采用map的方式傳遞
-
- 采用注解的方式
-
- 采用對象的方式
-
- 采用索引擷取
- 如果方法的形參超過3個建議使用Map或者封裝成對象,如果形參少于3個就使用注解,按照索引擷取不推薦使用,因為可讀性不強。
1.2.7說說MyBatis中ResultMap都是怎麼用的?
ResultMap标簽一般用在三種地方,
第一種:對象屬性和表中字段不一緻可以用ResultMap建立映射關系。
第二種:級聯查詢。
第三種:多個ResultMap可以繼承。
1.3 SpringMVC
1.3.1 SpringMVC工作原理
SpringMVC原理:
SpringMVC執行流程:
- 使用者發送請求至前端控制器DispatcherServlet。
- DispatcherServlet收到請求調用HandlerMapping映射器。
- 映射器找到具體的處理器(可以根據xml配置、注解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一并傳回給DispatcherServlet。
- DispatcherServlet調用HandlerAdapter處理器。
- HandlerAdapter經過适配調用具體的處理器(Controller,也叫後端控制器)。
- Controller執行完成傳回ModelAndView。
- HandlerAdapter将controller執行結果ModelAndView傳回給DispatcherServlet。
- DispatcherServlet将ModelAndView傳給ViewReslover視圖解析器。
- ViewReslover解析後傳回具體View。
- DispatcherServlet根據View進行渲染視圖(即将模型資料填充至視圖中)。
- DispatcherServlet響應使用者。
- SpringMVC中的重要元件:
- 前端控制器DispatcherServlet:作用是:接收請求、響應結果,相當于轉發器;
- DispatcherServlet相當于中央處理器,有了它就減少了其他元件之間的耦合。
- 處理器映射器HandlerMapping:作用是:根據請求的url查找Handler。
- 處理器擴充卡HandlerAdapter:作用是:按照特定的規則(HandlerAdapter要求的規則)去執行Handler。
- 視圖解析器ViewResolver:作用是:根據邏輯視圖名解析真正的視圖(View)。
- 視圖 View:View是一個接口,實作類支援不同的View類型(jsp、freemarker等)。
-
1.3.2 SpringMVC的常用注解有哪些?
- 元件行型注解
- 以上四種注解都是注解在類上的,被注解的類将被spring初始話為一個bean,然後統一管理。
注解 說明 @Component 類定義之前添加@Component注解,他會被spring容器識别,并轉為bean @Repository 對Dao實作類進行注解(特殊的@Component) @Service 用于對業務邏輯層進行注解(特殊的@Component) @Controller 用于控制層注解(特殊的@Component) - 請求和參數型注解
- @RequestMapping:用于處理請求位址映射,可以作用于類和方法上。
-
- value:定義request請求的映射位址
- method:請求的方式,預設接受get請求,如果請求方式和定義的方式不一樣則請求無法成功。
- params:定義request請求中必須包含的參數值。
-
headers:定義request請求中必須包含某些指定的請求頭,如:RequestMapping(value = "/something", headers = "content-type=text
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1.系統服務
tm.start();
tx.beign();
// 2.調用真實業務主體
Object result = method.invoke(target, args);
// 2.系統服務結束
tx.commit();
tm.end();
return result;
}
}
-
- @RequestMapping:用于處理請求位址映射,可以作用于類和方法上。
@Test
public void testAdd2() {
// 1.建立目标對象
IUserService userService = new UserServiceImpl();
// 2.增強
TransactionManager tx = new TransactionManager();
TimeManager tm = new TimeManager();
// 3.建立代理
ClassLoader classLoader = userService.getClass().getClassLoader(); // 目标對象類加載器
Class<?>[] interfaces = userService.getClass().getInterfaces(); // 目标對象接口
ProxyHanlder proxyHanlder = new ProxyHanlder(tx, tm, userService); // InvocationHandler
IUserService userServiceProxy = (IUserService)Proxy.newProxyInstance(classLoader, interfaces, proxyHanlder);
// 3.調用方法
userServiceProxy.add();
}
注意: JDK動态代理有一個限制,即它隻能為接口建立代理執行個體。
CGLIB實作動态代理
CGLib采用非常底層的位元組碼技術,可以在運作時為一個類建立子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用。
以上面的例子為例,目标對象的接口和實作類不變,隻需要改變動态代理的handler即可,需改代碼如下:
測試public class ProxyHanlder implements MethodInterceptor {
// 1.系統服務
private TransactionManager tx;
private TimeManager tm;
public ProxyHanlder(TransactionManager tx, TimeManager tm) {
this.tx = tx;
this.tm = tm;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 1.系統服務
tm.start();
tx.beign();
// 2.調用目标對象
Object result = proxy.invokeSuper(obj, args); // 調用父類的方法
tx.commit();
tm.end();
return result;
}
}
@Test
public void testAdd2() {
// 1.建立目标對象
IUserService userService = new UserServiceImpl();
// 2.增強
TransactionManager tx = new TransactionManager();
TimeManager tm = new TimeManager();
// 3.建立代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(userService.getClass()); // 設定父類(目标對象)
enhancer.setCallback(new ProxyHanlder(tx, tm));
IUserService userServiceProxy =(IUserService) enhancer.create(); // 建立代理類
// 3.調用方法
userServiceProxy.add();
}
注意: CGLIB動态代理有一個限制,不能給final修飾的類建立代理。
之後對兩者做了一個效率對比,在自己本機上通過System.nanoTime()對兩者做了記錄,結果如下。
No JDK動态代理 CGLiib 建立代理對象時間 720 1394 1 3473 7007 代理對象執行方法時間 97 7322 15 2080 一個建立花費時間長,一個執行時間長。
AOP的引用場景
事務,日志,緩存,權限,異常處理等。
1.4.25 在 Spring AOP 中,關注點和橫切關注的差別是什麼?
關注點是系統中的一個行為,也就是真實業務主體,橫切關注就是系統服務。1.4.26 Spring哪幾種類型?
- before:前置通知,在一個方法執行前被調用。
- after: 在方法執行之後調用的通知,無論方法執行是否成功。
- after-returning: 僅當方法成功完成後執行的通知。
- after-throwing: 在方法抛出異常退出時執行的通知。
- around: 在方法執行之前和之後調用的通知。
- Shiro
-
1.5.1簡單介紹一下 Shiro 架構
Apache Shiro 是 Java 的一個安全(權限)架構。 Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在 JavaSE 環境,也可以用在 JavaEE 環境。使用Shiro的人越來越多,因為它相當簡單,對比Spring Security,可能沒有Spring Security做的功能強大,但是在實際工作時可能并不需要那麼複雜的東西,是以使用小而簡單的Shiro就足夠了。
Shiro 可以完成:認證、授權、加密、會話管理、與Web 內建、緩存 等。
1.5.2 Shiro三個核心元件
Subject :正與系統進行互動的人,或某一個第三方服務。所有 Subject 執行個體都被綁定到(且這是必須的)一個SecurityManager 上。
SecurityManager:Shiro 架構的心髒,用來協調内部各安全元件,管理内部元件執行個體,并通過它來提供安全管理的各種服務。當 Shiro 與一個 Subject 進行互動時,實質上是幕後的 SecurityManager 處理所有繁重的 Subject 安全操作。
Realms :本質上是一個特定安全的 DAO。當配置 Shiro 時,必須指定至少一個 Realm 用來進行身份驗證和/或授權。Shiro 提供了多種可用的 Realms 來擷取安全相關的資料。如關系資料庫(JDBC),INI 及屬性檔案等。可以定義自己 Realm 實作來代表自定義的資料源
1.5.3 Shiro 的四大核心部分
Authentication(身份驗證):簡稱為“登入”,即證明使用者是誰。
Authorization(授權):通路控制的過程,即決定是否有權限去通路受保護的資源。
Session Management(會話管理):管理使用者特定的會話,即使在非 Web 或 EJB 應用程式。
Cryptography(加密):通過使用加密算法保持資料安全
1.5.4 shiro能解決什麼問題?
- 認證:驗證使用者的身份
- 授權:對使用者執行通路控制:判斷使用者是否被允許做某事
- 會話管理:在任何環境下使用 Session API,即使沒有 Web 或EJB 容器。
- 加密:以更簡潔易用的方式使用加密功能,保護或隐藏資料防止被偷窺
- Realms:聚集一個或多個使用者安全資料的資料源
-
1.5.5 Shiro 的四種權限控制方式
1.程式設計式
通過寫if/else授權代碼塊完成:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")) {
//有權限
} else {
//無權限
}
2.注解式
通過在執行的Java方法上放置相應的注解完成:
@RequiresRoles("admin") // 擁有admin角色才能通路該方法
public void hello() {
//有權限
}
// 擁有user:update權限才能方法該方法
@RequiresPermissions("user:update")
@RequestMapping(value = "/update")
public String update() {
System.out.println("UserController.update()");
return "ok";
}
如果要使用注解使用,首先要在配置檔案中開啟注解(SpringMVC配置檔案中)
<!-- 啟動shiro注解 -->
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
3. 頁面标簽權限控制
在JSP/GSP頁面通過相應的标簽完成:
4.url 級别權限控制<shiro:hasRole name="admin">
<!— 有權限 —>
</shiro:hasRole>
<property name="filterChainDefinitions">
<value>
/adminhasRole*接口,其會委托給SecurityManager,而SecurityManager接着會委托給Authorizer;
- Authorizer是真正的授權者,如果我們調用如isPermitted(“user:view”),其首先會通過PermissionResolver把字元串轉換成相應的Permission執行個體;
- 在進行授權之前,其會調用相應的Realm擷取Subject相應的角色/權限用于比對傳入的角色/權限;
- Authorizer會判斷Realm的角色/權限是否和傳入的比對,如果有多個Realm,會委托給ModularRealmAuthorizer進行循環判斷,如果比對如isPermitted*/hasRole*會傳回true,否則傳回false表示授權失敗。
ModularRealmAuthorizer進行多Realm比對流程:
1、首先檢查相應的Realm是否實作了實作了Authorizer;
2、如果實作了Authorizer,那麼接着調用其相應的isPermitted*/hasRole*接口進行比對;
3、如果有一個Realm比對那麼将傳回true,否則傳回false。
1.5.7 Shiro認證流程
在shiro中,使用者需要提供principals (身份)和credentials(證明)給shiro,進而應用能驗證使用者身份。
principals:身份,即主體的辨別屬性,可以是任何東西,如使用者名、郵箱等,唯一即可。一個主體可以有多個principals,但隻有一個Primary principals,一般是使用者名/密碼/手機号。
credentials:證明/憑證,即隻有主體知道的安全值,如密碼/數字證書等。
最常見的principals和credentials組合就是使用者名/密碼了。接下來先進行一個基本的身份認證。
認證流程如下:
- 首先調用Subject.login(token)進行登入,其會自動委托給Security Manager,調用之前必須通過SecurityUtils. setSecurityManager()設定;
- SecurityManager負責真正的身份驗證邏輯;它會委托給Authenticator進行身份驗證;
- Authenticator才是真正的身份驗證者,Shiro API中核心的身份認證入口點,此處可以自定義插入自己的實作;
- Authenticator可能會委托給相應的AuthenticationStrategy進行多Realm身份驗證,預設ModularRealmAuthenticator會調用AuthenticationStrategy進行多Realm身份驗證;
- Authenticator會把相應的token傳入Realm,從Realm擷取身份驗證資訊,如果沒有傳回/抛出異常表示身份驗證失敗了。此處可以配置多個Realm,将按照相應的順序及政策進行通路。
版權聲明:本文為CSDN部落客「qq_41992805」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/qq_41992805/article/details/83023439
更多相關推薦
spark知識架構
Spark
知識架構
第1章 Spark資料分析導論 11.1 Spark是什麼 11.2 一個大一統的軟體棧 21.2.1 SparkCore 21.2.2 SparkSQL 31.2.3 SparkStreaming 31.2.4 MLlib 31.2.5 GraphX 31.2.6 叢集管理器 41.3 Spark的使用者和用...
python知識架構_不知從何學起?送上Python系統學習知識架構
python知識架構
黑馬程式員上海月薪一萬隻是起點關注Python這個無所不在的程式設計語言,薪資就不說了,2018年也是火熱到沒朋友,大家對Python熱度持續高漲。越來越多的人咨詢學姐,“Python該怎麼學?”、“Python學些什麼?”這種的問題,...
知識體系、架構
知識體系、架構
知識體系、架構負載均衡:LVS,nginx,haproxy常見服務:ftp,ssh,mail,IIS,DNSweb:tomcat,nginx,apache,weblogic資料庫:mysql,mariadb存儲:NFS(DRBD+heartbeat+NFS),Hadoop,FastDFS,HDFS,MFS版本控制:svn,gi...
java架構基礎知識大全_java基礎知識--java集合架構
java架構基礎知識大全
java集合架構1、概述:集合架構被設計成要滿足以下幾個目标。該架構必須是高性能的。基本集合(動态數組,連結清單,樹,哈希表)的實作也必須是高效的。該架構允許不同類型的集合,以類似的方式工作,具有高度的互操作性....
mybatis架構知識總結
mybatis
mybatis架構知識總結
了解mybatis架構架構(Framework)是一種提供了科重用的公共結構的半成品。簡單來說架構也是一種快速開發的模闆。架構的優勢如下:1.不用再考慮公共問題,架構已經幫我們做好了。2.可以專心于業務邏輯,保證核心業務....
- 前端知識架構總結
- python定制架構知識點_Python : Unittest架構知識點
- Java架構前置知識3——工廠模式
- 架構知識-spring-第一章:認識spring+ioc
- 架構知識-spring-第三章:認識spring+JDBCTemplate
猜您喜歡
- BOOST::ASIO實作回顯伺服器
- 網絡基礎
- java診斷工具-Arthas(sc指令)檢視JVM已加載的類資訊
- springboot內建redis實作crud(springboot+mybatis+my...
- 注入系列:Hook注入
- VisionMobile:生态環境的遊戲:破壞Android的盛會
- php 規格,PHP 設計模式系列之 specification規格模式_...
- MongoDB資料庫使用者連接配接
- prusai3列印機使用教程_【列印虎原創】RepRap_Prusa_i...
- 詳細講解Quartz如何從入門到精通 (1)
- 藍橋杯 三部排序 day16
- python基礎爬蟲——深度優先與廣度優先
- 備份下自己的VIM配置檔案
- SolarWinds2002中各個工具的簡單說明
- 安卓逆向工具的使用(三)
文章随機推薦
- 首頁
- 技術部落格
- 聯系我們
- 版權申明
- 隐私條款
-
- @RequestMapping:用于處理請求位址映射,可以作用于類和方法上。