-
沒實現類的接口,為什么能調用定義的方法查看全部
-
Sprinf 1.Db層會消失, 2.Dao層會消失查看全部
-
接口的方式,規范了如何去訪問配置文件查看全部
-
通過接口調用,就不會出現那么多問題了 1.xml設置了全線類名,不會重復 2.調用的方法和參數也不會弄錯了查看全部
-
一行代碼
查看全部 -
攔截器實現分頁
實際開發中,會有很多查詢列表頁面需要分頁功能,這時分頁的簡單實現就不再適用,這時就需要把分頁功能的共通代碼分離出來,以供其他需要分頁的地方使用。
【1】頁面的jsp和js方法的封裝(完成跳轉到后臺)
【2】sql的xml文件的sql語句進行封裝(因為要傳入Limit的參數,這里定義了兩條幾乎同樣的sql語句,只不過是一條通過count(*)返回所有記錄數,這里需要把查詢所有sql語句封裝)
【3】配置文件sql語句后的Limit和起始位置和返回數量的封裝
【4】頁面可以通過自定義標簽來解決,這樣每個需要分頁的地方直接引入該標簽即可。
原始的JDBC中Dao層的封裝:
開發一個共通的方法,得到Connection對象,得到查詢的sql語句,該共通方法可以根據sql語句查詢出總條數(該sql語句無需截斷再拼接count(*),這樣容易出錯??梢园言搒ql語句當作子查詢,外面再嵌套一層sql語句),這樣Page的屬性就都有值了,就可以再Dao的sql語句后拼接Limit參數了。
Mybatis中Dao層的封裝:
該封裝的關鍵是sql語句在配置文件里,配置文件貌似獲取不到sql參數。
Mybatis攔截器:Mybatis攔截器提供為所有頁面實現分頁的功能,對攔截到的都會實現分頁功能(實現原理:就是攔截查詢的sql語句并把它修改為分頁查詢的sql語句)。
在沒有使用攔截器時,在執行sql之前攔截,并調用分頁共通,修改的是Mybatis的源碼。所以提供了攔截器,在不改變Mybatis源碼的同時,可以修改源碼的行為。
Mybatis執行sql語句簡化版的過程:它的原理和JDBC相似,根據sql語句得到PreparedStatement對象,為該對象setObject,再執行execute方法,最后得到結果,Mybatis攔截器就是相當于在傳入sql語句獲得PreparedStatement對象前,修改它為分頁的sql語句。
查看全部 -
分頁的簡單實現
重點關注分頁功能中Mybatis部分的內容(觀察Mybatis的介入和之前的分頁有何不同)
該分頁功能的參數:
1、記錄總數。
2、每頁顯示記錄數。
3、總頁數。
4、當前頁。
5、分頁查詢Limit的起始點。
6、分頁查詢Limit的返回個數。
通常封裝一個類來實現分頁功能:
Mysql使用Limit進行分頁。Limit關鍵字需要兩個參數,一個是從第幾條開始取,另一個是取多少條。
該類中還封裝了計算總頁數的方法,并且可以計算出Limit需要的兩個參數值,然后放到該類對象里供程序使用,如下圖1,如果傳進來的當前頁數大于總頁數,就把當前頁數置為最后一頁(也可以在前端頁面進行設置,這里都在后端實現)如圖2,還需保證當前頁數不能小于1,如果小于1,則把它置為1。如圖3,最后還需要計算sql語句Limit需要的參數,如圖4。
執行邏輯之前可以先查看Limit語句,第一頁是從第0條開始獲取,取5條。第二頁是從第5條開始取,取5條,以此類推。
分頁查詢過程:
預先定義好當前頁面記錄數和當前頁面數,通過查詢出來的記錄數,可以計算出總頁數,Limit的參數也就獲取到了,這樣Dao層就將Page對象交給XML,XML根據這兩個參數查詢,然后將查詢的結果返回。
Mybatis如果想傳入XML兩個參數時,可以使用Map集合。
分頁查詢一般來說是按照某一字段進行排序,而且該字段值最好不要重復,可以是主鍵或者該字段創建的時間createTime,如果不主動設置排序方式,按照數據庫默認排序方式,并不能保證每次執行sql語句的默認排序方式都是相同的。
前端的javascript的校驗相當于沒有校驗,所以如果考慮安全的問題時,在后端也需要加上校驗。
查看全部 -
接口式編程原理(中)
SqlSession接口有兩個實現類,分別為DefaultSqlSession和SqlSessionManager,這里獲取的對象是屬于DefaultSqlSession實現類的。
首先sqlSession是我們手動封裝獲取的,這里調用的是Mybatis的方法,然后通過SqlSessionFactory對象的OpernSession方法獲取SqlSession,其中SqlSessionFactory接口也有兩個實現類,分別為DefaultSqlSessionFactory和SqlSessionManager,由源碼得知SqlSessionFactoryBuilder類提供了build(Reader reader),該方法返回的是SqlSessionFactoryBuilder類提供的build(Reader reader,String envirment,Properties properties),該方法又返回的是SqlSessionFactoryBuilder提供的build(Configuration config)方法,該方法最后返回的是DefaultSqlSessionFactory對象。所以SqlSessionFactory獲取的是DefaultSqlSessionFactory實現類對象。所以openSession方法是屬于DefaultSqlSessionFactory里的方法。該openSession方法返回的是DefaultSqlSessionFactory提供的openSessionFromDataSource(ExecutorType execType,..),該方法最終返回的是DefaultSqlSession類的對象
所以最終獲取的是DefaultSqlSession。
這里我們可以查看DefaultSqlSession的getMapper方法,該方法返回的是Configuration類提供的getMapper方法,該方法第一個參數就是傳進來的class,也就是接口的類類型,第二個參數是this,也就是方法的對象,也就是sqlSession,該方法返回的是MapperRegistry類提供的getMapper方法,該方法返回值可以理解為通過代理工廠,生產一個代理返回出去(這里就是前面說的通過動態代理創建實例)。
該動態工廠的由來是Map對象(以類類型作為Key,MapperProxyFactory作為Value)的get方法,該get方法中傳入一個類類型,MapperRegistry類提供了addMapper方法,該方法需要傳入一個類類型,然后用Map對象的input方法把傳入的類類型作為key,臨時創建一個動態工廠作為value,并傳入這個類類型,只有調用了這個方法才會對Map集合初始化,該方法的調用是在加載Mybatis核心配置文件時進行調用的,它會不停的調用這個方法,為不同的Class創建代理工廠。
動態工廠創建代理對象是通過MapperProxyFactory的new Instance(SqlSession sqlSession)方法,該方法中創建了代理類,該代理類的構造函數中第一個參數是傳進來的sqlSession,后兩個參數是代理工廠的屬性,第二個參數就是接口的類類型,構造函數并沒有為其他屬性賦值,所以第三個參數只是Map初始化的一個對象。
查看全部 -
接口式編程原理
講解接口式原理之前需了解如下幾個問題
問題1:沒有實現類的接口為甚么可以實現方法的功能(答案:是通過動態代理。簡單的動態代理過程,是要有一個實現InvocationHandler接口的類,這里給這個類起名為MapperProxy,因為Mybatis源碼中就是起的這個名稱,這個類必須實現invoke()方法。然后用這個類通過Proxy.newProxyInstance(類加載器,接口,MapperProxy對象)創建一個代理實例,在案例中是通過sqlSession.getMapper()獲取到代理實例的,表面上看是使用接口承接的,但該對象不是實現類的對象,而是一個代理實例,然后是通過這個代理實例調用接口里的方法,由動態代理知識,它并不會執行這個方法,而是會觸發MapperProxy的invoke()方法,這樣沒有實現類的接口方法就可以執行了)。
問題2:雖然調用接口方法時,走的是invoke()方法,怎么知道調用的sql語句是什么樣子的呢?(因為調用的sql語句在配置文件中,在獲取SqlSession對象之前,由于調用加載Mybatis核心配置文件的方法,Mybatis配置文件中引入了sql配置文件的路徑,這樣加載Mybatis總配置文件的同時,也把其他配置文件加載了,這些信息Mybatis會存儲到對象(Configuration)中,當代理實例調用接口方法時,如果該接口方法與配置信息能對應上(配置文件的namespace等于包名+接口名,方法名等于id名,接口參數等于parameterType,返回值類型等于resultType),就可以成功調用,根據上節課知識,這里是存在某種聯系的——>接口的全名稱就是配置文件的namespace,調用的方法名就是定義sql標簽的id,接口的全名稱和調用的方法名在invoke()方法中是可以獲取到的,有了這些信息就可以獲取到配置文件信息,然后再invoke方法里就可以代替原來寫的方法)
問題3:根據動態代理的知識,Proxy.newProxyInstance()返回的是Object類型的對象,但卻賦值給接口類型的對象?
(答案:這里是泛型的作用,當使用getMapper()傳入的是什么樣的類類型,就可以使用什么樣的類型去接值,這就是Mybatis利用泛型進行強轉了)。
查看全部 -
接口式編程(針對調用配置文件sql語句這行代碼,有四處值得分析的地方)
1、namespace :方法里的namespace需要和mapper標簽的namespace一致,由于兩邊是手寫的,可能存在不一致的風險,而且也不能保證多個sql配置文件,namespace不沖突。
?2、與sql關聯的id :方法里的id和標簽的id也是手寫的,也存在不一致的風險。
?3、傳入的參數 :例如selectList傳入的參數類型是Object,所以傳入的參數不管是何種類型,它不會報錯,但是如果傳入的類型和parameterType類型不匹配,sql語句中引用的參數就會出錯。
?4、返回值:例如selectList方法,Mybatis提供了一種約束,只要是List即可,無論集合中存儲何種類型,但是這樣不意味執行就是對的,因為真正存入的是resultMap約束的,編譯時不會報錯,執行時可能就會報錯。
SqlSession方法的mapper的namespace、與sql關聯的id、傳入的參數、返回值。
接口式編程:就是為了避免上述風險,而人為做的強制性規范和約束,Mybatis提供的這種方式,就稱作接口式編程(相當于sql的配置文件有一個java接口作為代言人,這樣SqlSession對象直接調用接口里的方法即可。但是該接口有一些前提:
1、namespace的統一:該接口的包名+接口名就是namespace名。
2、sql標簽id的統一:代言人代言一條sql語句提供給外面,代言哪條sql語句,就提供和該sql的id相同名稱方法。該接口可以代言sql配置文件的sql語句,通過提供方法,方法名為id名。
3、方法參數的統一:接口方法參數類型為parameterType類型。
4、方法返回值的統一:接口方法返回值類型為resultMap類型。
注意:如上該接口就可以代言sql語句了。該接口不用人為手動編寫實現類,通過Mybatis獲取該接口,就可以調用,Mybatis已經實現了該接口(它就會知道該方法是調用配置文件中的哪條sql語句)。因為當前該接口沒有實現類,這里通過SqlSession對象的getMapper(接口的類類型)就可以獲得該接口實現類的代理對象,這樣就可以直接調用接口里的方法,而不用傳入namespace+id,而是按照接口的約束進行調用)。
這樣就避免了上述的問題:namespace不可能相同,因為不可能有多個相同名全路徑的接口,調用方法時也不需要傳入namespace。同一個接口里也不肯有多個相同名稱的方法。傳入的參數已經被限制,如果不符合編譯會報錯。返回的接口也被限定,如果不符合編譯也會報錯。
接口式編程的作用:
1、規范訪問配置文件2、當mybatis和spring整合后,配置的數據源將交給Spring管理,也就意味著認為手寫的提供SqlSession會消失,Spring將提供SqlSesssion,傳入的參數應該交給Service處理好再傳入進來。通過SqlSession調用接口式編程的這些代碼統統都由Spring來實現。這個Dao層將會消失,該接口將會變成真正的Dao層,這時Dao層將只剩下接口文件和配置文件。
當Mybatis與Spring結合時,這些Spring提供了便捷,這里只做一些了解即可,整合之后整個Dao層只剩接口文件和配置文件。查看全部 -
invoke方法的第一個參數proxy即拿到的代理實例
查看全部 -
getMapper返回的對象實際上是一個Proxy.newProxyInstance返回的代理實例
查看全部 -
MyBatis會加載mapper配置文件的namespace為一個Class<T>類型實例
查看全部 -
使用SqlSession的getMapper方法來直接獲取Dao接口的實現類
查看全部 -
1、在sql的配置文件中,parameterType是如何被加載出現的,
首先是加載總configuration.xml,將其轉換為Reader流,通過Reader流轉換為Document,使用jdk中的dom來解析其中的xml標簽,使用XPath來讀取xml中信息。
在逐步解析mappers,mapper,resource,慢慢解析到parameterType,其中這個parameterType不是固定的寫法需要類名.包名這種,此屬性無須擔心大小寫,簡寫map,int[]等什么的都是可以的,源碼中會將輸入的字符串都轉為小寫,而且對于簡寫也有相應的匹配項。
查看全部
舉報