我們都知道,在開發中只要按照一定的規範去配置的話,我們的mybatis是不需要寫mapper介面的實現類的,那麼mybatis到底是如何實現了讓我們可以不用寫mapper的實體類的呢?這裡寫了一下自己的看法,歡迎各位大神指導。 ...
原始dao層開發
在我們用mybatis開發了第一個小程式後,相信大家對於dao層的開發其實已經有了一個大概的思路了。其他的配置不用變,將原來的test方法,該為dao的方法,將原來的返回值,直接在dao層進行一下接收就可以了。依然是老一套,先是大框架,然後寫配置文件及UserMapper.xml文件這一系列的操作。如果不明白的,請參考本人博客《入門第一個程式》。
我們需要做的就是,首先建立一個會話工廠(SqlSessionFactory),然後用會話工廠創建會話(SqlSession)。然後通過讀取配置文件得到sql語句,執行,然後返回數據給dao層的對象。具體操作如下
首先,在原來的基礎上創建一個dao層的介面。
然後寫一個dao層介面的實現類
1 public class UserDaoImpl implements UserDao { 2 3 // 需要向dao實現類中註入SqlSessionFactory 4 // 這裡通過構造方法註入 5 private SqlSessionFactory sqlSessionFactory; 6 7 public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { 8 this.sqlSessionFactory = sqlSessionFactory; 9 } 10 11 @Override 12 public User findUserById(int id) throws Exception { 13 SqlSession sqlSession = sqlSessionFactory.openSession(); 14 15 User user = sqlSession.selectOne("test.findUserById", id); 16 17 // 釋放資源 18 sqlSession.close(); 19 20 return user; 21 22 } 23 24 @Override 25 public void insertUser(User user) throws Exception { 26 SqlSession sqlSession = sqlSessionFactory.openSession(); 27 28 //執行插入操作 29 sqlSession.insert("test.insertUser", user); 30 31 // 提交事務 32 sqlSession.commit(); 33 34 // 釋放資源 35 sqlSession.close(); 36 37 } 38 39 @Override 40 public void deleteUser(int id) throws Exception { 41 SqlSession sqlSession = sqlSessionFactory.openSession(); 42 43 //執行插入操作 44 sqlSession.delete("test.deleteUser", id); 45 46 // 提交事務 47 sqlSession.commit(); 48 49 // 釋放資源 50 sqlSession.close(); 51 52 } 53 54 }
然後就是對我們寫的代碼的測試
這樣開發的話有一些問題:
1.dao介面的實現類中存在大量的重覆代碼,這些代碼會增加程式員的工作量。
2.在實現類中,SqlSession的方法在調用映射文件中的sql語句時將statement的id(其中的"test.findUserById"便是statment的id)硬編碼了。
3.調用SqlSession方法時傳入的變數,由於sqlsession方法使用泛型,即使變數類型傳入的不是Dao方法中的參數類型,在編譯的時候也不會報錯,不利於程式的開發。
為瞭解決這些問題,我們通過滿足一些規範就將實體層的實現類的開發省去,由mybatis框架提供的代理實現類替我們去動態的調用我們的映射文件,然後去執行類中的方法。
mapper代理方法的原理
這之前,先聲明一下,使用mapper代理的時候,命名和之前的不太一樣。這裡的介面命名由原來的** 改為**Mapper,比如說原來的User介面,現改為UserMapper。其內容和本質沒有變,只是換了一個命名規範。除了介面外映射文件也是由User.xml改為UserMapper.xml其內容不變。
mapper代理方法的思路是,程式員接著寫持久層的介面。
和對應每個介面的配置文件。
在我們的原來的方法中需要再寫一個介面的實現類,用來構建會話工廠(SqlSessionFactory)和對應的會話(SqlSession)然後通過會話去讀取映射文件中的sql語句(這裡不准確,不過可以這麼理解)。那麼我麽就可以在這個實體類上做點文章了:
1.我們可以創建一個代理類,在我們相應的介面被調用的時候,代理類就會創建這個對象,創建會話工廠和會話並不難。
2.還有就是我們的sqlSession對象調用的方法名稱,這個很好理解,對應的是我們映射文件的標簽。
3.那麼接下來就是傳入參數和接收返回值的問題了:
3.1 傳入參數:我們總共需要傳入兩個參數,一個是映射文件的statement的id的字元串,另外一個是執行需要的參數。 比較困難的是,這個字元串的生成問題。我們的每個方法到底對應的是哪個映射文件的statement的id。對於這個問題,mybatis給出了我們的解決方法:我們的實體層對象讀到的字元串是這樣的:test.insertUser
可以將這個id分為兩部分,其中的test是我們的映射文件中的namespace的值,這個值讓它和持久層介面的地址一致,這樣就使得代理對象可以通過介面的地址動態的生成id的前半部分,也就是test的這一部分;而後半部分則是讓映射文件中的Statement的id和介面的方法一致。
也就是說,用戶在調用介面的某一個方法的時候,mybatis可以根據介面地址和對應的方法名生成sqlsession對象的方法傳入的字元串。這樣就解決了傳入參數中的字元串的問題了。
還有就是第二個問題:我們的傳入具體參數,mybatis規定,傳入參數類型必須和介面的傳入參數類型一致。這樣,我們的傳入參數類型就搞定了。
3.2傳入參數搞定了以後,返回值也就搞定了,跟傳入參數的規定一樣,返回值的類型要求和介面返回值一致,這樣就可以在代理類中將返回值傳出了。
這些搞定了,我們就可以使用代理對象代替我們的實體層具體實現類了。
至此,這些問題就差不多都解決了,不過還有一個問題:我們的select方法返回值問題,代理對象到底是用selectont方法去查詢資料庫呢還是使用selectlist方法呢?這個跟我們的介面(mapper)的返回值類型有關——如果mapper方法返回單個pojo對象(非集合對象),代理對象內部通過selectOne查詢資料庫;如果mapper方法返回集合對象,代理對象內部通過selectList查詢資料庫。
mapper代理方法
原理講完了,使用其實就基本上沒啥要說的了。
首先我們要創建一個介面。
然後寫對應介面的xml文件(截取部分,namespace處就不截圖了)。
最後別忘了在SqlMapConfig.xml文件中載入UserMapper.xml。
測試,這裡有必要說一下,我們的代理對象是通過SqlSession.getMapper(介面位元組碼文件)獲取的。:
至此,我們的mybatis的實體層的實體層實現類就可以省略不寫了。不過這樣做的壞處是加強了代碼的耦合性,每個介面和每個映射文件必須對應。不過還是感覺通過遵守一定的規範而使得代碼量大大減少更爽呢。