版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
SpringData實(shí)戰(zhàn)(簡化數(shù)據(jù)庫訪問)目錄\h第一部分背景知識(shí)\h第1章SpringData項(xiàng)目\h1.1為Spring開發(fā)人員提供的NoSQL數(shù)據(jù)訪問功能\h1.2主題概述\h1.3領(lǐng)域\h1.4示例代碼\h1.4.1將源碼導(dǎo)入到IDE\h第2章Repository:便利的數(shù)據(jù)訪問層\h2.1快速入門\h2.2定義查詢方法\h2.2.1查找查詢的策略\h2.2.2衍生查詢\h2.2.3分頁和排序\h2.3定義Repository\h2.3.1調(diào)整Repository接口\h2.3.2手動(dòng)實(shí)現(xiàn)Repository方法\h2.4IDE集成\h2.4.1IntelliIDEA\h第3章使用Querydsl實(shí)現(xiàn)類型安全的查詢\h3.1Querydsl簡介\h3.2生成查詢?cè)P蚛h3.2.1構(gòu)建系統(tǒng)集成\h3.2.2所支持的注解處理器\h3.2.3使用Querydsl對(duì)存儲(chǔ)進(jìn)行查詢\h3.3集成SpringDataRepository\h3.3.1執(zhí)行斷言\h3.3.2手動(dòng)實(shí)現(xiàn)Repository\h第二部分關(guān)系型數(shù)據(jù)庫\h第4章JPARepository\h4.1示例工程\h4.2傳統(tǒng)方式\h4.3啟動(dòng)示例代碼\h4.4使用SpringDataRepository\h4.4.1事務(wù)性\h4.4.2Repository與Querydsl集成\h第5章借助QuerydslSQL實(shí)現(xiàn)類型安全的JDBC編程\h5.1示例工程與搭建過程\h5.1.1HyperSQL數(shù)據(jù)庫\h5.1.2Querydsl的SQL模塊\h5.1.3構(gòu)建系統(tǒng)集成\h5.1.4數(shù)據(jù)庫模式\h5.1.5示例工程的領(lǐng)域?qū)崿F(xiàn)\h5.2QueryDslJdbcTemplate\h5.3執(zhí)行查詢\h5.3.1Repository實(shí)現(xiàn)起步\h5.3.2查詢單個(gè)對(duì)象\h5.3.3OneToManyResultSetExtractor抽象類\h5.3.4CustomerListExtractor實(shí)現(xiàn)\h5.3.5RowMapper的實(shí)現(xiàn)類\h5.3.6查詢對(duì)象列表\h5.4插入、更新和刪除操作\h5.4.1使用SQLInsertClause進(jìn)行插入操作\h5.4.2使用SQLUpdateClause進(jìn)行更新操作\h5.4.3使用SQLDeleteClause進(jìn)行刪除行操作\h第三部分NoSQL\h第6章MongoDB:文檔存儲(chǔ)\h6.1MongoDB簡介\h6.1.1設(shè)置MongoDB\h6.1.2使用MongoDBShell\h6.1.3MongoDBJava驅(qū)動(dòng)\h6.2使用Spring命名空間搭建基礎(chǔ)設(shè)施\h6.3映射模塊\h6.3.1領(lǐng)域模型\h6.3.2搭建映射的基礎(chǔ)設(shè)施\h6.3.3索引\h6.3.4自定義轉(zhuǎn)換\h6.4MongoTemplate\h6.5MongoRepository\h6.5.1搭建基礎(chǔ)設(shè)施\h6.5.2Repository詳解\h6.5.3MongoQuerydsl集成\h第7章Neo4j:圖數(shù)據(jù)庫\h7.1圖數(shù)據(jù)庫\h7.2Neo4j\h7.3SpringDataNeo4j概覽\h7.4將領(lǐng)域建模為圖\h7.5使用SpringDataNeo4j持久化領(lǐng)域?qū)ο骪h7.5.1Neo4jTemplate\h7.6組合發(fā)揮圖和Repository的威力\h7.6.1基本的圖Repository操作\h7.6.2衍生和基于注解的查找方法\h7.7示例領(lǐng)域模型中的高級(jí)圖用例\h7.7.1單個(gè)節(jié)點(diǎn)的多重角色\h7.7.2以產(chǎn)品分類和標(biāo)簽為例講解圖中的索引\h7.7.3利用類似的興趣(協(xié)同過濾)\h7.7.4推薦\h7.8事務(wù)、實(shí)體生命周期以及抓取策略\h7.9高級(jí)映射模型\h7.10使用Neo4j服務(wù)器\h7.11從這里繼續(xù)學(xué)習(xí)\h第8章Redis:鍵/值存儲(chǔ)\h8.1Redis概述\h8.1.1搭建Redis\h8.1.2使用RedisShell\h8.2連接到Redis\h8.3對(duì)象轉(zhuǎn)換\h8.4對(duì)象映射\h8.5原子級(jí)計(jì)數(shù)器\h8.6發(fā)布/訂閱功能\h8.6.1對(duì)信息進(jìn)行監(jiān)聽和響應(yīng)\h8.6.2在Redis中使用Spring的緩存抽象\h第四部分快速應(yīng)用開發(fā)\h第9章使用SpringRoo實(shí)現(xiàn)持久層\h9.1Roo簡介\h9.2Roo的持久層\h9.3快速起步\h9.3.1借助命令行使用Roo\h9.3.2借助SpringToolSuite使用Roo\h9.4SpringRooJPARepository示例\h9.4.1創(chuàng)建工程\h9.4.2搭建JPA持久化\h9.4.3創(chuàng)建實(shí)體\h9.4.4定義Repository\h9.4.5創(chuàng)建Web層\h9.4.6運(yùn)行示例\h9.5SpringMongoDBJPARepository的例子\h9.5.1創(chuàng)建工程\h9.5.2搭建MongoDB持久化\h9.5.3創(chuàng)建實(shí)體\h9.5.4定義Repository\h9.5.5創(chuàng)建Web層\h9.5.6運(yùn)行示例\h第10章RESTRepository導(dǎo)出器\h10.1示例工程\h10.1.1與Rest導(dǎo)出器進(jìn)行交互\h10.1.2訪問Product\h10.1.3訪問Customer\h10.1.4訪問Order\h第五部分大數(shù)據(jù)\h第11章SpringforApacheHadoop\h11.1Hadoop開發(fā)面臨的挑戰(zhàn)\h11.2HelloWorld\h11.3揭秘HelloWorld\h11.4使用SpringforApacheHadoop的HelloWorld\h11.5在JVM中編寫HDFS腳本\h11.6結(jié)合HDFS腳本與Job提交\h11.7Job調(diào)度\h11.7.1使用TaskScheduler調(diào)度MapReduceJob\h11.7.2使用Quartz調(diào)度MapReduceJob\h第12章使用Hadoop分析數(shù)據(jù)\h12.1使用Hive\h12.1.1HelloWorld\h12.1.2運(yùn)行Hive服務(wù)器\h12.1.3使用HiveThrift客戶端\h12.1.4使用HiveJDBC客戶端\h12.1.5使用Hive分析Apache日志文件\h12.2使用Pig\h12.2.1HelloWorld\h12.2.2運(yùn)行PigServer\h12.2.3控制運(yùn)行期腳本的執(zhí)行\(zhòng)h12.2.4在SpringIntegration數(shù)據(jù)管道中調(diào)用Pig腳本\h12.2.5使用Pig分析Apache日志文件\h12.3使用HBase\h12.3.1HelloWorld\h12.3.2使用HBaseJava客戶端\h第13章使用SpringBatch和SpringIntegration創(chuàng)建大數(shù)據(jù)管道\h13.1收集并將數(shù)據(jù)加載到HDFS\h13.1.1SpringIntegration介紹\h13.1.2復(fù)制日志文件\h13.1.3事件流\h13.1.4事件轉(zhuǎn)發(fā)\h13.1.5管理\h13.1.6SpringBatch簡介\h13.1.7從數(shù)據(jù)庫中加載并處理數(shù)據(jù)\h13.2Hadoop工作流\h13.2.1SpringBatch對(duì)Hadoop的支持\h13.2.2將wordcount樣例改造為SpringBatch應(yīng)用\h13.2.3Hive和Pig的步驟\h13.3從HDFS導(dǎo)出數(shù)據(jù)\h13.3.1從HDFS到JDBC\h13.3.2從HDFS到MongoDB\h13.4收集并加載數(shù)據(jù)到Splunk\h第六部分?jǐn)?shù)據(jù)網(wǎng)格\h第14章分布式數(shù)據(jù)網(wǎng)格:GemFire\h14.1GemFire簡介\h14.2緩存與域\h14.3如何獲取GemFire\h14.4通過SpringXML命名空間配置GemFire\h14.4.1緩存配置\h14.4.2域配置\h14.4.3緩存客戶端配置\h14.4.4緩存服務(wù)端配置\h14.4.5WAN配置\h14.4.6磁盤存儲(chǔ)配置\h14.5使用GemfireTemplate進(jìn)行數(shù)據(jù)訪問\h14.6使用Repository\h14.6.1POJO映射\h14.6.2創(chuàng)建Repository\h14.6.3PDX序列化\h14.7支持持續(xù)查詢第一部分背景知識(shí)
第1章SpringData項(xiàng)目SpringData項(xiàng)目是在“SpringOne2010開發(fā)者大會(huì)”上創(chuàng)建的,該項(xiàng)目起源于當(dāng)年早些時(shí)候RodJohnson(SpringSource)和EmilEifrem(NeoTechnologies)共同參與的一場黑客會(huì)議。他們?cè)噲D把Neo4j圖形數(shù)據(jù)庫整合到Spring框架中,并評(píng)估了各種不同的方式。這次會(huì)議最終為初始版本的SpringDataNeo4j模塊奠定了基礎(chǔ),這個(gè)新的SpringSource項(xiàng)目旨在迎合大眾對(duì)于NoSQL數(shù)據(jù)存儲(chǔ)日益增長的興趣,而這種趨勢一直持續(xù)到了今天。從創(chuàng)立之初,Spring就為傳統(tǒng)的數(shù)據(jù)訪問技術(shù)提供了完善的支持。不管是使用JDBC、Hibernate、JDO、TopLink還是iBatis作為持久化技術(shù),Spring都大大簡化了數(shù)據(jù)訪問層的實(shí)現(xiàn)。這種支持主要包括簡化基礎(chǔ)配置、資源管理并將異常轉(zhuǎn)換成Spring的DataAccessExceptions。這種支持多年以來已經(jīng)逐漸成熟,最新版本的Spring也對(duì)這一層提供了很好的支持。過去涉及數(shù)據(jù)持久化時(shí),關(guān)系型數(shù)據(jù)庫是可供選擇的主要工具,所以Spring對(duì)傳統(tǒng)數(shù)據(jù)訪問的支持只把關(guān)系數(shù)據(jù)庫作為唯一的目標(biāo)。但隨著NoSQL的問世并成為工具箱中可行的替代方案,從支持開發(fā)人員的角度來看就有了新的領(lǐng)域需要補(bǔ)充。另一方面,對(duì)于傳統(tǒng)關(guān)系型存儲(chǔ)的支持也還有許多需要改善的地方。這兩個(gè)方面是SpringData項(xiàng)目的主要驅(qū)動(dòng)力。SpringData包含NoSQL存儲(chǔ)的專有模塊以及為關(guān)系型數(shù)據(jù)庫提供更好支持的JPA和JDBC模塊。1.1為Spring開發(fā)人員提供的NoSQL數(shù)據(jù)訪問功能盡管用NoSQL這個(gè)術(shù)語統(tǒng)稱一系列的新型數(shù)據(jù)存儲(chǔ),但所有的這些存儲(chǔ)都有不同的特性和使用場景。具有諷刺意味的是,正是這種缺失特性的特點(diǎn)(缺乏對(duì)運(yùn)行SQL查詢的支持)命名了這一系列數(shù)據(jù)庫。由于這些存儲(chǔ)的特征非常不同,所以它們的Java驅(qū)動(dòng)要使用完全不同的API才能充分發(fā)揮其特性和功能。如果試圖對(duì)這些差異進(jìn)行抽象的話,就會(huì)失去每種NoSQL數(shù)據(jù)存儲(chǔ)能帶來的收益。圖形數(shù)據(jù)庫應(yīng)該用來存儲(chǔ)高度關(guān)聯(lián)的數(shù)據(jù);文件數(shù)據(jù)庫應(yīng)該存儲(chǔ)樹狀以及聚合狀的數(shù)據(jù)結(jié)構(gòu);如果需要類似緩存的功能和存取模式,那應(yīng)該選擇鍵/值(key/value)存儲(chǔ)。JavaEE(企業(yè)版)領(lǐng)域通過JPA提供了持久化API,這個(gè)API或許可以當(dāng)作NoSQL數(shù)據(jù)庫前端實(shí)現(xiàn)的候選方案。但是令人遺憾的是,規(guī)范的前兩句話已經(jīng)預(yù)示了這一點(diǎn)似乎不可能實(shí)現(xiàn):本文檔是關(guān)于在JavaEE和JavaSE中管理持久化和對(duì)象/關(guān)系映射的JavaAPI規(guī)范。這項(xiàng)成果的技術(shù)目標(biāo)是為Java應(yīng)用開發(fā)人員提供一個(gè)對(duì)象/關(guān)系映射機(jī)制,借助它可以使用域模型來管理關(guān)系型數(shù)據(jù)庫。這一主題在規(guī)范的后面有清晰的體現(xiàn),它定義了與關(guān)系型持久化領(lǐng)域緊密關(guān)聯(lián)的概念和API。@Table注解對(duì)NoSQL數(shù)據(jù)庫而言沒有太大的意義,@Column或@JoinColumn也是如此。在MongoDB這樣的存儲(chǔ)中如何實(shí)現(xiàn)事務(wù)API呢?要知道在這些環(huán)境中并沒有提供跨多文檔操作的事務(wù)語義。因此在NoSQL存儲(chǔ)之上實(shí)現(xiàn)JPA層,最好采用一個(gè)基于配置文件的API。另一方面,所有NoSQL存儲(chǔ)提供的特殊功能(地理空間功能、map-reduce操作、圖形遍歷)都需要以專有的方式去實(shí)現(xiàn),因?yàn)镴PA并沒有為它們提供抽象。因此我們可能會(huì)以“兩邊不討好”(worst-of-both-world)的局面收?qǐng)觯河械牟糠挚梢酝ㄟ^JPA來實(shí)現(xiàn),另外還需要使用專有的特性來重新啟用與特定存儲(chǔ)相關(guān)的功能。上文排除了采用JPA作為這些存儲(chǔ)的抽象API的可能性。Spring生態(tài)系統(tǒng)中的各種項(xiàng)目為開發(fā)人員帶來了高效率以及一致的編程模型,我們依然希望將其用于簡化對(duì)NoSQL存儲(chǔ)的使用。為此,SpringData團(tuán)隊(duì)發(fā)布了如下使命宣言:針對(duì)NoSQL和關(guān)系型存儲(chǔ),SpringData提供了基于Spring的熟知且一致的編程模型,同時(shí)保留特定存儲(chǔ)的特性和功能。因此,我們決定采取略有不同的方法。不再試圖以單一的API將所有存儲(chǔ)抽象化。相反,SpringData項(xiàng)目對(duì)不同的存儲(chǔ)實(shí)現(xiàn)都提供一致的編程模型,并使用了在Spring框架中大家已熟悉的模式和抽象。這樣,在使用不同的存儲(chǔ)時(shí),會(huì)有一致的體驗(yàn)。1.2主題概述SpringData的核心目標(biāo)是:支持對(duì)所有的存儲(chǔ)進(jìn)行資源配置,從而實(shí)現(xiàn)對(duì)該存儲(chǔ)的訪問。這種支持主要是通過XML命名空間和SpringJavaConfig的支持類實(shí)現(xiàn)的,這可以使我們輕松地對(duì)Mongo數(shù)據(jù)庫、嵌入式Neo4j實(shí)例等建立訪問。除此之外,它也集成了Spring的核心功能,如JMX。這意味著某些存儲(chǔ)可以通過原生API暴露統(tǒng)計(jì)數(shù)據(jù),這些數(shù)據(jù)將會(huì)由SpringData暴露給JMX。大部分的NoSQLJavaAPI并未支持將領(lǐng)域?qū)ο笥成涞酱鎯?chǔ)的數(shù)據(jù)抽象(MongoDB中的文件,Neo4j中的節(jié)點(diǎn)與關(guān)系)。因此,當(dāng)使用原生的Java驅(qū)動(dòng)程序進(jìn)行讀取和寫入操作時(shí),通常需要編寫大量的代碼來將數(shù)據(jù)映射到應(yīng)用程序的領(lǐng)域?qū)ο?。所以SpringData模塊最核心的部分是一個(gè)映射和轉(zhuǎn)換的API,用來獲取要持久化的領(lǐng)域類中的元數(shù)據(jù),使得任意領(lǐng)域?qū)ο蠖伎梢赞D(zhuǎn)換成存儲(chǔ)用的數(shù)據(jù)類型。在此基礎(chǔ)上,就如同著名的SpringJdbcTemplate、JmsTemplate等,我們也會(huì)看到以模板模式實(shí)現(xiàn)的API,其中包括RedisTemplate、MongoTemplate等。或許你已經(jīng)知道,這些模板提供了讓我們可以執(zhí)行常用操作的輔助方法。例如:在一條語句中持久化一個(gè)對(duì)象的時(shí)候,能夠自動(dòng)進(jìn)行資源管理和異常處理。此外,還提供了回調(diào)接口的API,允許在資源管理和異常處理過程中使用存儲(chǔ)原生的API,以提高靈活性。這些功能給我們提供了一個(gè)工具箱,使得我們可以像使用傳統(tǒng)的數(shù)據(jù)庫那樣來實(shí)現(xiàn)數(shù)據(jù)訪問層。后面的章節(jié)將會(huì)詳細(xì)介紹這些功能。為了讓程序變得更簡單,SpringData還在模板實(shí)現(xiàn)的基礎(chǔ)上提供了一個(gè)存儲(chǔ)(repository)抽象,這將減少數(shù)據(jù)訪問對(duì)象在實(shí)現(xiàn)一個(gè)普通接口時(shí)去定義通用場景的代價(jià),如標(biāo)準(zhǔn)的CRUD(創(chuàng)建、讀取、更新、刪除)操作以及執(zhí)行存儲(chǔ)支持的查詢語句。事實(shí)上,這種抽象位于最頂層,而且它會(huì)盡可能在合理范圍內(nèi)將不同存儲(chǔ)API融合在一起。因此,存儲(chǔ)的操作將擁有許多共同點(diǎn)。這也是后面會(huì)有專門的章節(jié)(第2章)來介紹基本編程模型的原因。接著,我們來看一下用來展示這些特定存儲(chǔ)模塊功能所用的示例代碼和領(lǐng)域模型。1.3領(lǐng)域?yàn)榱苏f明各種SpringData模塊的用法,我們會(huì)使用電子商務(wù)部門的示例領(lǐng)域(如圖1-1所示)。由于各種NoSQL數(shù)據(jù)存儲(chǔ)通常具有特定的功能和適用場景,在個(gè)別章節(jié)會(huì)對(duì)領(lǐng)域的實(shí)現(xiàn)方式做出一些調(diào)整,甚至只有它的部分實(shí)現(xiàn),這種做法不代表必須以一種特定的方法來實(shí)現(xiàn)領(lǐng)域,而是強(qiáng)調(diào)某些存儲(chǔ)應(yīng)該更適用于特定的應(yīng)用場景。在模型的核心,有客戶(customer),包含客戶的基本資料,如姓、名、電子郵箱地址、地址(一組包含街道、城市和國家的集合),還有由產(chǎn)品名稱、描述、價(jià)格和其他屬性構(gòu)成的產(chǎn)品(product)。這些抽象是組成CRM(客戶關(guān)系管理系統(tǒng))和庫存系統(tǒng)的基礎(chǔ)。最重要的是客戶可以訂購訂單(Order),訂單信息包含訂購的客戶、郵寄和付款地址、訂購時(shí)間、訂單狀態(tài)和一組商品明細(xì)。而這些商品明細(xì)又包含一個(gè)特定的產(chǎn)品、訂購的數(shù)量和產(chǎn)品的價(jià)格。圖1-1領(lǐng)域模型1.4示例代碼本書的示例代碼可從GitHub(\h/SpringSource/spring-data-book)上獲取。它是一個(gè)Maven項(xiàng)目,包含每一章的模塊。另外,還需要在電腦中安裝Maven3或者一個(gè)能導(dǎo)入Maven項(xiàng)目的IDE,比如SpringToolSuite(STS)。從下面的操作中可以看到,取得示例代碼就如同復(fù)制版本庫一樣簡單:現(xiàn)在可以在命令行中執(zhí)行Maven來構(gòu)建代碼:這樣Maven會(huì)解析依賴、編譯和測試代碼,執(zhí)行測試,最終打包模塊。1.4.1將源碼導(dǎo)入到IDESTS/Eclipse由于STS已經(jīng)配備了m2eclipse插件,所以可以在IDE中輕松使用Maven項(xiàng)目。如果已經(jīng)下載并安裝(詳情請(qǐng)見第3章)了STS,即可從File菜單選擇Import選項(xiàng),并在彈出的對(duì)話框中選擇ExistingMavenProjects,如圖1-2所示。圖1-2導(dǎo)入Maven項(xiàng)目到Eclipse(步驟1/2)在下一個(gè)窗口中,單擊Browse按鈕來選擇剛剛簽出的示例項(xiàng)目的文件夾。之后,在正下方的窗格中會(huì)列出并選中各個(gè)Maven模塊(如圖1-3所示)。單擊Finish按鈕進(jìn)行下一步,STS會(huì)將選中的Maven模塊導(dǎo)入到工作區(qū)。它將依照模塊根目錄下的pom.xml文件來解析所需的依賴和源文件夾。圖1-3將Maven項(xiàng)目導(dǎo)入到Eclispe(步驟2/2)最終會(huì)看到如圖1-4所示的包或者項(xiàng)目資源管理器。這時(shí)項(xiàng)目應(yīng)能成功編譯并且不包含紅色錯(cuò)誤標(biāo)記。圖1-4完成導(dǎo)入的EclipseProjectExplorer使用了Querydsl(詳見第5章)的項(xiàng)目可能會(huì)引發(fā)紅色的錯(cuò)誤標(biāo)記。原因是m2eclipse插件需要知道:在IDE構(gòu)建的生命周期中,哪個(gè)階段執(zhí)行Querydsl關(guān)聯(lián)的Maven插件。可以從m2e-querydsl擴(kuò)展更新站點(diǎn)來安裝這個(gè)插件,也可以在項(xiàng)目主頁上找最新的版本(\h/ilx/m2e-querydsl),復(fù)制最新版本的鏈接,并將它添加到可用的更新站點(diǎn)的列表中,如圖1-5所示。然后安裝在更新網(wǎng)站上發(fā)布了的功能,重新啟動(dòng)Eclipse,并更新Maven項(xiàng)目配置(在項(xiàng)目中單擊鼠標(biāo)右鍵,從彈出的快捷菜單中選擇Maven→UpdateProject),這樣就能去除Eclipse中的錯(cuò)誤標(biāo)記,并且成功地完成項(xiàng)目的編譯。圖1-5增加m2e-querydsl更新網(wǎng)站IntelliJIDEAIDEA可以直接打開Maven項(xiàng)目而不需要其他額外的設(shè)置。選擇菜單中的OpenProject選項(xiàng)之后會(huì)彈出對(duì)話框(如圖1-6所示)。圖1-6將Maven項(xiàng)目導(dǎo)入到IDEA(步驟1/2)IDE會(huì)打開項(xiàng)目并獲取所需的依賴。在下一個(gè)步驟(如圖1-7所示),它會(huì)探測已使用的框架(如Spring框架、JPA、WebApp等);可以使用彈出窗口的配置鏈接或者在事件日志中配置這些框架。圖1-7將Maven項(xiàng)目導(dǎo)入到IDEA(步驟2/2)這樣項(xiàng)目就可以使用了。此時(shí)可以看到“Project”視圖和“MavenProject”視圖,如圖1-8所示。然后便可以像往常一樣編譯項(xiàng)目了。圖1-8打開SpringDataBook項(xiàng)目的IDEA接下來,必須加入SpringDataJPA模塊的JPA支持以啟用finder方法以及版本庫的錯(cuò)誤檢查功能。只需要右鍵單擊該模塊并選擇“AddFrameworkSupport”項(xiàng),在彈出的對(duì)話框中勾選JavaEE持久化的支持并且選擇Hibernate提供的持久化支持,如圖1-9所示。接著它會(huì)生成一個(gè)持久化單元配置src/main/java/resources/META-INF/persistence.xml文件。圖1-9在SpringDataJPA模塊啟用JPA支持
第2章Repository:便利的數(shù)據(jù)訪問層長期以來,實(shí)現(xiàn)應(yīng)用程序的數(shù)據(jù)訪問層一直是件繁瑣的工作,因?yàn)槲覀兘?jīng)常需要編寫大量的樣板式代碼,而且貧血(anemic)的領(lǐng)域類并沒有按照真正面向?qū)ο蠡蝾I(lǐng)域驅(qū)動(dòng)方式來進(jìn)行設(shè)計(jì)。因此SpringDataRepository抽象的目標(biāo)就是大幅簡化各種持久化存儲(chǔ)持久層的實(shí)現(xiàn)。我們將會(huì)使用SpringDataJPA模塊作為例子來討論Repository抽象的基本理念。對(duì)于其他類型的存儲(chǔ),可以參考對(duì)應(yīng)的例子。2.1快速入門我們選取領(lǐng)域模型中的Customer領(lǐng)域類,它會(huì)被持久化到任意的存儲(chǔ)之中。這個(gè)類應(yīng)該如示例2-1所示。示例2-1Customer領(lǐng)域類傳統(tǒng)的實(shí)現(xiàn)數(shù)據(jù)訪問層的方式至少需要實(shí)現(xiàn)一個(gè)存儲(chǔ)類(repositoryclass),這個(gè)類會(huì)包含基本的CRUD(Create、Read、Update與Delete)方法以及通過限制條件來訪問實(shí)體子集的查詢方法。SpringDataRepository的方式能夠避免大多數(shù)的代碼,只需為這個(gè)實(shí)體存儲(chǔ)聲明簡單的接口定義即可,如示例2-2所示。示例2-2CustomerRepository接口定義正如你所見,我們擴(kuò)展了SpringData的Repository接口,它是通用的標(biāo)識(shí)接口。它的主要職責(zé)是讓SpringData的基礎(chǔ)設(shè)施識(shí)別出所有用戶定義的SpringDataRepository。除此之外,它還會(huì)捕獲托管的領(lǐng)域類以及實(shí)體的ID類型,稍后這些功能會(huì)提供很大的便利性。為了能夠自動(dòng)發(fā)現(xiàn)所聲明的接口,可以使用存儲(chǔ)特定的XML命名空間中的<repositories/>元素(如示例2-3所示),或是在使用JavaConfig時(shí)借助相關(guān)的@Enable...Repositories注解(如示例2-4所示)。在示例中會(huì)使用JPA。我們只需將XML元素的base-package屬性配置為我們的根包(rootpackage),SpringData會(huì)掃描它來查找Repository接口。如果沒有給出更進(jìn)一步的配置,那么它只會(huì)簡單地檢查包中帶有注解的類。示例2-3使用XML激活SpringDataRepository示例2-4使用JavaConfig激活SpringDataRepositoryXML和JavaConfig配置都需要添加存儲(chǔ)專用的Bean聲明來進(jìn)行完善,如JPA的EntityManagerFactory以及DataSource等。對(duì)于其他形式的存儲(chǔ),我們只需使用對(duì)應(yīng)的命名空間元素或注解即可。例如,示例2-5所示的配置片段,將會(huì)找到SpringDataRepository并創(chuàng)建SpringBean,這些Bean實(shí)際上是由一組實(shí)現(xiàn)了所發(fā)現(xiàn)接口的代理所組成的。因此,現(xiàn)在可以繼續(xù)編寫客戶端,通過Spring的自動(dòng)裝配就能訪問這個(gè)Bean了。CustomerRepository接口建立之后,我們就可以繼續(xù)深入學(xué)習(xí)并添加一些易于聲明的查詢方法。常見的需求是通過電子郵件地址來獲取Customer。為了做到這一點(diǎn),我們添加合適的查詢方法,如示例2-6所示。示例2-5客戶端使用SpringDataRepository示例2-6聲明查詢方法命名空間元素將會(huì)在容器啟動(dòng)的時(shí)候掃描到這個(gè)接口并觸發(fā)SpringData的基礎(chǔ)設(shè)施為其創(chuàng)建SpringBean?;A(chǔ)設(shè)施會(huì)探查接口中聲明的方法并確定方法調(diào)用時(shí)要執(zhí)行的查詢。如果只是這樣簡單地定義方法的話,那么SpringData將會(huì)根據(jù)其名字衍生出一個(gè)查詢。在定義查詢方面還有其他的途徑可選,可以閱讀2.2小節(jié)“定義查詢方法”來了解更多信息。在示例2-6中,由于我們遵循了領(lǐng)域?qū)ο髮傩缘拿s定,因而查詢可以衍生得到。查詢方法名中的EmailAddress部分其實(shí)就對(duì)應(yīng)了Customer類的emailAddress屬性,因此,在使用JPA模塊時(shí),SpringData會(huì)自動(dòng)為聲明的方法衍生出selectCfromCustomercwherec.emailAddress=?1。它還會(huì)檢查方法聲明中屬性引用的合法性,如果發(fā)現(xiàn)任何錯(cuò)誤則會(huì)在容器啟動(dòng)時(shí),出現(xiàn)啟動(dòng)失敗?,F(xiàn)在,客戶端可以很容易地執(zhí)行這個(gè)方法,給定的方法參數(shù)會(huì)綁定到根據(jù)方法名衍生出來的查詢之中并且執(zhí)行該查詢,如示例2-7所示。示例2-7執(zhí)行查詢方法2.2定義查詢方法2.2.1查找查詢的策略剛才看到的接口只聲明了一個(gè)簡單的查詢方法。聲明的方法會(huì)被基礎(chǔ)設(shè)施探測到并進(jìn)行解析,最終衍生出與存儲(chǔ)相關(guān)的查詢。但是,隨著查詢變得更加復(fù)雜,方法名會(huì)變得很冗長,顯得很笨拙。對(duì)于更復(fù)雜的查詢,依靠方法解析器所支持的關(guān)鍵字就不夠了。因此,每種存儲(chǔ)模塊都提供了@Query注解,如示例2-8所示,它會(huì)接受存儲(chǔ)相關(guān)的查詢語言所支持的查詢字符串,從而允許查詢執(zhí)行時(shí)進(jìn)一步地定制化。示例2-8使用@Query注解手動(dòng)定義查詢?cè)谶@里,我們使用JPA作為例子并手動(dòng)定義了一個(gè)查詢,當(dāng)然這個(gè)查詢?cè)疽彩强梢酝ㄟ^衍生得到的。查詢甚至可以外部化配置到屬性文件中(它位于MATA-INF目錄下的$store-named-perties文件中),在這里$store是用于替換jpa、mongo以及neo4j等的占位符。key值必須要遵循$domainType.$methodName這樣的約定。因此,為了將我們已有的方法替換成外部配置的命名查詢,key將會(huì)是Customer.findByEmailAddress。如果是使用已命名查詢的話,那就不需要使用@Query注解了。2.2.2衍生查詢?nèi)缡纠?-9所示,查詢衍生機(jī)制內(nèi)置于SpringDataRepository的基礎(chǔ)設(shè)施之中,對(duì)基于Repository的實(shí)體來構(gòu)建限制性的查詢很有用處。我們會(huì)從方法中截取findBy、readBy以及getBy前綴并解析剩余的部分。一個(gè)基礎(chǔ)的用法是,基于實(shí)體的屬性來定義條件并使用And和Or將它們連接起來。示例2-9由方法名衍生查詢解析的實(shí)際結(jié)果依賴于我們所使用的數(shù)據(jù)存儲(chǔ)。這里也有一些需要注意的通用事項(xiàng)。表達(dá)式通常會(huì)是屬性的遍歷以及操作符,它們可以連接起來。如示例2-9所示的那樣,可以通過And以及Or來連接屬性表達(dá)式。除此之外,對(duì)于屬性表達(dá)式來說,還可以支持各種操作符,如Between、LessThan、GreaterThan以及Like。因?yàn)椴煌臄?shù)據(jù)存儲(chǔ)之間所支持的操作符有所區(qū)別,所以要看查閱每種存儲(chǔ)對(duì)應(yīng)的章節(jié)。屬性表達(dá)式屬性表達(dá)式可以直接引用所管理實(shí)體的屬性(如示例2-9中所示)。在查詢創(chuàng)建的時(shí)候,我們已經(jīng)確保所解析的屬性就是領(lǐng)域類的屬性。但是,依然可以遍歷嵌套的屬性來定義限制條件。在前面我們可以看到,Customer的Address中具有ZipCode屬性。在這種情況下,如下這種方法名的查詢將會(huì)創(chuàng)建x.address.zipCode這樣的屬性遍歷。這個(gè)方案的算法首先會(huì)將整體(AddressZipCode)作為一個(gè)屬性進(jìn)行解析并檢查領(lǐng)域類中是否具有該名稱的屬性(第一個(gè)字母小寫)。如果它存在的話,就會(huì)使用該屬性。如果不存在,它會(huì)從右邊開始將源信息按照“駝峰”命名的規(guī)則將其拆分為頭部和尾部,然后嘗試查找對(duì)應(yīng)的屬性(如AddressZip和Code)。如果按照這個(gè)頭部信息找到了屬性,那么我們將會(huì)使用尾部的信息繼續(xù)往下構(gòu)建樹形的信息。因?yàn)槭纠?,第一次的分割并不匹配,我們將分割點(diǎn)繼續(xù)左移(從“AddressZip、Code”移到“Address、ZipCode”)。盡管這在大多數(shù)的場景下都是可行的,但是在一定情況下算法可能會(huì)選擇錯(cuò)誤的屬性。假設(shè)Customer同時(shí)還有一個(gè)addressZip屬性。那么我們的算法將會(huì)在第一次分割的時(shí)候就完成了匹配,這實(shí)際上選擇了錯(cuò)誤的屬性,并且會(huì)導(dǎo)致最終的失?。ㄒ?yàn)閍ddressZip類型可能并沒有code屬性)。為了解決這種模棱兩可的問題,可以在方法名中使用下劃線(_)來手動(dòng)定義遍歷點(diǎn)。所以,我們的方法名最終看起來是這樣的:2.2.3分頁和排序如果查詢所返回的結(jié)果數(shù)量增長很明顯,那么分塊訪問數(shù)據(jù)就很有意義了。為了做到這一點(diǎn),SpringData提供了可與Repository一起使用的分頁API。要讀取哪一塊數(shù)據(jù)的定義隱藏在Pageable接口及其實(shí)現(xiàn)PageRequest之中。得到的分頁數(shù)據(jù)存放在Page中,它不僅包含了數(shù)據(jù)本身,還包含了元信息,這些信息包括它是不是第一頁或最后一頁以及一共有多少頁等。為了計(jì)算這個(gè)元數(shù)據(jù),除了初始的查詢外,我們需要觸發(fā)第二次查詢。借助于Repository,我們要使用分頁功能時(shí)只需添加一個(gè)Pageable方法參數(shù)即可。不像其他的參數(shù)那樣,這個(gè)參數(shù)是不與查詢綁定的,用來限制返回的結(jié)果集。一種可選的方案就是返回Page類型,它會(huì)對(duì)結(jié)果集進(jìn)行限制,但是需要另外一次查詢來獲取元信息(如可用元素的總數(shù))。另一種可選的方案是使用List,它會(huì)避免額外的查詢,但是不會(huì)提供元數(shù)據(jù)。如果不需要分頁功能,只是想要排序,那么可以給方法簽名上添加Sort參數(shù),如示例2-10所示。示例2-10使用Pageable和Sort的查詢方法第一個(gè)方法允許傳遞Pageable實(shí)例到查詢方法中,從而為靜態(tài)定義的查詢動(dòng)態(tài)地增加分頁功能。排序的功能可以通過Sort參數(shù)顯式地傳遞給方法,也可以內(nèi)嵌到PageRequest值對(duì)象中,如示例2-11所示。示例2-11使用Pageable和Sort2.3定義Repository到目前為止,我們看到了帶有查詢方法的Repository接口,這些查詢有的是從方法名中衍生出來的,有的是手動(dòng)聲明的,這取決于SpringData為實(shí)際存儲(chǔ)類型所提供的使用方式。為了衍生出這些查詢,我們必須擴(kuò)展SpringData的特定標(biāo)識(shí)接口:Repository。除了查詢以外,在你的Repository中還需要一些其他的功能:存儲(chǔ)對(duì)象,刪除對(duì)象,根據(jù)ID進(jìn)行查找,返回所有存儲(chǔ)的實(shí)體或按頁對(duì)它們進(jìn)行訪問。通過Repository接口來暴露這些功能的最簡單方式就是使用一個(gè)SpringData所提供的更為高級(jí)的Repository接口。Repository一個(gè)簡單的標(biāo)識(shí)接口,允許SpringData的基礎(chǔ)設(shè)施獲取用戶定義的Repository。CrudRepository擴(kuò)展自Repository并添加了基本的持久化方法如對(duì)實(shí)體的保存、查找以及刪除。PagingAndSortingRepositories擴(kuò)展自CrudRepository并添加了按頁訪問實(shí)體以及根據(jù)給定的條件(criteria)進(jìn)行排序的方法。假設(shè)我們想讓CustomerRepository暴露基本的CRUD方法,所需要做就是修改其聲明,如示例2-12所示。示例2-12暴露CRUD方法的CustomerRepositoryCrudRepository接口如示例2-13所示。它包括了保存單個(gè)實(shí)體以及多個(gè)Iterable實(shí)體的方法、獲取單個(gè)實(shí)體或所有實(shí)體的方法以及不同形式的delete(...)方法。示例2-13CrudRepository支持Repository方式的每個(gè)SpringData模塊都提供了這個(gè)接口的實(shí)現(xiàn)。因此,我們聲明的命名空間元素會(huì)觸發(fā)基礎(chǔ)設(shè)施,這些設(shè)施不僅會(huì)啟動(dòng)那些用于執(zhí)行查詢方法的合適代碼,同時(shí)還會(huì)使用一個(gè)通用Repository實(shí)現(xiàn)類的實(shí)例來在背后執(zhí)行CrudRepository中所聲明的方法,最終會(huì)將save(...)、findAll()等方法的調(diào)用委托給該實(shí)例。PagingAndSortingRepository(如示例2-14所示)擴(kuò)展了CrudRepository并為通用的findAll(...)添加了處理Pageable和Sort實(shí)例的方法,從而能夠?qū)崿F(xiàn)逐頁訪問實(shí)體。示例2-14PagingAndSortingRepository要將這些功能引入到CustomerRepository中,只需簡單地?cái)U(kuò)展PagingAndSortingRepository來取代CrudRepository即可。2.3.1調(diào)整Repository接口正如我們?cè)谇懊嫠?,通過擴(kuò)展合適的SpringData接口,可以很容易地引入大量預(yù)先定義的功能。這種級(jí)別的粒度實(shí)際上是一種權(quán)衡,那就是如果為所有的查找方法、所有的保存方法等都定義單獨(dú)的接口,我們會(huì)暴露接口的數(shù)量(以及因此導(dǎo)致的復(fù)雜性)以及開發(fā)人員使用的便利性之間的權(quán)衡。但是,可能會(huì)有這樣的場景,那就是只想暴露讀方法(CRUD中的R)或者只想在Repository接口中將刪除方法屏蔽掉。如今,SpringData允許定義個(gè)性化的基礎(chǔ)Repository,只需按照以下的步驟操作即可。1.創(chuàng)建一個(gè)接口,這個(gè)接口要么擴(kuò)展自Repository,要么添加@RepositoryDefinition注解。2.添加想要暴露的方法并確保它們與SpringData基礎(chǔ)Repository接口所提供的方法簽名相同。3.對(duì)于實(shí)體所對(duì)應(yīng)的接口聲明,要使用這個(gè)接口作為基礎(chǔ)接口。為了闡述這一點(diǎn),假設(shè)我們只想暴露接收Pageable的findAll(...)方法以及save方法。這個(gè)基礎(chǔ)接口看起來可能如示例2-15所示。示例2-15自定義基礎(chǔ)Repository接口需要注意的一點(diǎn)是我們?yōu)檫@個(gè)接口添加了一個(gè)額外的注解@NoRepositoryBean,從而確保SpringDataRepository的基礎(chǔ)設(shè)施不會(huì)試圖為其創(chuàng)建Bean的實(shí)例。讓CustomerRepository擴(kuò)展這個(gè)接口就能精確做到只暴露你所定義的API。接下來可以定義出各種基本的接口(如ReadOnlyRepository或SaveOnlyRepository)甚至組成它們的繼承體系,這取決于項(xiàng)目的需要。通常建議本地定義的CRUD方法在開始的時(shí)候直接位于每個(gè)實(shí)體的具體Repository中,必要的話,再將它們要么轉(zhuǎn)移到SpringData提供的基礎(chǔ)Repository中,要么轉(zhuǎn)移到特制的Repository中。按照這種方式,可以保證隨著項(xiàng)目復(fù)雜性的增長,構(gòu)件(artifact)的數(shù)量能夠自然地增長。2.3.2手動(dòng)實(shí)現(xiàn)Repository方法到目前為止,看到了兩種類型的Repository方法:CRUD方法和查詢方法。每種類型都是由SpringData的基礎(chǔ)設(shè)施實(shí)現(xiàn)的,要么通過背后的實(shí)現(xiàn)類,要么通過查詢執(zhí)行引擎。當(dāng)構(gòu)建應(yīng)用程序的時(shí)候,這兩種場景可能會(huì)覆蓋你所面臨的很大范圍的數(shù)據(jù)訪問操作。但是,有些場景需要手動(dòng)實(shí)現(xiàn)代碼。現(xiàn)在,讓我們看一下如何做到這一點(diǎn)。我們開始只實(shí)現(xiàn)那些需要手動(dòng)實(shí)現(xiàn)的功能并在實(shí)現(xiàn)類中遵循一些命名的約定,如示例2-16所示。示例2-16為Repository實(shí)現(xiàn)自定義功能接口和實(shí)現(xiàn)類均不需要了解SpringData的任何事情。它與使用Spring手動(dòng)實(shí)現(xiàn)代碼非常類似。按照SpringData來看,這個(gè)代碼片段最有意思的地方在于實(shí)現(xiàn)類的名字遵循了命名的約定,也就是在核心Repository接口(在我們的場景中就是CustomerRepository)的名字上加Impl后綴。同時(shí)需要注意,我們將接口和實(shí)現(xiàn)類都設(shè)為包內(nèi)私有(packageprivate),從而阻止從包外訪問它們。最后一步是修改初始Repository接口的聲明,使其擴(kuò)展剛剛引入的接口,如示例2-17所示。示例2-17在CustomerRepository中包含自定義功能現(xiàn)在,我們已經(jīng)將CustomerRepositoryCustom暴露的API引入到CustomerRepository之中了,這會(huì)使其成為Customer數(shù)據(jù)訪問API的中心點(diǎn)??蛻舳舜a現(xiàn)在就可以調(diào)用CustomerRepository.myCustomMethod(...)了。但是,這個(gè)實(shí)現(xiàn)類會(huì)如何被發(fā)現(xiàn)并置于最終執(zhí)行的代理之中的呢?實(shí)際上,Repository的啟動(dòng)過程看起來是這樣的。1.發(fā)現(xiàn)repository接口(如CustomerRepository)。2.嘗試尋找一個(gè)Bean定義,這個(gè)Bean的名字為接口的小寫形式并添加Impl后綴(如customerRepositoryImpl)。如果能夠找到,就使用它。3.如果沒有找到,我們會(huì)掃描尋找一個(gè)類,這個(gè)類的名字為核心Repository接口的名字并添加Impl后綴(例如,在這個(gè)例子中CustomerRepositoryImpl會(huì)被找到)。如果找到了這樣的類,那么將其注冊(cè)為SpringBean并使用它。4.找到的自定義實(shí)現(xiàn)類將會(huì)裝配到被發(fā)現(xiàn)接口的代理配置之中并且在方法調(diào)用時(shí)會(huì)作為潛在的目標(biāo)類。這種機(jī)制可以很容易地為特定Repository實(shí)現(xiàn)自定義代碼。用于進(jìn)行實(shí)現(xiàn)查找的后綴可以在XML命名空間中或啟用Repository的注解屬性中(查看各種存儲(chǔ)相關(guān)的章節(jié)來了解更多)進(jìn)行個(gè)性化設(shè)置。參考文檔(\hhttps://bit.ly/VzYToo)中也包含了一些關(guān)于如何將自定義的行為應(yīng)用于多個(gè)Repositor的學(xué)習(xí)材料。2.4IDE集成在3.0版本中,Spring工具套件(SpringToolSuite,STS)提供了與SpringDataRepository抽象進(jìn)行集成的功能。STS為SpringData所提供的核心支持是查找方法的查詢衍生機(jī)制。它所能做到的第一件事就是在IDE中校驗(yàn)衍生查詢方法的正確性,這樣,不需要啟動(dòng)ApplicationContext就能立刻探測出方法名中引入的拼寫錯(cuò)誤。
STS是一個(gè)特殊的Eclipse發(fā)布版本,它內(nèi)置了一些插件從而盡可能地便于進(jìn)行Spring應(yīng)用的構(gòu)建。這個(gè)工具可以在項(xiàng)目的站點(diǎn)上(\hhttp://www./sts)下載或者使用一般的Eclipse發(fā)布版本并通過STS更新站點(diǎn)進(jìn)行更新(基于Eclipse3.8(\h/release/TOOLS/update/e3.8)或Eclipse4.2(\h/release/TOOLS/update/e4.2))。如圖2-1所示,IDE檢測到Descrption是非法的,因?yàn)镻roduct類中并沒有這樣的屬性。為了發(fā)現(xiàn)這些拼寫錯(cuò)誤,它會(huì)分析Product領(lǐng)域類(這些事情在啟動(dòng)SpringDataRepository時(shí)也會(huì)做)來獲取屬性并將方法名解析為屬性的遍歷樹。為了盡早避免這種類型的拼寫錯(cuò)誤,STS的SpringData輔助功能為屬性名、條件關(guān)鍵字(criteriakeyword)以及像And和Or這樣的連接符提供了代碼補(bǔ)全功能,如圖2-2所示。圖2-1SpringDataSTS對(duì)衍生查詢方法名進(jìn)行校驗(yàn)圖2-2對(duì)衍生查詢方法的屬性代碼補(bǔ)全提示Order類中有一些你可能想要引用的屬性。假設(shè)我們要遍歷billingAddress屬性,Cmd+Space(或者在Windows中使用Ctrl+Space組合鍵)將會(huì)觸發(fā)嵌套屬性的遍歷,這樣將會(huì)提示出嵌套的屬性并根據(jù)此時(shí)所遍歷的屬性類型提示所匹配的關(guān)鍵字(如圖2-3所示)。因此,String類型的屬性將會(huì)多一個(gè)Like的提示。圖2-3嵌套屬性和關(guān)鍵字提示為了提供一些錦上添花的特性,SpringDataSTS會(huì)將Repository作為IDE導(dǎo)航中的一等公民,使其帶有眾所周知的SpringBean標(biāo)識(shí)。除此之外,導(dǎo)航中的Spring元素(SpringElements)節(jié)點(diǎn)將會(huì)包含一個(gè)專有的SpringDataRepositories節(jié)點(diǎn),用來放置應(yīng)用程序中所配置的所有Repository,如圖2-4所示。圖2-4在STS中,具備SpringData支持的Eclipse項(xiàng)目資源管理器可以看到,你能夠快速找到Repository接口并跟蹤它實(shí)際上來源于哪一個(gè)配置元素。2.4.1IntelliIDEA最后,啟用JPA支持后,IDEA提供了Repository查找方法的補(bǔ)全功能,這種補(bǔ)全涵蓋了衍生的屬性名以及可用的關(guān)鍵字,如圖2-5所示。圖2-5在IDEA編輯器中,查詢方法的補(bǔ)全功能
第3章使用Querydsl實(shí)現(xiàn)類型安全的查詢編寫訪問數(shù)據(jù)的查詢通常會(huì)使用Java的字符串(String)來完成。對(duì)于JDBC來說,可供選擇的查詢語言就是SQL,而對(duì)于Hibernate/JPA來說就是HQL/JPQL。使用簡單的字符串來定義查詢的功能是很強(qiáng)大的,但也易于出錯(cuò),因?yàn)楹苋菀滓肫磳戝e(cuò)誤。除此之外,它與實(shí)際的查詢?cè)椿虻讓哟鎯?chǔ)很少有關(guān)聯(lián),所以列引用(在JDBC的場景下)或?qū)傩砸茫ㄔ贖QL/JPQL上下文中)在維護(hù)方面會(huì)成為一種負(fù)擔(dān),這是因?yàn)楸砘驅(qū)ο竽P偷淖兓⒉荒芎苋菀椎赜成涞讲樵冎小uerydsl項(xiàng)目()試圖解決這樣的問題,它提供了一個(gè)非常流暢的API來定義查詢。這個(gè)API衍生于實(shí)際的表或?qū)ο竽P停瑫r(shí)又是與存儲(chǔ)和模型高度無關(guān)的,所以它允許為各種存儲(chǔ)類型創(chuàng)建和使用查詢API。它目前支持JPA、Hibernate、JDO、原生的JDBC、Lucene、HibernateSearch以及MongoDB。功能的多樣性是SpringData集成Querydsl的主要原因,因?yàn)镾pringData也集成了多種類型的存儲(chǔ)。下面將會(huì)介紹Querydsl項(xiàng)目以及它的基本理念。在本書后面各種存儲(chǔ)類型相關(guān)的章節(jié)中,我們還會(huì)介紹它對(duì)這些存儲(chǔ)的支持。3.1Querydsl簡介當(dāng)使用Querydsl的時(shí)候,通常開始會(huì)從領(lǐng)域類中衍生出元模型。盡管這個(gè)類庫也可以使用簡單的字符串文本,但創(chuàng)建元模型會(huì)釋放出Querydsl的全部能量,尤其是在屬性的類型安全以及關(guān)鍵字引用方面。衍生機(jī)制基于Java6的注解處理工具(AnnotationProcessingTool,APT),它能夠掛接到編譯器中并對(duì)源碼甚至編譯后的類進(jìn)行處理。想了解更多信息的話,可參閱3.2小節(jié)“生成查詢模型”。開始之前,需要定義一個(gè)領(lǐng)域類,如示例3-1所示。我們使用幾個(gè)原始類型和非原始類型的屬性來為Customer建模。示例3-1Customer領(lǐng)域類注意我們?yōu)檫@個(gè)類使用了@QueryEntity注解。這是默認(rèn)的注解,Querydsl注解處理器將會(huì)使用它來生成相關(guān)的查詢對(duì)象。在與特定的存儲(chǔ)集成使用的時(shí)候,APT處理器能夠識(shí)別出特定存儲(chǔ)的實(shí)體(如對(duì)于JPA所使用的就是@Entity)并使用它們來衍生查詢類。在這個(gè)簡介部分,我們不會(huì)與存儲(chǔ)一起工作,所以無法使用特定存儲(chǔ)的映射注解,只是使用@QueryEntity而已。生成的Querydsl查詢類如示例3-2所示。示例3-2Querydsl生成的查詢類可以在該模塊示例工程的target/generated-sources/queries目錄下找到這些類。該類暴露了公開的Path屬性以及對(duì)其他查詢類的引用(如QEmailAddress)。在定義斷言(predicate)的時(shí)候,IDE就能在代碼補(bǔ)全時(shí)列出所有可用的Path??梢允褂眠@些Path表達(dá)式來定義可重用的斷言,如示例3-3所示。示例3-3使用查詢類來定義斷言將靜態(tài)的QCustomer.customer實(shí)例賦值給customer變量,這樣就能非常簡潔地引用它的Path屬性。可以看到,斷言的定義非常簡單整潔,而且──最重要的就是──類型安全。變更領(lǐng)域類將會(huì)重新生成查詢模型。變更所導(dǎo)致的屬性引用失效將會(huì)產(chǎn)生編譯錯(cuò)誤,從而提示我們要進(jìn)行修改的地方。對(duì)于每種Path類型所能使用的方法也會(huì)考慮到Path的類型(如like(…)方法就只能對(duì)String屬性有意義,因此只會(huì)提供給這種類型使用)。因?yàn)閿嘌缘亩x非常簡潔,所以它們能夠很容易地用在方法聲明的內(nèi)部。另一方面,我們可以很容易地以可重用的方式來定義斷言,定義原子性的斷言并通過像And或Or這樣的操作符將其連接起來以形成復(fù)雜的斷言(如示例3-4所示)。示例3-4連接原子性的斷言我們可以使用剛剛編寫的斷言來為特定的存儲(chǔ)或者簡單的集合來編寫查詢。鑒于對(duì)特定存儲(chǔ)的查詢執(zhí)行主要會(huì)通過SpringDataRepository抽象來實(shí)現(xiàn)(見3.3小節(jié)“與SpringDataRepository集成”),所以作為例子,為了簡單起見,我們會(huì)使用這個(gè)特性來對(duì)集合進(jìn)行查詢。首先,構(gòu)建各種類型的Product,這樣我們就有東西可以過濾了,如示例3-5所示。示例3-5構(gòu)建Product接下來,我們可以使用Querydsl來對(duì)這個(gè)集合建立查詢,也就是對(duì)其建立某種類型的過濾器(如示例3-6所示)。示例3-6使用Querydsl斷言過濾Product我們使用from(...)方法來建立Querydsl查詢,它是querydsl-collections模塊之中MiniAPI類的一個(gè)靜態(tài)方法。我們將Product的查詢類實(shí)例以及作為源的集合傳遞給它。現(xiàn)在,我們就可以使用where(...)方法將斷言應(yīng)用于源列表之上并使用某一個(gè)list(...)方法(如示例3-7所示)執(zhí)行查詢。在我們的場景下,只是簡單地返回那些匹配預(yù)先定義斷言的Product。將$.description傳遞給list(…)方法后,我們就能將結(jié)果投射(project)到Product的名稱上,因此返回一個(gè)String的集合。示例3-7使用Querydsl斷言過濾Product(投射)借助于Querydsl,我們能夠以一種簡潔和便利的方式來定義實(shí)體斷言。它們可以基于各種存儲(chǔ)以及Java類的映射信息而產(chǎn)生。Querydsl的API以及它所支持的存儲(chǔ)方式使得我們可以產(chǎn)生斷言,并以此來定義查詢。相同的API也可以用于對(duì)Java集合進(jìn)行過濾。3.2生成查詢?cè)P驼缭谇拔闹兴?,Querydsl的核心構(gòu)件(artifact)就是查詢?cè)P皖悺_@些類是通過注解處理工具集(APT,AnnotationProcessingToolkit,見\h/en/jsr/detail?id=175)生成的,它是Java6中javacJava編譯器的一部分。APT有一種機(jī)制,能夠以編碼的方式探查已有的Java源代碼以查找特定的注解,然后再回過頭來調(diào)用函數(shù)以生成Java代碼。Querydsl使用了這種機(jī)制來提供特定的APT處理器實(shí)現(xiàn)類,這個(gè)類可以用于探查注解。示例3-1使用了Querydsl特定的注解,如@QueryEntity以及@QueryEmbeddable。如果我們已經(jīng)擁有了某種存儲(chǔ)類型的映射領(lǐng)域類,而這種存儲(chǔ)類型是Querydsl所能夠支持的,那么生成元模型類并不需要額外的過程。在這里的核心集成點(diǎn)在于需要將注解處理器傳遞給QuerydslAPT。通常處理器會(huì)在構(gòu)建過程中執(zhí)行。3.2.1構(gòu)建系統(tǒng)集成為了與Maven集成,Querydsl提供了maven-apt-plugin插件,借助它就能夠配置實(shí)際使用的處理器類了。在示例3-8中,將process的目標(biāo)設(shè)置為generate-source階段,這樣所配置的處理器類就能探查src/main/java目錄下的類。如果想要為測試源碼包(src/test/java)中的類生成元模型類的話,需要在generate-test-sources階段執(zhí)行test-process目標(biāo)。示例3-8設(shè)置MavenAPT插件3.2.2所支持的注解處理器Querydsl自帶了各種APT處理器,以用于探查不同的注解并生成對(duì)應(yīng)的元模型類。QuerydslAnnotationProcessor非常核心的注解處理器,會(huì)探查Querydsl的特定注解,如@QueryEntity和@QueryEmbeddable。JPAAnnotationProcessor用于探查javax.persistence注解,如@Entity以及@Embeddable。HibernateAnnotationProcessor類似于JPA處理器,但是增加了對(duì)Hibernate注解的支持。JDOAnnotationProcessor探查JDO注解,如@PersistenceCapable和@EmbeddedOnly。MongoAnnotationProcessorSpringData的一個(gè)專用的處理器,會(huì)探查@Document注解。閱讀6.3小節(jié)“映射模塊”以了解更多信息。3.2.3使用Querydsl對(duì)存儲(chǔ)進(jìn)行查詢現(xiàn)在查詢類已經(jīng)就緒了,讓我們看一下如何實(shí)際使用它們構(gòu)建與特定存儲(chǔ)相關(guān)的查詢。正如前面所提到的,Querydsl為各種存儲(chǔ)都提供了集成模塊,這些模塊以優(yōu)雅且一致的API來創(chuàng)建查詢對(duì)象、通過所生成的元模型類進(jìn)行斷言定義并執(zhí)行最終的查詢。例如,JPA模塊提供了一個(gè)JPAQuery實(shí)現(xiàn)類,它會(huì)接受EntityManager并提供了API能夠?qū)崿F(xiàn)在執(zhí)行之前應(yīng)用斷言,如示例3-9所示。示例3-9使用QuerydslJPA模塊來查詢關(guān)系型存儲(chǔ)如果還記得示例3-6的話,就會(huì)發(fā)現(xiàn)這兩個(gè)代碼片段看上去區(qū)別并不大。實(shí)際上,唯一的區(qū)別在于我們采用JPAQuery作為基礎(chǔ),而前面的示例使用是集合包裝類(CollectionWrapper)。為MongoDB存儲(chǔ)實(shí)現(xiàn)相同的場景同樣也沒有太大的變化,對(duì)于這一點(diǎn)你應(yīng)該不會(huì)感到太驚訝(如示例3-10所示)。示例3-10聯(lián)合使用QuerydslMongoDB模塊以及SpringDataMongoDB3.3集成SpringDataRepository如你所見,使用Querydsl所執(zhí)行的查詢一般來說包括3個(gè)主要的步驟。1.構(gòu)建存儲(chǔ)相關(guān)的查詢實(shí)例。2.在查詢上使用一些過濾斷言。3.執(zhí)行查詢實(shí)例,可能會(huì)對(duì)其使用投射。其中的兩個(gè)步驟可以視為樣板式的,因?yàn)樗鼈兺ǔ?huì)編寫類似的代碼。另一方面,SpringDataRepository會(huì)盡可能幫助用戶減少不必要代碼的數(shù)量,因此將Repository抽象與Querydsl集成在一起是很有意義的。3.3.1執(zhí)行斷言集成的核心在于QueryDslPredicateExecutor接口,它指定了用戶可以執(zhí)行Querydsl斷言的API,類似于CrudRepository所提供的CRUD方法,如示例3-11所示。示例3-11QueryDslPredicateExecutor接口目前,SpringDataJPA和MongoDB模塊通過提供實(shí)現(xiàn)類來實(shí)現(xiàn)示例3-11中的QueryDslPredicateExecutor接口以支持這個(gè)API。為了通過Repository接口暴露這個(gè)API,需要讓其擴(kuò)展QueryDslPredicateExecutor以及Repository(或其他可用的基礎(chǔ)接口),參見示例3-12。示例3-12CustomerRepository接口擴(kuò)展了QueryDslPredicateExecutor擴(kuò)展這個(gè)接口會(huì)有兩個(gè)重要的結(jié)果:首先(可能也是最重要的)就是它將API引入了進(jìn)來并且將其暴露給CustomerRepository的客戶端。其次,SpringDataRepository基礎(chǔ)設(shè)施將會(huì)探查每個(gè)Repository接口并判斷它是否擴(kuò)展了QueryDslPredicateExecutor。如果答案是肯定的并且Querydsl位于類路徑之中,那么SpringData將會(huì)選擇一個(gè)特定的基類支撐Repository代理,它一般會(huì)通過創(chuàng)建存儲(chǔ)相關(guān)的查詢實(shí)例、綁定給定的斷言、使用分頁并最終執(zhí)行查詢來實(shí)現(xiàn)這些API方法。3.3.2手動(dòng)實(shí)現(xiàn)Repository我們剛剛所看到的方式解決了為Repository所管理的領(lǐng)域類進(jìn)行通用查詢的問題。但是,無法通過這種機(jī)制執(zhí)行更新或刪除操作,或者管理特定存儲(chǔ)的查詢實(shí)例。這種場景可以很好地參與Repository抽象的特性,可以有選擇地實(shí)現(xiàn)那些需要手動(dòng)代碼的方法(參見2.3.2小節(jié)“手動(dòng)實(shí)現(xiàn)Repository方法”了解這個(gè)話題的更多細(xì)節(jié))。為了便于實(shí)現(xiàn)自定義的Repository擴(kuò)展,我們提供了特定存儲(chǔ)的基礎(chǔ)類。要了解更多細(xì)節(jié),參見4.4.2小節(jié)“Repository與Querydsl集成”以及6.5.3小節(jié)“MongoQuerydsl集成”。
第二部分關(guān)系型數(shù)據(jù)庫
第4章JPARepositoryJava持久化API(JPA,JavaPersistenceAPI)是將Java對(duì)象持久化到關(guān)系型數(shù)據(jù)庫的標(biāo)準(zhǔn)方式。JPA包含了兩個(gè)部分:用于將類匹配到關(guān)系型表的映射模塊以及訪問對(duì)象、定義和執(zhí)行查詢等功能的EntityManagerAPI。JPA抽象了多種實(shí)現(xiàn),如Hibernate(\h)、EclipseLink(\h/eclipselink)、OpenJpa(\h)等。Spring框架一直以來就為簡化JPA的存儲(chǔ)實(shí)現(xiàn)提供了良好的支持。它所提供的支持包括構(gòu)建EntityManager的輔助類、集成Spring的事務(wù)抽象并將JPA的特定異常轉(zhuǎn)換為Spring的DataAccessException異常體系。SpringDataJPA模塊實(shí)現(xiàn)了SpringData通用的Repository抽象從而進(jìn)一步簡化了存儲(chǔ)的實(shí)現(xiàn),這樣在大多數(shù)場景無需手動(dòng)實(shí)現(xiàn)存儲(chǔ)類。要了解Repository抽象的簡介信息,可以參考第2章。本章將會(huì)帶你了解這個(gè)模塊的通用構(gòu)建過程和特性。4.1示例工程本章的示例工程包含了3個(gè)包:com.oreilly.springdata.jpa這個(gè)基礎(chǔ)包以及core和order兩個(gè)子包?;A(chǔ)包里面包含了一個(gè)Spring的JavaConfig類,它可以通過簡單的Java類而不是XML來配置Spring容器。其他的兩個(gè)包中包含了我們的領(lǐng)域類以及Repository接口。正如其名字所示,core包中包含了對(duì)領(lǐng)域模型的基礎(chǔ)抽象:像AbstractEntity這種技術(shù)化的幫助類,同時(shí)也包含了像EmailAddress、Address、Customer以及Product這樣的領(lǐng)域概念。然后,還有order包,基于這些基礎(chǔ)的概念實(shí)現(xiàn)了訂單的概念。所以在這里可以看到Order以及OrderItem。在后面的段落中,我們會(huì)仔細(xì)地查看每個(gè)類,介紹其目的以及使用JPA映射注解將它們匹配到數(shù)據(jù)庫的方式。我們領(lǐng)域模型中所有實(shí)體的基礎(chǔ)類就是AbstractEntity(如示例4-1所示)。它使用了@MappedSuperclass來表示它本身并不是受管理的實(shí)體類,而是會(huì)由其他的實(shí)體類進(jìn)行擴(kuò)展。我們?cè)谶@里聲明了一個(gè)Long類型的id并且告知持久化提供商自動(dòng)選擇最合適的策略來生成主鍵。除此之外,我們通過檢查id屬性實(shí)現(xiàn)了equals(…)和hashCode(…)方法,這樣一來,具有相同id的同種類型的實(shí)體會(huì)被視為相等。在這個(gè)類中,包含了持久化實(shí)體的主要技術(shù)化信息,因此在具體的實(shí)體類中就可以關(guān)注實(shí)際的領(lǐng)域?qū)傩?。示?-1AbstractEntity類讓我們繼續(xù)看一下非常簡單的Address領(lǐng)域類。如示例4-2所示,它是一個(gè)帶有@Entity注解的普通類,并且包含了3個(gè)String屬性。因?yàn)樗鼈兌际腔A(chǔ)屬性,因此不需要添加額外的注解,持久化提供商會(huì)自動(dòng)將它們匹配到表的列上。如果需要自定義屬性要持久化的列名,可以使用@Column注解。示例4-2Address領(lǐng)域類Address會(huì)被Customer實(shí)體所引用。Customer會(huì)包含一些其他的屬性(如原始類型的firstname和lastname)。它們?cè)谟成渖吓c我們看到的Address類似。每個(gè)Customer還會(huì)有一個(gè)電子郵件地址,這會(huì)通過EmailAddress類體現(xiàn)(如示例4-3所示)。示例4-3EmailAddress領(lǐng)域類這個(gè)類是一個(gè)值對(duì)象(valueobject,\h/node/135),這個(gè)概念是EricEvans在《模型驅(qū)動(dòng)設(shè)計(jì)》[Evans03]一書中定義的。值對(duì)象通常用來表述領(lǐng)域的概念,可以簡單地將其實(shí)現(xiàn)為原始類型(在本例中就是字符串),但是值對(duì)象還允許在其內(nèi)部實(shí)現(xiàn)領(lǐng)域約束。電子郵件地址必須要遵循一定的格式,否則,就會(huì)出現(xiàn)不合法的郵件地址。所以,我們實(shí)際上會(huì)通過一些正則表達(dá)式實(shí)現(xiàn)格式檢查,這樣就能阻止實(shí)例化非法的EmailAddress。這就意味著,在處理這種類型的實(shí)例時(shí),能夠確保有合法的電子郵件地址,所以我們不再需要專門的組件來校驗(yàn)它。在持久化映射方面,EmailAddress帶有@Embeddable注解,這會(huì)導(dǎo)致持久化供應(yīng)商會(huì)將其屬性放到包含它的類所對(duì)應(yīng)的表中。在我們的場景下,這是一個(gè)列,我們將其定義為個(gè)性化的名字:email??梢钥吹?,我們需要為JPA持久化廠商提供一個(gè)空的構(gòu)造方法,這樣它才能夠通過反射實(shí)例化EmailAddress對(duì)象(見示例4-3)。這是一個(gè)很明顯的缺點(diǎn),因?yàn)闆]有辦法讓emailAddress是final的或者斷言它是非空的。用于NoSQL實(shí)現(xiàn)的SpringData映射子系統(tǒng)并沒有將這一需求暴露給開發(fā)人員。作為示例,可以查看6.3小節(jié)“映射模塊”來了解在MongoDB中如何對(duì)更為嚴(yán)格的值對(duì)象實(shí)現(xiàn)進(jìn)行建模。示例4-4Customer領(lǐng)域類我們?cè)陔娮余]件地址上使用了@Column注解,以保證某一個(gè)電子郵件地址不能被多個(gè)客戶所使用,這樣就能根據(jù)電子郵件來唯一地查詢客戶了。最后,我們聲明Customer有一個(gè)Address的集合。這個(gè)屬性需要更多關(guān)注一下,因?yàn)槲覀冊(cè)谶@里定義了很多事情。首先,也是最基礎(chǔ)的,我們使用@OneToMany注解來指明一個(gè)Customer可以擁有多個(gè)Address。在這個(gè)注解中,我們將級(jí)聯(lián)類型(cascade)設(shè)置成了CascadeType.ALL,并為Address啟用了子對(duì)象移除(orphanremoval)。這會(huì)產(chǎn)生多種影響。例如,當(dāng)我們持久化、更新或者刪除Customer時(shí),Address也會(huì)被持久化、更新或刪除。因此,我們不再需要在前端持久化Address實(shí)例了并且當(dāng)刪除Customer時(shí),也不用再關(guān)心刪除所有的Address了。持久化廠商會(huì)做到這一點(diǎn)。需要注意的一點(diǎn)是,這并不是數(shù)據(jù)庫級(jí)別的級(jí)聯(lián),而是你的JPA持久化廠商所管理的級(jí)聯(lián)。除此之外,將子對(duì)象移除設(shè)置為true,那么如果Address在集合中被移除了,它們也會(huì)在數(shù)據(jù)庫中被刪除。以上所有的結(jié)果將會(huì)導(dǎo)致Address的生命周期被Customer所控制,這種關(guān)系是一種經(jīng)典的組合,在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(domain-drivendesign)術(shù)語中,Customer會(huì)被成為聚合根(aggregateroot,\h/wiki/Domain-drivendesign#Buildingblocks_of_DDD),因?yàn)樗粌H為自己也為其他實(shí)體控制持久化操作以及約束。最后,我們?cè)赼ddresses屬性上使用@JoinColumn,這會(huì)導(dǎo)致持久化廠商為Address對(duì)象后端所對(duì)應(yīng)的表添加另外一列。這一列會(huì)用來引用Customer,從而實(shí)現(xiàn)表關(guān)聯(lián)。如果我們遺漏了這個(gè)注解的話,持久化廠商將會(huì)創(chuàng)建一個(gè)專門的連接表。在包c(diǎn)ore中,最后一部分內(nèi)容是Product,如示例4-5所示。就像前面討論的其他類一樣,它包含了多種基本屬性,所以無需添加注解就能使持久化廠商對(duì)它們產(chǎn)生映射。我們只是在定義name和price屬性時(shí),使用@Column注解,其目的是將其定義為強(qiáng)制性的屬性。除此之外,還添加了一個(gè)Map,它會(huì)用來存儲(chǔ)額外的屬性,不同的產(chǎn)品之間這些屬性可能是不同的。示例4-5Product領(lǐng)域類對(duì)于構(gòu)建基本的客戶關(guān)系管理(CustomerRelationManagement,CRM)或庫存系統(tǒng)而言,我們已經(jīng)萬事俱備了。接下來,為了實(shí)現(xiàn)系統(tǒng)中Product的訂單,我們需要進(jìn)行一些抽象。首先,我們引入了LineItem,它會(huì)持有對(duì)Product的引用以及Product的數(shù)量和購買的價(jià)格。匹配Product屬性時(shí),我們使用了@ManyToOne注解,它實(shí)際上會(huì)轉(zhuǎn)化成LineItem對(duì)應(yīng)表中的product_id列,這一列會(huì)用來指向Product,如示例4-6所示。示例4-6LineItem領(lǐng)域類完成這個(gè)“拼圖游戲”的最后一塊就是Order實(shí)體了,它基本上就是一個(gè)指針,指向了Customer、投遞地址、賬單地址以及表示實(shí)際購買的多個(gè)LineItem(如示例4-7所示)。LineItem的映射與前面看到的Customer和Address之間映射類似。Order會(huì)自動(dòng)對(duì)LineItem實(shí)例進(jìn)行級(jí)聯(lián)持久化操作。因此,沒有必要單獨(dú)管理LineItem的持久化生命周期。其他的屬性都是已經(jīng)討論過的多對(duì)一關(guān)系。要注意的是,我們?yōu)镺rder定義了一個(gè)個(gè)性化的表名,因?yàn)樵诖蠖鄶?shù)的數(shù)據(jù)庫中,Order本身就是一個(gè)保留字,因此,所生成的建表SQL以及為查詢和數(shù)據(jù)操作生成的SQL在執(zhí)行時(shí)都會(huì)導(dǎo)致異常產(chǎn)生。示例4-7Order領(lǐng)域類最后一個(gè)值得注意的方面是Order類的構(gòu)造器使用了投遞地址和賬單地址的副本。這能夠保證方法傳遞進(jìn)來的Address實(shí)例如果發(fā)生變化的話,不會(huì)關(guān)聯(lián)影響到已經(jīng)存在的訂單。如果我們不創(chuàng)建這個(gè)副本的話,如果顧客稍后修改他的地址信息,那使用這個(gè)地址創(chuàng)建的訂單中對(duì)應(yīng)的地址也會(huì)隨之發(fā)生變化。4.2傳統(tǒng)方式在開始之前,首先看一下SpringData如何為領(lǐng)域模型實(shí)現(xiàn)數(shù)據(jù)訪問層,并討論如何按照傳統(tǒng)的方式實(shí)現(xiàn)數(shù)據(jù)訪問層。在示例項(xiàng)目中會(huì)找到示例的實(shí)現(xiàn)和客戶端都使用了一些額外的注解如@Profile(對(duì)于實(shí)現(xiàn))和@ActiveProfile(對(duì)于測試用例)。因?yàn)镾pringDataRepository方式會(huì)創(chuàng)建一個(gè)CustomerRepository實(shí)例,并且我們還有一個(gè)為手動(dòng)實(shí)現(xiàn)所創(chuàng)建的實(shí)例。因此,這里使用了Spring的Profile機(jī)制,只對(duì)單一的測試用例啟動(dòng)傳統(tǒng)的實(shí)現(xiàn)方式。我們并沒有在這里的實(shí)例代碼中展示這些注解,因?yàn)槿绻凑諅鹘y(tǒng)的方式來實(shí)現(xiàn)整個(gè)數(shù)據(jù)訪問層的話,它們實(shí)際上并不會(huì)被用到。為了使用普通的JPA來持久化前述的實(shí)體,現(xiàn)在要?jiǎng)?chuàng)建一個(gè)接口以及針對(duì)我們存儲(chǔ)的實(shí)現(xiàn),如示例4-8所示。示例4-8為Customer定義的存儲(chǔ)接口這樣,我們定義了一個(gè)save(…)方法來存儲(chǔ)賬號(hào)以及根據(jù)電子郵件地址查找所有賬號(hào)的查詢方法?,F(xiàn)在看一下如果基于普通JPA實(shí)現(xiàn)的話,存儲(chǔ)的實(shí)現(xiàn)類會(huì)是什么樣子,如示例4-9所示。示例4-9對(duì)于Customer的傳統(tǒng)存儲(chǔ)實(shí)現(xiàn)實(shí)現(xiàn)類中使用了JPA的EntityManager,因?yàn)樵O(shè)置了JPA的@PersistenceContext注解,因此它將會(huì)通過Spring容器注入進(jìn)來。這個(gè)類設(shè)置了@Repository注解,因此會(huì)將JPA的異常轉(zhuǎn)換為Spring的DataAccessException異常體系。除此之外,我們使用了@Transactional注解,從而保證save(...)操作運(yùn)行在事務(wù)之中,并且還允許為findByEmailAddress(...)設(shè)置readOnly標(biāo)志(在類級(jí)別)。這有助于在持久化廠商內(nèi)部以及數(shù)據(jù)庫級(jí)別對(duì)性能進(jìn)行優(yōu)化。因?yàn)槲覀儾幌胱尶蛻舳藖頉Q定該調(diào)用EntityManager的merge(...)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度臨時(shí)用工工作滿意度調(diào)查及改進(jìn)協(xié)議4篇
- 二零二五年度宿舍安全管理宿管員聘用協(xié)議范本3篇
- 二零二五年度ISO 22000食品安全管理體系認(rèn)證咨詢協(xié)議3篇
- 二零二五年度商業(yè)地產(chǎn)項(xiàng)目配套場地租賃服務(wù)協(xié)議2篇
- 二零二五年度外資企業(yè)外籍員工聘用協(xié)議范本3篇
- 2025年度文化旅游項(xiàng)目募集資金三方監(jiān)管合同4篇
- 2025年度豬圈建造與生物安全防護(hù)合同4篇
- 2025年度生物制藥研發(fā)合作協(xié)議
- 二零二五年度城市綠化用地承包合同范本4篇
- 2025年智能車輛識(shí)別一體機(jī)銷售與服務(wù)合同范本4篇
- 纖維增強(qiáng)復(fù)合材料 單向增強(qiáng)材料Ⅰ型-Ⅱ 型混合層間斷裂韌性的測定 編制說明
- 習(xí)近平法治思想概論教學(xué)課件緒論
- 寵物會(huì)展策劃設(shè)計(jì)方案
- 孤殘兒童護(hù)理員(四級(jí))試題
- 梁湘潤《子平基礎(chǔ)概要》簡體版
- 醫(yī)院急診醫(yī)學(xué)小講課課件:急診呼吸衰竭的處理
- 腸梗阻導(dǎo)管在臨床中的使用及護(hù)理課件
- 調(diào)料廠工作管理制度
- 小學(xué)英語單詞匯總大全打印
- 衛(wèi)生健康系統(tǒng)安全生產(chǎn)隱患全面排查
- GB/T 15114-2023鋁合金壓鑄件
評(píng)論
0/150
提交評(píng)論