web service 相關(guān)
什么是Web Service?
答:從表面上看,Web Service就是一個(gè)應(yīng)用程序,它向外界暴露出一個(gè)能夠通過Web進(jìn)行調(diào)用的API。這就是說,你能夠用編程的方法透明的調(diào)用這個(gè)應(yīng)用程序,不需要了解它的任何細(xì)節(jié),跟你使用的編程語言也沒有關(guān)系。例如可以創(chuàng)建一個(gè)提供天氣預(yù)報(bào)的Web Service,那么無論你用哪種編程語言開發(fā)的應(yīng)用都可以通過調(diào)用它的API并傳入城市信息來獲得該城市的天氣預(yù)報(bào)。之所以稱之為Web Service,是因?yàn)樗贖TTP協(xié)議傳輸數(shù)據(jù),這使得運(yùn)行在不同機(jī)器上的不同應(yīng)用無須借助附加的、專門的第三方軟件或硬件,就可相互交換數(shù)據(jù)或集成。
REST和SOAP比較
REST(Representational State Transfer),REST采用簡單的URL的方式來代表一個(gè)對象,例如一個(gè)URL就對應(yīng)一個(gè)對象。
REST的優(yōu)點(diǎn):
- 輕量級的解決方案,不必向SOAP那樣要構(gòu)建一個(gè)標(biāo)準(zhǔn)的SOAP XML。
- 可讀性比較好:可以把URL的名字取得有實(shí)際意義。
- 不需要SDK支持:直接一個(gè)Http請求就可以,但是SOAP則可能需要使用到一些Webservice的類庫(例如Apache的Axis)。
REST的缺點(diǎn): - 復(fù)雜的應(yīng)用中,URL可能非常長,而且不容易解析。
Amazon、Yahoo和國內(nèi)的阿里軟件都提供了REST方式的Webservice調(diào)用。
SOAP的優(yōu)點(diǎn):
- 定義嚴(yán)格。必須符合SOAP的格式
- 某些時(shí)候使用比較方便
- 開發(fā)工具支持比較多一點(diǎn)。
Google基本上采用SOAP方式的Webservice。
概念解釋:SOAP、WSDL、UDDI。
- SOAP:簡單對象訪問協(xié)議(Simple Object Access Protocol),是Web Service中交換數(shù)據(jù)的一種協(xié)議規(guī)范。
- WSDL:Web服務(wù)描述語言(Web Service Description Language),它描述了Web服務(wù)的公共接口。這是一個(gè)基于XML的關(guān)于如何與Web服務(wù)通訊和使用的服務(wù)描述;也就是描述與目錄中列出的Web服務(wù)進(jìn)行交互時(shí)需要綁定的協(xié)議和信息格式。通常采用抽象語言描述該服務(wù)支持的操作和信息,使用的時(shí)候再將實(shí)際的網(wǎng)絡(luò)協(xié)議和信息格式綁定給該服務(wù)。
- UDDI:統(tǒng)一描述、發(fā)現(xiàn)和集成(Universal Description, Discovery and Integration),它是一個(gè)基于XML的跨平臺(tái)的描述規(guī)范,可以使世界范圍內(nèi)的企業(yè)在互聯(lián)網(wǎng)上發(fā)布自己所提供的服務(wù)。簡單的說,UDDI是訪問各種WSDL的一個(gè)門面(可以參考設(shè)計(jì)模式中的門面模式)
Java規(guī)范中和Web Service相關(guān)的規(guī)范有哪些?
Java規(guī)范中和Web Service相關(guān)的有三個(gè):
- JAX-WS(JSR 224):這個(gè)規(guī)范是早期的基于SOAP的Web Service規(guī)范JAX-RPC的替代版本,它并不提供向下兼容性,因?yàn)镽PC樣式的WSDL以及相關(guān)的API已經(jīng)在Java EE5中被移除了。WS-MetaData是JAX-WS的依賴規(guī)范,提供了基于注解配置Web Service和SOAP消息的相關(guān)API。
- JAXM(JSR 67):定義了發(fā)送和接收消息所需的API,相當(dāng)于Web Service的服務(wù)器端。
- JAX-RS(JSR 311 & JSR 339 & JSR 370):是Java針對REST(Representation State Transfer)架構(gòu)風(fēng)格制定的一套Web Service規(guī)范。REST是一種軟件架構(gòu)模式,是一種風(fēng)格,它不像SOAP那樣本身承載著一種消息協(xié)議, (兩種風(fēng)格的Web Service均采用了HTTP做傳輸協(xié)議,因?yàn)镠TTP協(xié)議能穿越防火墻,Java的遠(yuǎn)程方法調(diào)用(RMI)等是重量級協(xié)議,通常不能穿越防火墻),因此可以將REST視為基于HTTP協(xié)議的軟件架構(gòu)。REST中最重要的兩個(gè)概念是資源定位和資源操作,而HTTP協(xié)議恰好完整的提供了這兩個(gè)點(diǎn)。HTTP協(xié)議中的URI可以完成資源定位,而GET、POST、OPTION、DELETE方法可以完成資源操作。因此REST完全依賴HTTP協(xié)議就可以完成Web Service,而不像SOAP協(xié)議那樣只利用了HTTP的傳輸特性,定位和操作都是由SOAP協(xié)議自身完成的,也正是由于SOAP消息的存在使得基于SOAP的Web Service顯得笨重而逐漸被淘汰。
介紹一下你了解的Java領(lǐng)域的Web Service框架
- Axis2(Axis的升級版本)
- Jersey(RESTful的Web Service框架)
- CXF(XFire的延續(xù)版本)
- Hessian、Turmeric、JBoss SOA等。
Spring 相關(guān)
什么是IoC和DI?DI是如何實(shí)現(xiàn)的?
IoC(Inversion of Control)控制反轉(zhuǎn),DI(Dependency Injection)依賴注入,是對IoC更簡單的詮釋。控制反轉(zhuǎn)是把傳統(tǒng)上由程序代碼直接操控的對象的調(diào)用權(quán)交給容器,通過容器來實(shí)現(xiàn)對象組件的裝配和管理。所謂的"控制反轉(zhuǎn)"就是對組件對象控制權(quán)的轉(zhuǎn)移,從程序代碼本身轉(zhuǎn)移到了外部容器,由容器來創(chuàng)建對象并管理對象之間的依賴關(guān)系。
IoC體現(xiàn)了好萊塢原則 - "Don’t call me, we will call you"。依賴注入的基本原則是應(yīng)用組件不應(yīng)該負(fù)責(zé)查找資源或者其他依賴的協(xié)作對象。配置對象的工作應(yīng)該由容器負(fù)責(zé),查找資源的邏輯應(yīng)該從應(yīng)用組件的代碼中抽取出來,交給容器來完成。DI是對IoC更準(zhǔn)確的描述,即組件之間的依賴關(guān)系由容器在運(yùn)行期決定,形象的來說,即由容器動(dòng)態(tài)的將某種依賴關(guān)系注入到組件之中。
舉個(gè)例子:一個(gè)類A需要用到接口B中的方法,那么就需要為類A和接口B建立關(guān)聯(lián)或依賴關(guān)系,最原始的方法是在類A中創(chuàng)建一個(gè)接口B的實(shí)現(xiàn)類C的實(shí)例,但這種方法需要開發(fā)人員自行維護(hù)二者的依賴關(guān)系,也就是說當(dāng)依賴關(guān)系發(fā)生變動(dòng)的時(shí)候需要修改代碼并重新構(gòu)建整個(gè)系統(tǒng)。如果通過一個(gè)容器來管理這些對象以及對象的依賴關(guān)系,則只需要在類A中定義好用于關(guān)聯(lián)接口B的方法(構(gòu)造器或setter方法),將類A和接口B的實(shí)現(xiàn)類C放入容器中,通過對容器的配置來實(shí)現(xiàn)二者的關(guān)聯(lián)。
依賴注入可以通過setter方法注入(設(shè)值注入)、構(gòu)造器注入和接口注入三種方式來實(shí)現(xiàn),Spring支持setter注入和構(gòu)造器注入,通常使用構(gòu)造器注入來注入必須的依賴關(guān)系,對于可選的依賴關(guān)系,則setter注入是更好的選擇,setter注入需要類提供無參構(gòu)造器或者無參的靜態(tài)工廠方法來創(chuàng)建對象。
Spring中Bean的作用域有哪些?
答:
- singleton:單例模式;在 Spring IoC 容器中,僅存在一個(gè) Bean 實(shí)例。
- prototype:原型模式;每次從容器調(diào)用 Bean ,都返回一個(gè)新實(shí)例。
- request:每次 HTTP 請求,都創(chuàng)建一個(gè)新實(shí)例,該作用域適用于 WebApplicationContext 環(huán)境。
- session:同一個(gè) HTTP Session 共享一個(gè) Bean,不同 Session 使用不同 Bean,僅適用于 WebApplicationContext 環(huán)境。
- globalSession:
在Spring的早期版本中,僅有兩個(gè)作用域:singleton和prototype,前者表示Bean以單例的方式存在;后者表示每次從容器中調(diào)用Bean時(shí),都會(huì)返回一個(gè)新的實(shí)例,prototype通常翻譯為原型。
補(bǔ)充:設(shè)計(jì)模式中的創(chuàng)建型模式中也有一個(gè)原型模式,原型模式也是一個(gè)常用的模式,例如做一個(gè)室內(nèi)設(shè)計(jì)軟件,所有的素材都在工具箱中,而每次從工具箱中取出的都是素材對象的一個(gè)原型,可以通過對象克隆來實(shí)現(xiàn)原型模式。
Spring 2.x中針對WebApplicationContext新增了3個(gè)作用域,分別是:request(每次HTTP請求都會(huì)創(chuàng)建一個(gè)新的Bean)、session(同一個(gè)HttpSession共享同一個(gè)Bean,不同的HttpSession使用不同的Bean)和globalSession(同一個(gè)全局Session共享一個(gè)Bean)。
說明:單例模式和原型模式都是重要的設(shè)計(jì)模式。一般情況下,無狀態(tài)或狀態(tài)不可變的類適合使用單例模式。在傳統(tǒng)開發(fā)中,由于DAO持有Connection這個(gè)非線程安全對象因而沒有使用單例模式;但在Spring環(huán)境下,所有DAO類對可以采用單例模式,因?yàn)镾pring利用AOP和Java API中的ThreadLocal對非線程安全的對象進(jìn)行了特殊處理。
ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路。ThreadLocal,顧名思義是線程的一個(gè)本地化對象,當(dāng)工作于多線程中的對象使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程分配一個(gè)獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立的改變自己的副本,而不影響其他線程所對應(yīng)的副本。從線程的角度看,這個(gè)變量就像是線程的本地變量。
ThreadLocal類非常簡單好用,只有四個(gè)方法,能用上的也就是下面三個(gè)方法:
- void set(T value):設(shè)置當(dāng)前線程的線程局部變量的值。
- T get():獲得當(dāng)前線程所對應(yīng)的線程局部變量的值。
- void remove():刪除當(dāng)前線程中線程局部變量的值。
ThreadLocal是如何做到為每一個(gè)線程維護(hù)一份獨(dú)立的變量副本的呢?在ThreadLocal類中有一個(gè)Map,鍵為線程對象,值是其線程對應(yīng)的變量的副本,
解釋一下什么叫AOP(面向切面編程)?
AOP(Aspect-Oriented Programming)指一種程序設(shè)計(jì)范型,該范型以一種稱為切面(aspect)的語言構(gòu)造為基礎(chǔ),切面是一種新的模塊化機(jī)制,用來描述分散在對象、類或方法中的橫切關(guān)注點(diǎn)(crosscutting concern)。
Spring中自動(dòng)裝配的方式有哪些?
- no:不進(jìn)行自動(dòng)裝配,手動(dòng)設(shè)置Bean的依賴關(guān)系。
- byName:根據(jù)Bean的名字進(jìn)行自動(dòng)裝配。
- byType:根據(jù)Bean的類型進(jìn)行自動(dòng)裝配。
- constructor:類似于byType,不過是應(yīng)用于構(gòu)造器的參數(shù),如果正好有一個(gè)Bean與構(gòu)造器的參數(shù)類型相同則可以自動(dòng)裝配,否則會(huì)導(dǎo)致錯(cuò)誤。
- autodetect:如果有默認(rèn)的構(gòu)造器,則通過constructor的方式進(jìn)行自動(dòng)裝配,否則使用byType的方式進(jìn)行自動(dòng)裝配。
說明:自動(dòng)裝配沒有自定義裝配方式那么精確,而且不能自動(dòng)裝配簡單屬性(基本類型、字符串等),在使用時(shí)應(yīng)注意。
Spring中的自動(dòng)裝配有哪些限制?
- 如果使用了構(gòu)造器注入或者setter注入,那么將覆蓋自動(dòng)裝配的依賴關(guān)系。
- 基本數(shù)據(jù)類型的值、字符串字面量、類字面量無法使用自動(dòng)裝配來注入。
- 優(yōu)先考慮使用顯式的裝配來進(jìn)行更精確的依賴注入而不是使用自動(dòng)裝配。
Spring中如何使用注解來配置Bean?有哪些相關(guān)的注解?
首先需要在Spring配置文件中增加如下配置:
<context:component-scan base-package="org.example"/>
然后可以用@Component、@Controller、@Service、@Repository注解來標(biāo)注需要由Spring IoC容器進(jìn)行對象托管的類。這幾個(gè)注解沒有本質(zhì)區(qū)別,只不過@Controller通常用于控制器,@Service通常用于業(yè)務(wù)邏輯類,@Repository通常用于倉儲(chǔ)類(例如我們的DAO實(shí)現(xiàn)類),普通的類用@Component來標(biāo)注。
闡述Spring框架中Bean的生命周期?
- Spring IoC容器找到關(guān)于Bean的定義并實(shí)例化該Bean。
- Spring IoC容器對Bean進(jìn)行依賴注入。
- 如果Bean實(shí)現(xiàn)了BeanNameAware接口,則將該Bean的id傳給setBeanName方法。
- 如果Bean實(shí)現(xiàn)了BeanFactoryAware接口,則將BeanFactory對象傳給setBeanFactory方法。
- 如果Bean實(shí)現(xiàn)了BeanPostProcessor接口,則調(diào)用其postProcessBeforeInitialization方法。
- 如果Bean實(shí)現(xiàn)了InitializingBean接口,則調(diào)用其afterPropertySet方法。
- 如果有和Bean關(guān)聯(lián)的BeanPostProcessors對象,則這些對象的postProcessAfterInitialization方法被調(diào)用。
- 當(dāng)銷毀Bean實(shí)例時(shí),如果Bean實(shí)現(xiàn)了DisposableBean接口,則調(diào)用其destroy方法。
依賴注入時(shí)如何注入集合屬性?
可以在定義Bean屬性時(shí),通過<list> / <set> / <map> / <props>分別為其注入列表、集合、映射和鍵值都是字符串的映射屬性。
Spring IoC容器配置Bean的方式?
- 基于XML文件進(jìn)行配置。
- 基于注解進(jìn)行配置。
- 基于Java程序進(jìn)行配置(Spring 3+)
在Web項(xiàng)目中如何獲得Spring的IoC容器?
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
如何在Web項(xiàng)目中配置Spring MVC?
要使用Spring MVC需要在Web項(xiàng)目配置文件中配置其前端控制器DispatcherServlet,如下所示:
<web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>\*.html</url-pattern>
</servlet-mapping>
</web-app>
Spring MVC的工作原理是怎樣的?
- 客戶端的所有請求都交給前端控制器DispatcherServlet來處理,它會(huì)負(fù)責(zé)調(diào)用系統(tǒng)的其他模塊來真正處理用戶的請求。
- ispatcherServlet收到請求后,將根據(jù)請求的信息(包括URL、HTTP協(xié)議方法、請求頭、請求參數(shù)、Cookie等)以及HandlerMapping的配置找到處理該請求的Handler(任何一個(gè)對象都可以作為請求的Handler)。
- 在這個(gè)地方Spring會(huì)通過HandlerAdapter對該處理器進(jìn)行封裝。
- andlerAdapter是一個(gè)適配器,它用統(tǒng)一的接口對各種Handler中的方法進(jìn)行調(diào)用。
- andler完成對用戶請求的處理后,會(huì)返回一個(gè)ModelAndView對象給DispatcherServlet,ModelAndView顧名思義,包含了數(shù)據(jù)模型以及相應(yīng)的視圖的信息。
- ModelAndView的視圖是邏輯視圖,DispatcherServlet還要借助ViewResolver完成從邏輯視圖到真實(shí)視圖對象的解析工作。
- 當(dāng)?shù)玫秸嬲囊晥D對象后,DispatcherServlet會(huì)利用視圖對象對模型數(shù)據(jù)進(jìn)行渲染。
- 客戶端得到響應(yīng),可能是一個(gè)普通的HTML頁面,也可以是XML或JSON字符串,還可以是一張圖片或者一個(gè)PDF文件。
Hibernate 相關(guān)
什么是ORM?
答:對象關(guān)系映射(Object-Relational Mapping,簡稱ORM)是一種為了解決程序的面向?qū)ο竽P团c數(shù)據(jù)庫的關(guān)系模型互不匹配問題的技術(shù);簡單的說,ORM是通過使用描述對象和數(shù)據(jù)庫之間映射的元數(shù)據(jù)(在Java中可以用XML或者是注解),將程序中的對象自動(dòng)持久化到關(guān)系數(shù)據(jù)庫中或者將關(guān)系數(shù)據(jù)庫表中的行轉(zhuǎn)換成Java對象,其本質(zhì)上就是將數(shù)據(jù)從一種形式轉(zhuǎn)換到另外一種形式。
JPA、Hibernate、Mybatis都含有ORM模型。
持久層設(shè)計(jì)要考慮的問題有哪些?你用過的持久層框架有哪些?
答:所謂"持久"就是將數(shù)據(jù)保存到可掉電式存儲(chǔ)設(shè)備中以便今后使用,簡單的說,就是將內(nèi)存中的數(shù)據(jù)保存到關(guān)系型數(shù)據(jù)庫、文件系統(tǒng)、消息隊(duì)列等提供持久化支持的設(shè)備中。持久層就是系統(tǒng)中專注于實(shí)現(xiàn)數(shù)據(jù)持久化的相對獨(dú)立的層面。
持久層設(shè)計(jì)的目標(biāo)包括:
- 數(shù)據(jù)存儲(chǔ)邏輯的分離,提供抽象化的數(shù)據(jù)訪問接口。
- 數(shù)據(jù)訪問底層實(shí)現(xiàn)的分離,可以在不修改代碼的情況下切換底層實(shí)現(xiàn)。
- 資源管理和調(diào)度的分離,在數(shù)據(jù)訪問層實(shí)現(xiàn)統(tǒng)一的資源調(diào)度(如緩存機(jī)制)。
- 數(shù)據(jù)抽象,提供更面向?qū)ο蟮臄?shù)據(jù)操作。
持久層框架有:
- Hibernate
- MyBatis
- TopLink
- Guzz
- jOOQ
- Spring Data
- ActiveJDBC
Hibernate中SessionFactory是線程安全的嗎?Session是線程安全的嗎(兩個(gè)線程能夠共享同一個(gè)Session嗎)?
答:SessionFactory對應(yīng)Hibernate的一個(gè)數(shù)據(jù)存儲(chǔ)的概念,它是線程安全的,可以被多個(gè)線程并發(fā)訪問。SessionFactory一般只會(huì)在啟動(dòng)的時(shí)候構(gòu)建。對于應(yīng)用程序,最好將SessionFactory通過單例模式進(jìn)行封裝以便于訪問。Session是一個(gè)輕量級非線程安全的對象(線程間不能共享session),它表示與數(shù)據(jù)庫進(jìn)行交互的一個(gè)工作單元。Session是由SessionFactory創(chuàng)建的,在任務(wù)完成之后它會(huì)被關(guān)閉。Session是持久層服務(wù)對外提供的主要接口。Session會(huì)延遲獲取數(shù)據(jù)庫連接(也就是在需要的時(shí)候才會(huì)獲?。?。為了避免創(chuàng)建太多的session,可以使用ThreadLocal將session和當(dāng)前線程綁定在一起,這樣可以讓同一個(gè)線程獲得的總是同一個(gè)session。Hibernate 3中SessionFactory的getCurrentSession()方法就可以做到。
Hibernate中Session的load和get方法的區(qū)別是什么?
答:主要有以下三項(xiàng)區(qū)別:
- 如果沒有找到符合條件的記錄,get方法返回null,load方法拋出異常。
- get方法直接返回實(shí)體類對象,load方法返回實(shí)體類對象的代理。
- 在Hibernate3之前,get方法只在一級緩存中進(jìn)行數(shù)據(jù)查找,如果沒有找到對應(yīng)的數(shù)據(jù)則越過二級緩存,直接發(fā)出SQL語句完成數(shù)據(jù)讀?。籰oad方法則可以從二級緩存中獲取數(shù)據(jù);從Hibernate 3開始,get方法不再是對二級緩存只寫不讀,它也是可以訪問二級緩存的。
說明:對于load()方法Hibernate認(rèn)為該數(shù)據(jù)在數(shù)據(jù)庫中一定存在可以放心的使用代理來實(shí)現(xiàn)延遲加載,如果沒有數(shù)據(jù)就拋出異常,而通過get()方法獲取的數(shù)據(jù)可以不存在。
Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分別是做什么的?有什么區(qū)別?
答:Hibernate的對象有三種狀態(tài):瞬時(shí)態(tài)(transient)、持久態(tài)(persistent)和游離態(tài)(detached)。
瞬時(shí)態(tài)的實(shí)例可以通過調(diào)用save()、persist() 或 saveOrUpdate() 方法變成持久態(tài);
游離態(tài)的實(shí)例可以通過調(diào)用update()、lock()、replicate() 或 saveOrUpdate() 變成持久態(tài)。
save() 和 persist() 會(huì)引發(fā)SQL的INSERT語句;
update() 或 merge() 會(huì)引發(fā)UPDATE語句。
save() 和 update() 的區(qū)別在于一個(gè)是將瞬時(shí)態(tài)對象變成持久態(tài),一個(gè)是將游離態(tài)對象變?yōu)槌志脩B(tài)。
merge()方法可以完成 save() 和 update() 方法的功能,它的意圖是將新的狀態(tài)合并到已有的持久化對象上或創(chuàng)建新的持久化對象。
對于persist()方法,按照官方文檔的說明:
- persist()方法把一個(gè)瞬時(shí)態(tài)的實(shí)例持久化,但是并不保證標(biāo)識(shí)符被立刻填入到持久化實(shí)例中,標(biāo)識(shí)符的填入可能被推遲到flush的時(shí)間;
- persist()方法保證當(dāng)它在一個(gè)事務(wù)外部被調(diào)用的時(shí)候并不觸發(fā)一個(gè)INSERT語句,當(dāng)需要封裝一個(gè)長會(huì)話流程的時(shí)候,persist()方法是很有必要的;
- save()方法不保證第②條,它要返回標(biāo)識(shí)符,所以它會(huì)立即執(zhí)行INSERT語句,不管是在事務(wù)內(nèi)部還是外部。
- update()方法是把一個(gè)已經(jīng)更改過的脫管狀態(tài)的對象變成持久狀態(tài);lock()方法是把一個(gè)沒有更改過的脫管狀態(tài)的對象變成持久狀態(tài)。
闡述Session加載實(shí)體對象的過程。
答:Session加載實(shí)體對象的步驟是:
- Session在調(diào)用數(shù)據(jù)庫查詢功能之前,首先會(huì)在一級緩存中通過實(shí)體類型和主鍵進(jìn)行查找,如果一級緩存查找命中且數(shù)據(jù)狀態(tài)合法,則直接返回;
- 如果一級緩存沒有命中,接下來Session會(huì)在當(dāng)前NonExists記錄(相當(dāng)于一個(gè)查詢黑名單,如果出現(xiàn)重復(fù)的無效查詢可以迅速做出判斷,從而提升性能)中進(jìn)行查找,如果NonExists中存在同樣的查詢條件,則返回null;
- 如果一級緩存查詢失敗則查詢二級緩存,如果二級緩存命中則直接返回;
- 如果之前的查詢都未命中,則發(fā)出SQL語句,如果查詢未發(fā)現(xiàn)對應(yīng)記錄則將此次查詢添加到Session的NonExists中加以記錄,并返回null;
- 根據(jù)映射配置和SQL語句得到ResultSet,并創(chuàng)建對應(yīng)的實(shí)體對象;
- 將對象納入Session(一級緩存)的管理;
- 如果有對應(yīng)的攔截器,則執(zhí)行攔截器的onLoad方法;
- 如果開啟并設(shè)置了要使用二級緩存,則將數(shù)據(jù)對象納入二級緩存;
- 返回?cái)?shù)據(jù)對象。
Query 接口的 list 方法和 iterate 方法有什么區(qū)別?
答:
- list()方法無法利用一級緩存和二級緩存(對緩存只寫不讀),它只能在開啟查詢緩存的前提下使用查詢緩存;iterate()方法可以充分利用緩存,如果目標(biāo)數(shù)據(jù)只讀或者讀取頻繁,使用iterate()方法可以減少性能開銷。
- list()方法不會(huì)引起N+1查詢問題,而iterate()方法可能引起N+1查詢問題
說明:關(guān)于N+1查詢問題,可以參考CSDN上的一篇文章《什么是N+1查詢》
Hibernate如何實(shí)現(xiàn)分頁查詢?
答:通過Hibernate實(shí)現(xiàn)分頁查詢,開發(fā)人員只需要提供HQL語句(調(diào)用Session的createQuery()方法)或查詢條件(調(diào)用Session的createCriteria()方法)、設(shè)置查詢起始行數(shù)(調(diào)用Query或Criteria接口的setFirstResult()方法)和最大查詢行數(shù)(調(diào)用Query或Criteria接口的setMaxResults()方法),并調(diào)用Query或Criteria接口的list()方法,Hibernate會(huì)自動(dòng)生成分頁查詢的SQL語句。
鎖機(jī)制有什么用?簡述Hibernate的悲觀鎖和樂觀鎖機(jī)制。
答:有些業(yè)務(wù)邏輯在執(zhí)行過程中要求對數(shù)據(jù)進(jìn)行排他性的訪問,于是需要通過一些機(jī)制保證在此過程中數(shù)據(jù)被鎖住不會(huì)被外界修改,這就是所謂的鎖機(jī)制。
Hibernate支持悲觀鎖和樂觀鎖兩種鎖機(jī)制。
- 悲觀鎖,顧名思義悲觀的認(rèn)為在數(shù)據(jù)處理過程中極有可能存在修改數(shù)據(jù)的并發(fā)事務(wù)(包括本系統(tǒng)的其他事務(wù)或來自外部系統(tǒng)的事務(wù)),于是將處理的數(shù)據(jù)設(shè)置為鎖定狀態(tài)。悲觀鎖必須依賴數(shù)據(jù)庫本身的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,關(guān)于數(shù)據(jù)庫的鎖機(jī)制和事務(wù)隔離級別在《Java面試題大全(上)》中已經(jīng)討論過了。
- 樂觀鎖,顧名思義,對并發(fā)事務(wù)持樂觀態(tài)度(認(rèn)為對數(shù)據(jù)的并發(fā)操作不會(huì)經(jīng)常性的發(fā)生),通過更加寬松的鎖機(jī)制來解決由于悲觀鎖排他性的數(shù)據(jù)訪問對系統(tǒng)性能造成的嚴(yán)重影響。最常見的樂觀鎖是通過數(shù)據(jù)版本標(biāo)識(shí)來實(shí)現(xiàn)的,讀取數(shù)據(jù)時(shí)獲得數(shù)據(jù)的版本號(hào),更新數(shù)據(jù)時(shí)將此版本號(hào)加1,然后和數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本號(hào)進(jìn)行比較,如果提交的數(shù)據(jù)版本號(hào)大于數(shù)據(jù)庫中此記錄的當(dāng)前版本號(hào)則更新數(shù)據(jù),否則認(rèn)為是過期數(shù)據(jù)無法更新。
Hibernate中通過Session的get()和load()方法從數(shù)據(jù)庫中加載對象時(shí)可以通過參數(shù)指定使用悲觀鎖;而樂觀鎖可以通過給實(shí)體類加整型的版本字段再通過XML或@Version注解進(jìn)行配置。
提示:使用樂觀鎖會(huì)增加了一個(gè)版本字段,很明顯這需要額外的空間來存儲(chǔ)這個(gè)版本字段,浪費(fèi)了空間,但是樂觀鎖會(huì)讓系統(tǒng)具有更好的并發(fā)性,這是對時(shí)間的節(jié)省。因此樂觀鎖也是典型的空間換時(shí)間的策略。
闡述實(shí)體對象的三種狀態(tài)以及轉(zhuǎn)換關(guān)系。
答:最新的Hibernate文檔中為Hibernate對象定義了四種狀態(tài)(原來是三種狀態(tài),面試的時(shí)候基本上問的也是三種狀態(tài)),分別是:瞬時(shí)態(tài)(new, or transient)、持久態(tài)(managed, or persistent)、游狀態(tài)(detached)和移除態(tài)(removed,以前Hibernate文檔中定義的三種狀態(tài)中沒有移除態(tài)),如下圖所示,就以前的Hibernate文檔中移除態(tài)被視為是瞬時(shí)態(tài)。
- 瞬時(shí)態(tài):當(dāng)new一個(gè)實(shí)體對象后,這個(gè)對象處于瞬時(shí)態(tài),即這個(gè)對象只是一個(gè)保存臨時(shí)數(shù)據(jù)的內(nèi)存區(qū)域,如果沒有變量引用這個(gè)對象,則會(huì)被JVM的垃圾回收機(jī)制回收。這個(gè)對象所保存的數(shù)據(jù)與數(shù)據(jù)庫沒有任何關(guān)系,除非通過Session的save()、saveOrUpdate()、persist()、merge()方法把瞬時(shí)態(tài)對象與數(shù)據(jù)庫關(guān)聯(lián),并把數(shù)據(jù)插入或者更新到數(shù)據(jù)庫,這個(gè)對象才轉(zhuǎn)換為持久態(tài)對象。
- 持久態(tài):持久態(tài)對象的實(shí)例在數(shù)據(jù)庫中有對應(yīng)的記錄,并擁有一個(gè)持久化標(biāo)識(shí)(ID)。對持久態(tài)對象進(jìn)行delete操作后,數(shù)據(jù)庫中對應(yīng)的記錄將被刪除,那么持久態(tài)對象與數(shù)據(jù)庫記錄不再存在對應(yīng)關(guān)系,持久態(tài)對象變成移除態(tài)(可以視為瞬時(shí)態(tài))。持久態(tài)對象被修改變更后,不會(huì)馬上同步到數(shù)據(jù)庫,直到數(shù)據(jù)庫事務(wù)提交。
- 游離態(tài):當(dāng)Session進(jìn)行了close()、clear()、evict()或flush()后,實(shí)體對象從持久態(tài)變成游離態(tài),對象雖然擁有持久和與數(shù)據(jù)庫對應(yīng)記錄一致的標(biāo)識(shí)值,但是因?yàn)閷ο笠呀?jīng)從會(huì)話中清除掉,對象不在持久化管理之內(nèi),所以處于游離態(tài)(也叫脫管態(tài))。游離態(tài)的對象與臨時(shí)狀態(tài)對象是十分相似的,只是它還含有持久化標(biāo)識(shí)。
提示:關(guān)于這個(gè)問題,在Hibernate的官方文檔中有更為詳細(xì)的解讀。
如何理解Hibernate的延遲加載機(jī)制?在實(shí)際應(yīng)用中,延遲加載與Session關(guān)閉的矛盾是如何處理的?
答:延遲加載就是并不是在讀取的時(shí)候就把數(shù)據(jù)加載進(jìn)來,而是等到使用時(shí)再加載。Hibernate使用了虛擬代理機(jī)制實(shí)現(xiàn)延遲加載,我們使用Session的load()方法加載數(shù)據(jù)或者一對多關(guān)聯(lián)映射在使用延遲加載的情況下從一的一方加載多的一方,得到的都是虛擬代理,簡單的說返回給用戶的并不是實(shí)體本身,而是實(shí)體對象的代理。代理對象在用戶調(diào)用getter方法時(shí)才會(huì)去數(shù)據(jù)庫加載數(shù)據(jù)。但加載數(shù)據(jù)就需要數(shù)據(jù)庫連接。而當(dāng)我們把會(huì)話關(guān)閉時(shí),數(shù)據(jù)庫連接就同時(shí)關(guān)閉了。
延遲加載與session關(guān)閉的矛盾一般可以這樣處理:
- 關(guān)閉延遲加載特性。這種方式操作起來比較簡單,因?yàn)镠ibernate的延遲加載特性是可以通過映射文件或者注解進(jìn)行配置的,但這種解決方案存在明顯的缺陷。首先,出現(xiàn)"no session or session was closed"通常說明系統(tǒng)中已經(jīng)存在主外鍵關(guān)聯(lián),如果去掉延遲加載的話,每次查詢的開銷都會(huì)變得很大。
- 在session關(guān)閉之前先獲取需要查詢的數(shù)據(jù),可以使用工具方法Hibernate.isInitialized()判斷對象是否被加載,如果沒有被加載則可以使用Hibernate.initialize()方法加載對象。
- 使用攔截器或過濾器延長Session的生命周期直到視圖獲得數(shù)據(jù)。Spring整合Hibernate提供的OpenSessionInViewFilter和OpenSessionInViewInterceptor就是這種做法。
舉一個(gè)多對多關(guān)聯(lián)的例子,并說明如何實(shí)現(xiàn)多對多關(guān)聯(lián)映射。
答:例如:商品和訂單、學(xué)生和課程都是典型的多對多關(guān)系??梢栽趯?shí)體類上通過@ManyToMany注解配置多對多關(guān)聯(lián)或者通過映射文件中的和標(biāo)簽配置多對多關(guān)聯(lián),但是實(shí)際項(xiàng)目開發(fā)中,很多時(shí)候都是將多對多關(guān)聯(lián)映射轉(zhuǎn)換成兩個(gè)多對一關(guān)聯(lián)映射來實(shí)現(xiàn)的。
談一下你對繼承映射的理解。
答:繼承關(guān)系的映射策略有三種:
- 每個(gè)繼承結(jié)構(gòu)一張表(table per class hierarchy),不管多少個(gè)子類都用一張表。
- 每個(gè)子類一張表(table per subclass),公共信息放一張表,特有信息放單獨(dú)的表。
- 每個(gè)具體類一張表(table per concrete class),有多少個(gè)子類就有多少張表。
第一種方式屬于單表策略,其優(yōu)點(diǎn)在于查詢子類對象的時(shí)候無需表連接,查詢速度快,適合多態(tài)查詢;缺點(diǎn)是可能導(dǎo)致表很大。后兩種方式屬于多表策略,其優(yōu)點(diǎn)在于數(shù)據(jù)存儲(chǔ)緊湊,其缺點(diǎn)是需要進(jìn)行連接查詢,不適合多態(tài)查詢。
簡述Hibernate常見優(yōu)化策略。
答:這個(gè)問題應(yīng)當(dāng)挑自己使用過的優(yōu)化策略回答,常用的有:
- 制定合理的緩存策略(二級緩存、查詢緩存)。
- 采用合理的Session管理機(jī)制。
- 盡量使用延遲加載特性。
- 設(shè)定合理的批處理參數(shù)。
- 如果可以,選用UUID作為主鍵生成器。
- 如果可以,選用基于版本號(hào)的樂觀鎖替代悲觀鎖。
- 在開發(fā)過程中, 開啟hibernate.show_sql選項(xiàng)查看生成的SQL,從而了解底層的狀況;開發(fā)完成后關(guān)閉此選項(xiàng)。
- 考慮數(shù)據(jù)庫本身的優(yōu)化,合理的索引、恰當(dāng)?shù)臄?shù)據(jù)分區(qū)策略等都會(huì)對持久層的性能帶來可觀的提升,但這些需要專業(yè)的DBA(數(shù)據(jù)庫管理員)提供支持。
談一談Hibernate的一級緩存、二級緩存和查詢緩存。
答:Hibernate的Session提供了一級緩存的功能,默認(rèn)總是有效的,當(dāng)應(yīng)用程序保存持久化實(shí)體、修改持久化實(shí)體時(shí),Session并不會(huì)立即把這種改變提交到數(shù)據(jù)庫,而是緩存在當(dāng)前的Session中,除非顯示調(diào)用了Session的flush()方法或通過close()方法關(guān)閉Session。通過一級緩存,可以減少程序與數(shù)據(jù)庫的交互,從而提高數(shù)據(jù)庫訪問性能。
SessionFactory級別的二級緩存是全局性的,所有的Session可以共享這個(gè)二級緩存。不過二級緩存默認(rèn)是關(guān)閉的,需要顯示開啟并指定需要使用哪種二級緩存實(shí)現(xiàn)類(可以使用第三方提供的實(shí)現(xiàn))。一旦開啟了二級緩存并設(shè)置了需要使用二級緩存的實(shí)體類,SessionFactory就會(huì)緩存訪問過的該實(shí)體類的每個(gè)對象,除非緩存的數(shù)據(jù)超出了指定的緩存空間。
一級緩存和二級緩存都是對整個(gè)實(shí)體進(jìn)行緩存,不會(huì)緩存普通屬性,如果希望對普通屬性進(jìn)行緩存,可以使用查詢緩存。查詢緩存是將HQL或SQL語句以及它們的查詢結(jié)果作為鍵值對進(jìn)行緩存,對于同樣的查詢可以直接從緩存中獲取數(shù)據(jù)。查詢緩存默認(rèn)也是關(guān)閉的,需要顯示開啟。
Hibernate中DetachedCriteria類是做什么的?
答:DetachedCriteria和Criteria的用法基本上是一致的,但Criteria是由Session的createCriteria()方法創(chuàng)建的,也就意味著離開創(chuàng)建它的Session,Criteria就無法使用了。DetachedCriteria不需要Session就可以創(chuàng)建(使用DetachedCriteria.forClass()方法創(chuàng)建),所以通常也稱其為離線的Criteria,在需要進(jìn)行查詢操作的時(shí)候再和Session綁定(調(diào)用其getExecutableCriteria(Session)方法),這也就意味著一個(gè)DetachedCriteria可以在需要的時(shí)候和不同的Session進(jìn)行綁定。
@OneToMany注解的mappedBy屬性有什么作用?
答:@OneToMany用來配置一對多關(guān)聯(lián)映射,但通常情況下,一對多關(guān)聯(lián)映射都由多的一方來維護(hù)關(guān)聯(lián)關(guān)系,例如學(xué)生和班級,應(yīng)該在學(xué)生類中添加班級屬性來維持學(xué)生和班級的關(guān)聯(lián)關(guān)系(在數(shù)據(jù)庫中是由學(xué)生表中的外鍵班級編號(hào)來維護(hù)學(xué)生表和班級表的多對一關(guān)系),如果要使用雙向關(guān)聯(lián),在班級類中添加一個(gè)容器屬性來存放學(xué)生,并使用@OneToMany注解進(jìn)行映射,此時(shí)mappedBy屬性就非常重要。如果使用XML進(jìn)行配置,可以用<set>標(biāo)簽的inverse="true"設(shè)置來達(dá)到同樣的效果。
MyBatis中使用#和$書寫占位符有什么區(qū)別?
答:#將傳入的數(shù)據(jù)都當(dāng)成一個(gè)字符串,會(huì)對傳入的數(shù)據(jù)自動(dòng)加上引號(hào);$將傳入的數(shù)據(jù)直接顯示生成在SQL中。注意:使用$占位符可能會(huì)導(dǎo)致SQL注射攻擊,能用#的地方就不要使用$,寫order by子句的時(shí)候應(yīng)該用$而不是#。
解釋一下MyBatis中命名空間(namespace)的作用。
答:在大型項(xiàng)目中,可能存在大量的SQL語句,這時(shí)候?yàn)槊總€(gè)SQL語句起一個(gè)唯一的標(biāo)識(shí)(ID)就變得并不容易了。為了解決這個(gè)問題,在MyBatis中,可以為每個(gè)映射文件起一個(gè)唯一的命名空間,這樣定義在這個(gè)映射文件中的每個(gè)SQL語句就成了定義在這個(gè)命名空間中的一個(gè)ID。只要我們能夠保證每個(gè)命名空間中這個(gè)ID是唯一的,即使在不同映射文件中的語句ID相同,也不會(huì)再產(chǎn)生沖突了。
MyBatis中的動(dòng)態(tài)SQL是什么意思?
答:對于一些復(fù)雜的查詢,我們可能會(huì)指定多個(gè)查詢條件,但是這些條件可能存在也可能不存在,例如在58同城上面找房子,我們可能會(huì)指定面積、樓層和所在位置來查找房源,也可能會(huì)指定面積、價(jià)格、戶型和所在位置來查找房源,此時(shí)就需要根據(jù)用戶指定的條件動(dòng)態(tài)生成SQL語句。如果不使用持久層框架我們可能需要自己拼裝SQL語句,還好MyBatis提供了動(dòng)態(tài)SQL的功能來解決這個(gè)問題。MyBatis中用于實(shí)現(xiàn)動(dòng)態(tài)SQL的元素主要有:
- if
- choose / when / otherwise
- trim
- where
- set
- foreach
下面是映射文件的片段。
<select id="foo" parameterType="Blog" resultType="Blog">
select * from t_blog where 1 = 1
<if test="title != null">
and title = #{title}
</if>
<if test="content != null">
and content = #{content}
</if>
<if test="owner != null">
and owner = #{owner}
</if>
</select>
當(dāng)然也可以像下面這些書寫。
<select id="foo" parameterType="Blog" resultType="Blog">
select * from t_blog where 1 = 1
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="content != null">
and content = #{content}
</when>
<otherwise>
and owner = "owner1"
</otherwise>
</choose>
</select>
再看看下面這個(gè)例子。
<select id="bar" resultType="Blog">
select * from t_blog where id in
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
常用設(shè)計(jì)模式
- 裝飾器模式、如 IO 流,BufferedInputStream
- 單例模式
- 原型模式,Spring bean 每次從容器中調(diào)用Bean時(shí),都會(huì)返回一個(gè)新的實(shí)例
- 工廠模式,