本文收錄於模仿與學習MyBatis系列
簡述
在上一篇中,實現了一個MapperProxy類,代入接口會生成一個mapper,通過它來調用方法,將輸出begin #方法名 end
這樣的字串。
而在本篇中將討論的是,什麼是註解,MapperProxy如何與註解搭配起來,回調session中的方法。即要達到如下圖中的效果,最後分析一下這樣設計的原因。
最終結果是一個Java Maven項目,代碼存在github上。
註解與解析
首先需要新建一個annotation包,加一個SQL類,代碼如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SQL {
String value();
}
@interface表示這是一個註解,@Target裏可以指定它能被注在哪個地方,選項有METHOD \ TYPE \ FIELD等等,此處選擇注在METHOD(方法)上。@Retention中可以選擇存活的時期,選擇RUNTIME(運行中)。然後具體使用註解的方式如下:
public interface ArticleMapper {
// 正規寫法是@SQL(value="xxxx"),若只有value屬性可略去
@SQL("select * from article where id = {0}")
void findById(int id);
}
這樣,當我們使用時,直接從method中可以取到此註解的值。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
SQL sql = method.getAnnotation(SQL.class);
System.out.println(sql.value());
return null;
}
參數代入與回調Session
上面的代碼,正常的輸出了註解上的select from article where id = {0}
,下一步就是想着如何將{0}這種數字替換爲參數中的第0個(int id)了。以下先寫一段比較簡陋的代碼,用正則表達式找到sql語句中的所有{xxx}格式的字串,替換爲參數,勉強實現一下功能:
public class MapperProxy<T> implements InvocationHandler {
private Session session;
@SuppressWarnings("unchecked")
public static <T> T newInstance(Class<T> clazz, Session session)
{
MapperProxy<T> proxy = new MapperProxy<>();
proxy.session = session;
// 動態代理
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, proxy);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
SQL anno = method.getAnnotation(SQL.class);
if (anno == null) {
return null;
}
String sql = anno.value();
Pattern p = Pattern.compile("\\{[^\\}]+\\}");
Matcher m = p.matcher(sql);
if (m.find()) {
StringBuffer sb = new StringBuffer();
do {
//獲取{param},用BeanWrapper處理後的返回值來替換
String s = m.group();
int index = Integer.valueOf(s.substring(1, s.length() - 1));
m.appendReplacement(sb, args[index].toString());
} while (m.find());
sql = m.appendTail(sb).toString();
}
System.out.println(sql);
session.exec(sql);
return null;
}
}
注意改動了構造函數,需要代入session,因爲在執行時我們希望回調session了。而session中也新增一個接口:
@Override
public <T> T getMapper(Class<T> clazz) {
return MapperProxy.newInstance(clazz, this);
}
以上代碼雖然有點繞,但是已經完整的,把SessionFactory->Session->Mapper->invoke的這個流程走通了:
public static void main(String args[]) throws Exception {
SessionFactory factory = new VSessionFactory("config.xml");
Session session = factory.openSession();
ArticleMapper mapper = session.getMapper(ArticleMapper.class);
mapper.findById(1);
}
輸出結果爲:
select * from article where id = 1
--------------------print--------------------
id=1
title=歡迎來到自由茶社
brief=這裏是大門的入口
content=這裏是大門的入口
father_id=0
author_name=me
create_time=2016-11-16 23:44:36.0
modify_time=2016-12-06 00:41:07.0
結果正確,目前流程走通,已經可以通過接口與註解,實現sql語句查詢了。後續將繼續完善,將sql查詢到的值轉爲Java類返回,而不是簡單的打在屏幕上。