博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tk.mapper 原理解读
阅读量:6620 次
发布时间:2019-06-25

本文共 9597 字,大约阅读时间需要 31 分钟。

hot3.png

一个简单的类TK.MAPPER实现

mybatis支持@Insert与@InsertProvider注解。这两个注解的实现如下:

入口

void parseStatement(Method method) {    Class
parameterTypeClass = getParameterType(method); LanguageDriver languageDriver = getLanguageDriver(method); //解析出SQL SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver); if (sqlSource != null) { Options options = method.getAnnotation(Options.class); final String mappedStatementId = type.getName() + "." + method.getName(); Integer fetchSize = null; Integer timeout = null; StatementType statementType = StatementType.PREPARED; ResultSetType resultSetType = ResultSetType.FORWARD_ONLY; SqlCommandType sqlCommandType = getSqlCommandType(method); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = !isSelect; boolean useCache = isSelect;
private SqlSource getSqlSourceFromAnnotations(Method method, Class
parameterType, LanguageDriver languageDriver) { try { Class
sqlAnnotationType = getSqlAnnotationType(method); Class
sqlProviderAnnotationType = getSqlProviderAnnotationType(method); if (sqlAnnotationType != null) { if (sqlProviderAnnotationType != null) { throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName()); } //解析@Insert Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType); final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation); return buildSqlSourceFromStrings(strings, parameterType, languageDriver); } else if (sqlProviderAnnotationType != null) { //解析@InsertProvider Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType); return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method); } return null; } catch (Exception e) { throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e); } }

通过代码发现@Insert解析,类似于XML文件形式,相当于将XML文件的类容写到JAVA里面。@InsertProvider是在执行SQL语句的时候,把参数给你,由你自己来解析出SQL语句。相对于 第二种更灵活一些。 看看是怎么调用你写的方法的:

private SqlSource createSqlSource(Object parameterObject) {    try {      int bindParameterCount = providerMethodParameterTypes.length - (providerContext == null ? 0 : 1);      String sql;      if (providerMethodParameterTypes.length == 0) {        sql = (String) providerMethod.invoke(providerType.newInstance());      } else if (bindParameterCount == 0) {        sql = (String) providerMethod.invoke(providerType.newInstance(), providerContext);      } else if (bindParameterCount == 1 &&              (parameterObject == null || providerMethodParameterTypes[(providerContextIndex == null || providerContextIndex == 1) ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {        sql = (String) providerMethod.invoke(providerType.newInstance(), extractProviderMethodArguments(parameterObject));      } else if (parameterObject instanceof Map) {        @SuppressWarnings("unchecked")        Map
params = (Map
) parameterObject; sql = (String) providerMethod.invoke(providerType.newInstance(), extractProviderMethodArguments(params, providerMethodArgumentNames)); } else { throw new BuilderException("Error invoking SqlProvider method (" + providerType.getName() + "." + providerMethod.getName() + "). Cannot invoke a method that holds " + (bindParameterCount == 1 ? "named argument(@Param)": "multiple arguments") + " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object."); } Class
parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); return sqlSourceParser.parse(replacePlaceholder(sql), parameterType, new HashMap
()); } catch (BuilderException e) { throw e; } catch (Exception e) { throw new BuilderException("Error invoking SqlProvider method (" + providerType.getName() + "." + providerMethod.getName() + "). Cause: " + e, e); } }

一个例子:

public interface MyMapper {    @InsertProvider(type=MyMapperImpl.class,method = "insertSelect")    public void insertSelect(String s1, ChannelOrders channelOrders);}

实现

public class MyMapperImpl {    public String insertSelect(ProviderContext pc,Object agrs){        Class clazz = pc.getMapperType();        System.out.println("xxxx");        return "select 1";    }}

这是一个简单的例子,但也是tk.mapper的原理。

tk.mapper

TK是在初始化完毕的时候,替换掉原有的SqlSource.

public void setSqlSource(MappedStatement ms) throws Exception {        if (this.mapperClass == getMapperClass(ms.getId())) {            throw new MapperException("请不要配置或扫描通用Mapper接口类:" + this.mapperClass);        }        Method method = methodMap.get(getMethodName(ms));        try {            //第一种,直接操作ms,不需要返回值            if (method.getReturnType() == Void.TYPE) {                method.invoke(this, ms);            }            //第二种,返回SqlNode            else if (SqlNode.class.isAssignableFrom(method.getReturnType())) {                SqlNode sqlNode = (SqlNode) method.invoke(this, ms);                DynamicSqlSource dynamicSqlSource = new DynamicSqlSource(ms.getConfiguration(), sqlNode);                setSqlSource(ms, dynamicSqlSource);            }            //第三种,返回xml形式的sql字符串            else if (String.class.equals(method.getReturnType())) {                String xmlSql = (String) method.invoke(this, ms);                SqlSource sqlSource = createSqlSource(ms, xmlSql);                //替换原有的SqlSource                setSqlSource(ms, sqlSource);            } else {                throw new MapperException("自定义Mapper方法返回类型错误,可选的返回类型为void,SqlNode,String三种!");            }        } catch (IllegalAccessException e) {            throw new MapperException(e);        } catch (InvocationTargetException e) {            throw new MapperException(e.getTargetException() != null ? e.getTargetException() : e);        }    }

下面我们看一下InsertSelect的生成原理

public String insertSelective(MappedStatement ms) {        Class
entityClass = getEntityClass(ms); StringBuilder sql = new StringBuilder(); //获取全部列 Set
columnList = EntityHelper.getColumns(entityClass); //Identity列只能有一个 Boolean hasIdentityKey = false; //先处理cache或bind节点 for (EntityColumn column : columnList) { if (!column.isInsertable()) { continue; } if (StringUtil.isNotEmpty(column.getSequenceName())) { //sql.append(column.getColumn() + ","); } else if (column.isIdentity()) { //这种情况下,如果原先的字段有值,需要先缓存起来,否则就一定会使用自动增长 //这是一个bind节点 sql.append(SqlHelper.getBindCache(column)); //如果是Identity列,就需要插入selectKey //如果已经存在Identity列,抛出异常 if (hasIdentityKey) { //jdbc类型只需要添加一次 if (column.getGenerator() != null && column.getGenerator().equals("JDBC")) { continue; } throw new MapperException(ms.getId() + "对应的实体类" + entityClass.getCanonicalName() + "中包含多个MySql的自动增长列,最多只能有一个!"); } //插入selectKey SelectKeyHelper.newSelectKeyMappedStatement(ms, column, entityClass, isBEFORE(), getIDENTITY(column)); hasIdentityKey = true; } else if (column.isUuid()) { //uuid的情况,直接插入bind节点 sql.append(SqlHelper.getBindValue(column, getUUID())); } } sql.append(SqlHelper.insertIntoTable(entityClass, tableName(entityClass))); sql.append("
"); for (EntityColumn column : columnList) { if (!column.isInsertable()) { continue; } if (StringUtil.isNotEmpty(column.getSequenceName()) || column.isIdentity() || column.isUuid()) { sql.append(column.getColumn() + ","); } else { sql.append(SqlHelper.getIfNotNull(column, column.getColumn() + ",", isNotEmpty())); } } sql.append("
"); sql.append("
"); for (EntityColumn column : columnList) { if (!column.isInsertable()) { continue; } //优先使用传入的属性值,当原属性property!=null时,用原属性 //自增的情况下,如果默认有值,就会备份到property_cache中,所以这里需要先判断备份的值是否存在 if (column.isIdentity()) { sql.append(SqlHelper.getIfCacheNotNull(column, column.getColumnHolder(null, "_cache", ","))); } else { //其他情况值仍然存在原property中 sql.append(SqlHelper.getIfNotNull(column, column.getColumnHolder(null, null, ","), isNotEmpty())); } //当属性为null时,如果存在主键策略,会自动获取值,如果不存在,则使用null //序列的情况 if (StringUtil.isNotEmpty(column.getSequenceName())) { sql.append(SqlHelper.getIfIsNull(column, getSeqNextVal(column) + " ,", isNotEmpty())); } else if (column.isIdentity()) { sql.append(SqlHelper.getIfCacheIsNull(column, column.getColumnHolder() + ",")); } else if (column.isUuid()) { sql.append(SqlHelper.getIfIsNull(column, column.getColumnHolder(null, "_bind", ","), isNotEmpty())); } } sql.append("
"); return sql.toString(); }

可以看到tk.mapper生成的SQL语句和XML一样。

转载于:https://my.oschina.net/u/3217171/blog/3042406

你可能感兴趣的文章
FUNCS.H中的函数声明
查看>>
让织梦CMS的后台编辑器支持优酷视频
查看>>
Python语言中round函数的一个疑惑
查看>>
Spring事务隔离级别,事务传播行为
查看>>
CRC是什么?
查看>>
每次看完羽毛球赛
查看>>
第一章练习题
查看>>
三、JVM垃圾回收1(如何寻找垃圾?)
查看>>
RabbitMQ-从基础到实战(2)— 防止消息丢失
查看>>
【译】ExtJS 4.1会带来什么
查看>>
重要的话
查看>>
mysql多个TimeStamp设置(转)
查看>>
php中的占位符
查看>>
BSS段 data段 text段 堆heap 和 栈stack
查看>>
数据库创建好之后如何创建scott用户
查看>>
EBS销售订单挑库发放处理程序
查看>>
动态大小的图片上的超链接
查看>>
总结一下常用的排序,冒泡排序,选择排序,快速排序
查看>>
Sql Server系列:系统函数
查看>>
php5.5 yum源
查看>>