BeetlSql 有一個BaseMapper,提供了不少內置的Dao操做,如增刪改查等10幾個方法,用戶只須要些一個類繼承此接口便能很快的完成一個Dao,好比程序員
public interface UserDao extends BaseMapper<User>{ }
UserDao沒有包含任何方法,但集成了BaseMapper,所以具有了不少內置的Dao操做。若是這些操做不知足要求,可使用BeetlSql的MD文檔來維護複雜的SQL,好比,一個根據角色查詢全部用戶的操做sql
@SqlResource("common.user") public interface UserDao extends BaseMapper<User>{ List<User> getUsersByRole(String role); }
註解@SqlResource註明了sql文件的位置,位於common/user.md 文件,其中user.md 包含了sql查詢app
getUsersByRole === select u.* from user,user_role,role where ....
這是一個BeetlSql的基本概念,在這裏不詳細說明,有興趣的能夠參考官網文檔 如今有這樣一個需求,BaseMapper包含了太多的內置方法,有時候程序員對其中的某些方法並不敢興趣,有的內置方法則是不容許調用的,好比deleteById 方法,老是有一次物理刪除操做。應該在企業應用系統裏禁止這種調用,那麼,BeetlSql可否定製BaseMapper呢?ide
答案是能定製。只須要新寫一個接口,經過SQLManager註冊爲BaseMapper便可,而後,爲接口的每一個方法添加實現便可,實際上,自帶的BaseMapper就是這樣實現的。工具
以下是一個自定義的BaseMapper,爲了簡單起見,Dao只提供三個接口ui
public interface MyBaseMapper<T> { public T single(Object key); public T single(Object key,List attrs); public List<T> allData(); }
第一個方法single和第三個all方法都是原有的BaseMapper自帶,第二個方法是根據主鍵獲取對象,但只獲取部分屬性。咱們將在博客最後講述如何實現,如今,咱們要實現allData方法。this
allData方法返回目標對象對應的數據全部值,這在原有的BaseMapper已經實現,是經過org.beetl.sql.core.mapper.internal.AllAmi 實現,所以咱們只須要複用這個實現。code
首先,告訴SQLManager,我要添加一個新的BaseMapper對象
SQLManager sql = .... MapperConfig config = sql.setBaseMapper(MyBaseMapper.class);
其次,添加allData實現繼承
config.getBuilder().addAmi("allData", new AllAmi());
這樣,就很是容易的定義了一個allData內置的Dao方法,使用了已經實現的AllAmi方法,AllAmi方法也很是簡單,直接使用SQLManager.all方法實現,其源碼以下
public class AllAmi implements MapperInvoke { @Override public Object call(SQLManager sm, Class entityClass, String sqlId, Method m, Object[] args) { if (args == null || args.length == 0) { return sm.all(entityClass); } return sm.all(entityClass, (Integer) args[0], (Integer) args[1]); } }
這樣,你就具有初步自定義Dao的功能,好比以下代碼
MyUserMapper dao = sql.getMapper(MyUserMapper.class); List<User> users =dao.allData();
如今,面臨新的挑戰,實現一個根據主鍵查詢,但僅僅返回須要的字段,也就是實現以下方法
public T single(Object key,List attrs);
這比較複雜,由於在BeetlSQL裏並未內置,須要咱們本身完成後註冊,完成思路很簡單,生成相似以下sql語句,並交給SQLManager 執行便可,好比
select id,name where id = #id#
咱們寫一個新的擴展,叫作SingleAmiExt(參考了自帶的SingleAmi),其腳手架以下
@Override public Object call(SQLManager sm, Class entityClass, String sqlId, Method m, Object[] args) { if (args.length == 1) { return sm.single(entityClass, args[0]); } List attrs = (List) args[1]; String sql = getSingleSelect(entityClass,sm,attrs); Map paras = this.setIdsParas(sm, args[0], entityClass); List list = sm.execute(sql, entityClass, paras); if(list.size()==0) { return null; }else { return list.get(0); } }
這段代碼前倆行先判斷參數,若是隻有一個參數,則認爲沒有屬性過濾,直接調用內置的single方法便可。不然,須要本身拼寫一個Sql模板,經過SQLManager.execute 來獲取
getSingleSelect方法用於生成目標sql模板,代碼以下
private String getSingleSelect(Class cls, SQLManager sm, List attrs) { NameConversion nameConversion = sm.getNc(); String condition = appendIdCondition(sm, cls); StringBuilder sb = new StringBuilder("select "); for (Object o : attrs) { String attr = (String) o; String col = nameConversion.getColName(cls, attr); sb.append(col).append(" ,"); } // 去掉最後一逗號 sb.setLength(sb.length() - 1); sb.append(" from ").append(nameConversion.getTableName(cls)).append(condition); return sb.toString(); } /* 參考了AbstractDBStyle的內置代碼生成辦法 */ protected String appendIdCondition(SQLManager sm, Class<?> cls) { AbstractDBStyle style = (AbstractDBStyle) sm.getDbStyle(); MetadataManager metadataManager = sm.getMetaDataManager(); NameConversion nameConversion = sm.getNc(); String tableName = nameConversion.getTableName(cls); StringBuilder condition = new StringBuilder(" where "); TableDesc table = metadataManager.getTable(tableName); ClassDesc classDesc = table.getClassDesc(cls, nameConversion); List<String> colIds = classDesc.getIdCols(); List<String> propertieIds = classDesc.getIdAttrs(); Iterator<String> colIt = colIds.iterator(); Iterator<String> propertieIt = propertieIds.iterator(); if (colIt.hasNext() && propertieIt.hasNext()) { String colId = colIt.next(); String properId = propertieIt.next(); condition.append(style.getKeyWordHandler().getCol(colId)).append(" = ").append(style.HOLDER_START) .append(properId).append(style.HOLDER_END); while (colIt.hasNext() && propertieIt.hasNext()) { colId = colIt.next(); properId = propertieIt.next(); condition.append(" and ").append(style.getKeyWordHandler().getCol(colId)).append(" = ") .append(style.HOLDER_START).append(properId).append(style.HOLDER_END); } } return condition.toString(); }
代碼複雜是由於考慮到通用性,尤爲是符合主鍵這裏,若是你的表都是單一主鍵,或者主鍵都叫ID,那代碼就只有幾行
一樣的setIdsParas也由於複合主鍵緣由,也較爲複雜,內容以下
private Map setIdsParas(SQLManager sm, Object key, Class entityClass) { AbstractDBStyle style = (AbstractDBStyle) sm.getDbStyle(); MetadataManager metadataManager = sm.getMetaDataManager(); NameConversion nameConversion = sm.getNc(); String tableName = nameConversion.getTableName(entityClass); TableDesc table = metadataManager.getTable(tableName); ClassDesc desc = table.getClassDesc(entityClass, nameConversion); Map paras = new HashMap(); List<String> idAttrs = desc.getIdAttrs(); if (idAttrs.size() == 1) { paras.put(idAttrs.get(0), key); } else { // 來自對象id的屬性.複合主鍵 Map<String, Object> map = desc.getIdMethods(); for (int i = 0; i < idAttrs.size(); i++) { String idCol = idAttrs.get(i); String idAttr = idAttrs.get(i); Method m = (Method) map.get(idAttr); try { Object os = m.invoke(key, new Object[0]); paras.put(idAttr, os); } catch (Exception ex) { throw new BeetlSQLException(BeetlSQLException.ID_VALUE_ERROR, "沒法設置複合主鍵:" + idCol, ex); } } } return paras; }
完成上訴代碼後,能夠添加這個實現
config.getBuilder().addAmi("single", new SingleAmiExt());
而後,可使用了,好比按照主鍵查詢,只關心id和name
dao.single(1,Arrays.asList("id","name"));
結束語: BaseMapper大部分擴展都較爲簡單,這裏的SingleAmiExt複雜是考慮到複合主鍵,且如今的BeetlSQL版本還沒法重用內置的代碼生成片斷致使的,叫來BeetlSQL版本會把內置代碼生成獨立出來,能夠供第三方工具使用,這樣,開發者就能輕易擴展BeetlSQL了。
關於BeetlSQL,請參考官網ibeetl.com