beetlsql對外暴露最重要的類是SQLManager,SQLmanager裏有很多實用的方法。
但是Sqlmnager並不是真正流程的執行者,而是SQLScript這個類。這個類裏面都是執行細節。
我們這次只挑出其select行爲的最終方法List<T> select(Class<T> clazz, Map<String, Object> paras, RowMapper<T> mapper)
如下所示:
public <T> List<T> select(Class<T> clazz, Map<String, Object> paras, RowMapper<T> mapper) {
//檢查是否modele類上是否有帶有@Builder的註解,如果有的話,執行一下before
checkAnnotatonBeforeSelect(clazz, paras);
//運行sql模板,獲取實際的sql語句
SQLResult result = run(paras);
String sql = result.jdbcSql;
List<SQLParameter> objs = result.jdbcPara;
ResultSet rs = null;
PreparedStatement ps = null;
List<T> resultList = null;
//執行Interceptor的before
InterceptorContext ctx = this.callInterceptorAsBefore(this.id, sql, false, objs, paras);
//如果Interceptor的before有返回值,那麼就直接返回,然後執行Interceptor的after
if (ctx.getResult() != null) {
this.callInterceptorAsAfter(ctx, ctx.getResult());
return (List<T>) ctx.getResult();
}
//再次獲取參數,因爲有可能被改變
sql = ctx.getSql();
objs = ctx.getParas();
Connection conn = null;
try {
conn = sm.getDs().getConn(id, false, sql, objs);
ps = conn.prepareStatement(sql);
this.setPreparedStatementPara(ps, objs);
rs = ps.executeQuery();
//檢查是否有RowMapper,如果有自定義RowMapper,那麼按照Rowmapper進行分行解析
if (mapper != null) {
BeanProcessor beanProcessor = this.getBeanProcessor();
resultList = new RowMapperResultSetExt<T>(mapper, beanProcessor).handleResultSet(this.id,rs, clazz);
} else {
//按照正常解析
resultList = mappingSelect(rs, clazz);
}
//執行Interceptor的after
this.callInterceptorAsAfter(ctx, resultList);
//檢查是否modele類上是否有帶有@Builder的註解,如果有的話,執行一下after
resultList = this.checkAnnotatonAfterSelect(clazz, resultList, result);
//sql 腳本里通過listener 實現最後處理,這個目前只有orm.single等
if (this.getListener() != null) {
for (SQLResultListener listener : getListener()) {
listener.dataSelectd(resultList,paras,this.sm,result);
}
}
return resultList;
} catch (SQLException e) {
this.callInterceptorAsException(ctx, e);
throw new BeetlSQLException(BeetlSQLException.SQL_EXCEPTION, e);
} finally {
clean(false, conn, ps, rs);
}
}
通過這段代碼,可以瞭解到beetlsql的select流程如下:
- Builder.before
- Interceptor.before
- query數據庫
- 解析statement數據,有Rowmapper按Rowmapper,沒有按mappingSelect(rs, clazz);
- Interceptor.before
- Builder.after
- Listener
那麼在一般情況下,我們沒有新建builder/interceptor/mapper/listener
所以只會執行第4條的mappingSelect
之所以要看這個結構,主要還是對於一些高級應用,要看插入自定義的一些組件的時機。
對一些組件的解釋:
builder:
官方有個@Builder的註解,該註解是針對自定義註解的。如果你寫一個註解,上面再蓋上@Builder,然後將這個註解放到model類上或者model的屬性上就可以被檢索到。
當然@Builder裏是有值的,比如ObjectSelectBuilder,這是針對查詢的接口,你還需要新建個子類實現它。
具體的還是深入上面代碼的第一行checkAnnotatonBeforeSelect裏有個cache.init()裏就瞭解了。
interceptor:
這是個攔截器,有查詢的前置攔截和後置攔截。用處我沒有仔細看,但可以更改sql語句,比如,你可以寫個攔截器,思路如下:
根據方法名,如果帶有ByPage的後綴且xx的參數,那麼就加上limit xx.......
Rowmapper:
這個是行解析器。注意,是行解析器。
也就是說,你查詢表,查詢到了10行數據,這個會for循環10行,拋出每一行留給你自己去自定義封裝。
其實這個有點過度設計了。一點用沒有。
作者實際上應該拋出所有數據讓用戶自定義封裝。
listener:
目前都是用於@orm.single等orm
使用了線程的數據傳遞holder。當然肯定可以後續擴展。
接下來看第4條的mappingSelect方法。
@SuppressWarnings("unchecked")
public <T> List<T> mappingSelect(ResultSet rs, Class<T> clazz) throws SQLException {
List<T> resultList = null;
BeanProcessor beanProcessor = this.getBeanProcessor();
//類型判斷需要做性能優化
if (Map.class.isAssignableFrom(clazz)) {
// 如果是Map的子類或者父類,返回List<Map<String,Object>>
resultList = new ArrayList<T>();
while (rs.next()) {
Map map = beanProcessor.toMap(this.sqlSource.getId(), clazz, rs);
resultList.add((T) map);
}
return resultList;
} else if (isBaseDataType(clazz)) {
resultList = new ArrayList<T>(1);
while (rs.next()) {
Object result = beanProcessor.toBaseType(this.sqlSource.getId(), clazz, rs);
resultList.add((T) result);
}
} else {
resultList = beanProcessor.toBeanList(this.sqlSource.getId(), rs, clazz);
return resultList;
}
return resultList;
}
這段代碼簡單的說是這樣的,
取出默認的BeanProcessor,對封裝結果的類型進行檢查。
如果是map,則走 beanProcessor.toMap
如果是基本數據類型如Int,boolean,String等,則走beanProcessor.toBaseType
其他的就走 beanProcessor.toBeanList方法。
很明顯,對於絕大部分查詢都會走beanProcessor.toBeanList方法的。
這是個很有意思的事情,BeanProcessor是可以自定義的,通過SQLManager.getProcessors得到了一個HashMap<sqlid,BeanProcessor>,你可以在初始化的時候塞入一個自定義的BeanProcessor截斷源碼的結果集解析。