Mybatis系列(六)介面式程式設計
Mybatis系列之介面式程式設計
引言
在前面的文章《Mybatis系列之簡單示例》曾有一段程式碼涉及到了介面式程式設計,當時並沒有展開闡述,今天我們單獨把這一段拿出來表一表。
在講Mybatis介面式程式設計之前,我們先回憶一下前面是如何呼叫對映檔案中的SQL程式碼的。通常情況下,都是使用SqlSession例項的selectXXX(selectOne, selectList, selectMap)方法來執行對映檔案中相應的SQL語句的,這些方法都有一個共同的特徵,那就是第一個引數都是String型別的,我們需要使用這個引數明確告之Mybatis我們是需要執行對映檔案的哪一個元素下的SQL語句,所以這個引數內容應該是對映檔案的名稱空間加上相應元素的id值,如:
Object obj = session.selectOne("com.emerson.learning.mapping.user.getByID", 240);
User user = (User)obj;
... ...
這條語句告訴我們,要在名稱空間com.emerson.learning.mapping.user下查詢一個id為getById的元素,並執行其SQL語句。在獲取返回值之後,我們還需要對其進行強型別轉換。
這裡存在一些潛在的問題:
- 為了確保名稱空間的唯一性,通常會使用相對較長的、且有一定含義的字串來作為其值,這樣就很難保證我們在程式碼不出現拼寫錯誤的情況,即使是直接從對映檔案拷貝過來的,也存在不經意間被修改的可能性;
- 從selectXXX方法的簽名可以看到,她的第二個引數是Object型別,那麼如果我們傳入的引數型別與對映檔案中由parameterType屬性指定的型別不一致時,將會出現不可預知的錯誤
- 同樣,selectXXX方法返回值使用了泛型,我們須確保用於接收其返回值的變數型別與對映檔案中屬性resultType指定的型別相一致
Mybatis規避上述風險的手段,我們稱之為介面式程式設計,也就是我們今天的主題。
定義代理介面
介面式程式設計,我們可以簡單的理解為Mybatis為對映檔案定義了一個代理介面,以後全部通過這個介面來和對映檔案互動,而不再是使用以前方法。
對映檔案如何知道自己被哪個介面代理呢?這裡就是通過名稱空間來實現的,對映檔案的名稱空間再也不是隨心所欲的定義的了,而是要使用代理介面的全限定名作為其名稱空間。所謂全限定名,就是介面所在的包名加上介面名稱。
<mapper namespace="com.emerson.learning.dao.ICommunicatorDao"> <select id="getById" parameterType="int" resultType="Communicator"> select * from communicator where communicator_id=#{id} </select> <select id="getAll" resultType="Communicator"> select * from communicator order by communicator_name </select> </mapper>
介面定義好了,那麼如何將對映檔案中的select / insert / update / delete 等元素與代理介面中的方法繫結呢?其實很簡單,只需要在代理介面中定義一些方法,並以相應元素的id屬性值做為方法名,parameterType屬性值做為方法引數型別,屬性resultType值做為方法的返回值即可。下面定義的兩個方法就分別對應上面對映檔案中的兩個select元素。
public interface ICommunicatorDao {
public Communicator getById(int id);
public List<Communicator> getAll();
}
有些朋友會問了,介面定義好了,是不是還要再定義一個實現類呢?答案是否定的。Mybatis會使用動態代理機制來幫助我們完成額外的工作,我們需要做的就是把這個介面註冊到Mybatis中。在Mybatis的總配置檔案中,加入如下語句。
<mappers> <mapper class="com.emerson.learning.dao.ICommunicatorDao" /> </mappers>
這樣,我們就可以在Java程式碼中直接呼叫我們定義的代理介面中的方法了。
@Test
public void testGetById() {
SqlSession session = sqlSessionFactory.openSession();
try {
ICommunicatorDao cp = session.getMapper(ICommunicatorDao.class);
Communicator c = cp.getById(1);
if (null == c) {
System.out.println("the result is null.");
} else {
System.out.println(c);
}
} finally {
session.close();
}
}
@Test
public void testGetAll() {
SqlSession session = sqlSessionFactory.openSession();
try {
ICommunicatorDao cp = session.getMapper(ICommunicatorDao.class);
List<Communicator> list = cp.getAll();
for (Communicator c : list) {
System.out.println(c);
}
} finally {
session.close();
}
}
是不是很簡單,而且程式碼看上去比之前的要簡潔了許多。
介面式程式設計與之前的呼叫方式相比較,有以下優點:
- 呼叫方法明確,因為我們呼叫的是介面中的某個具體方法,而不再是通過一個字串來指定執行對映檔案中的某個SQL語句了
- 傳入引數和返回值都不再是Object了,這樣就可以在程式碼編寫階段確保傳入的引數型別是正確的,也不再需要對返回值進行強型別轉換了
- 最主要的一點,就是將來Mybatis遇到了Spring,更能發揮出介面式程式設計的強大潛力。
使用註解
在Mybatis3.0之後,加入了更強的註解功能。如果不需要使用到較複雜的SQL語句,可以直接把對映檔案省去,直接在Java程式碼中使用註解的方式指定SQL語句。這種寫法很簡潔,但卻失去了對映檔案的靈活性。
public interface ICommunicatorDao {
@Select("SELECT * FROM communicator WHERE communicator_id=#{id}")
public Communicator getById(@Param(value = "id") int id);
@Select("SELECT * FROM communicator ORDER BY communicator_id")
public List<Communicator> getAll();
}