模仿與學習MyBatis - 1.6 註解與Session:sql語句

本文收錄於模仿與學習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類返回,而不是簡單的打在屏幕上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章