介紹
Hap框架的通用Mapper實(shí)現(xiàn)了對單表的增刪改查,是在開源通用mapper(github地址)的基礎(chǔ)上涂屁,添加了對多語言支持等功能,以適應(yīng)Hap開發(fā)的需要心俗。眾所周知嘱函,MyBatis除了通過XML配置,還可以通過java注解來配置列另,通用mapper正是利用了MyBatis的SelectProvider芽腾、DeleteProvider等注解實(shí)現(xiàn)。
使用方法
自定義的mapper通過繼承通用mapper页衙,這樣mapper就實(shí)現(xiàn)了通用mapper接口所用方法的復(fù)用摊滔,其中必須指定通用Mapper的泛型,這樣就可以在service的實(shí)現(xiàn)類通過自定義mapper調(diào)用通用mapper接口中的所有方法店乐,自定義mapper如下所示:
public interface CustomMapper extends Mapper<DTO> {
...
}
原理剖析
通用mapper接口通過多層的接口繼承實(shí)現(xiàn)了方法的復(fù)用艰躺,其繼承鏈如下圖所示:
最頂層的Mapper接口均包含一個與接口對應(yīng)的方法,下面具體結(jié)合SelectByPrimaryMapper的selectByPrimaryKey方法分析通用mapper的實(shí)現(xiàn)原理眨八,SelectProvider注解實(shí)現(xiàn)機(jī)制暫不予剖析描滔,僅從通用mapper使用該注解的層次予以分析。
public interface SelectByPrimaryKeyMapper<T> {
@SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
T selectByPrimaryKey(Object key);
}
在selectByPrimaryKey方法上運(yùn)用了SelectProvider注解,Mybatis官方文檔對其解釋如下:
這些可選的 SQL 注解(指SelectProvider, DeleteProvider, UpdateProvider, InsertProvider)允許你指定一個 類名和一個方法在執(zhí)行時來返回運(yùn)行允許創(chuàng)建動態(tài) 的 SQL踪古『ぃ基于執(zhí)行的映射語句, MyBatis 會實(shí)例化這個類,然后執(zhí)行由 provider 指定的方法.type 屬性是類。method 屬性是方法名伏穆。 引用自 Mybatis文檔
在示例中拘泞,selectByPrimaryKey方法可以理解為由BaseSelectProvider類中的method屬性對應(yīng)的方法實(shí)現(xiàn)(在本例中methond屬性指定的“dynamicSQL”,實(shí)際對應(yīng)BaseSelectMapper的抽象父類MapperTemplate的dynamicSQL方法,返回“dynamicSQL”字符串)枕扫,查看BaseSelectProvider源碼可見陪腌,
所有方法均返回拼接好的SQL字符串。
以下代碼截取自BaseSelectProvider.java:
// BaseSelectProvider.java
....
public String selectByPrimaryKey(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
boolean isMl = EntityHelper.getEntityTable(entityClass).isSupportMultiLanguage();
setResultType(ms, entityClass);
StringBuilder sql = new StringBuilder();
if (isMl) {
sql.append(SqlHelper.selectAllColumns_TL(entityClass));
sql.append(SqlHelper.fromTable_TL(entityClass, tableName(entityClass)));
sql.append(SqlHelper.wherePKColumns_TL(entityClass));
} else {
sql.append(SqlHelper.selectAllColumns(entityClass));
sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));
sql.append(SqlHelper.wherePKColumns(entityClass));
}
return sql.toString();
}
...
這個方法接收一個MappedStatement對象(對應(yīng)xml映射文件的一個<select/>或<insert/>等節(jié)點(diǎn))烟瞧。
getEntityClass方法返回了節(jié)點(diǎn)對應(yīng)的DTO(通過自定義mapper的泛型獲仁肌)的類類型。
然后借助EntityHelper的getEntityTable方法獲取DTO @Table的值,即DTO對應(yīng)的表的信息的類参滴,獲取該表是否支持多語言信息(多語言信息的設(shè)置通過檢查DTO是否有@MutibleLanguage注解實(shí)現(xiàn))强岸。
通過條件語句根據(jù)是否支持多語言返回拼接的不同字符串,為簡化分析砾赔,以不支持多語言的表分析蝌箍。
第一步,創(chuàng)建了一個名為sql的StringBuilder類用來拼接字符串用于返回暴心。
第二步妓盲,拼接字符串。拼接的sql字符串由三個部分組成专普,這三個部分均通過SqlHelper的方法返回悯衬,返回類型均為字符串。分別為:
- 字段檀夹。SqlHelper.selectAllColumns(entityClass)
- 表筋粗。SqlHelper.fromTable(entityClass, this.tableName(entityClass))
- 限定條件。SqlHelper.whereAllIfColumns(entityClass, this.isNotEmpty())
// SqlHelper.java
...
public static String getAllColumns(Class<?> entityClass) {
Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
StringBuilder sql = new StringBuilder();
Iterator var3 = columnList.iterator();
while(var3.hasNext()) {
EntityColumn entityColumn = (EntityColumn)var3.next();
sql.append(entityColumn.getColumn()).append(",");
}
// 返回字符串形式如下"column1,column2,column3 ..."
return sql.substring(0, sql.length() - 1);
}
public static String selectAllColumns(Class<?> entityClass) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ");
sql.append(getAllColumns(entityClass));
sql.append(" ");
// 返回字符串形式如下 "select column1, solumn2, column3"
return sql.toString();
}
...
以獲取字段為例击胜,主要通過EntityHelper的getColumns方法獲取所有字段的集合亏狰,然后遍歷集合,返回所有的字段拼接的字符串表示偶摔,最后與"select"暇唾、""拼接到一起并返回。
第三步辰斋,返回拼接好的select語句策州。
總結(jié)
通用Mapper的實(shí)現(xiàn)主要通過MyBatis的Provider注解,本質(zhì)上是一個通過泛型和注解動態(tài)獲取表的信息宫仗,拼接sql字符串的過程够挂。