Mybatis中selectKey源码分析

刚回答了一个问题这样一个问题,mybatis不能正常返回主键增加值  下面通过源码分析一下selectKey都具体实现;关于Mybatis 基于注解Mapper源码分析 可以看一下具体解析过程。

 @Insert("insert into table2 (name) values(#{name})")
    @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
    int insertTable2(Name name);

  

KeyGenerator接口

/**
 * key生成器接口
 */
public interface KeyGenerator {
  //在执行主SQL前执行selectKey
  void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

  //在主SQL执行后执行selectkey
  void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

}

before具体执行时机  

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
//  //执行before
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

  

  protected void generateKeys(Object parameter) {
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    ErrorContext.instance().store();
    keyGenerator.processBefore(executor, mappedStatement, null, parameter);
    ErrorContext.instance().recall();
  }

  

  @Override
  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    //是否执行前执行,如果不是就不执行
    if (executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  after执行时机

@Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    //如果executeBefore配置为false则执行
    if (!executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  

具体执行源码

 private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
      //如果parameter不为null同时keyStatement不为null且keyStatement 指定了keyProperties
      if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
        //获取keyProperties
        String[] keyProperties = keyStatement.getKeyProperties();
        //获取配置信息
        final Configuration configuration = ms.getConfiguration();
        //获取参数对象元数据
        final MetaObject metaParam = configuration.newMetaObject(parameter);
        //其实已经判断过了
        if (keyProperties != null) {
          //新建keyExecutor
          Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
          //执行查询
          List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
          //如果查询结果为0个则抛出异常
          if (values.size() == 0) {
            throw new ExecutorException("SelectKey returned no data.");
          } else if (values.size() > 1) {//查询的结果个数多余1个则抛出异常
            throw new ExecutorException("SelectKey returned more than one value.");
          } else {//只返了一个结果值
            MetaObject metaResult = configuration.newMetaObject(values.get(0));
            //如果keyProperty个数只有1个
            if (keyProperties.length == 1) {
              //如果查询结果对象存在这个属性的getter方法
              if (metaResult.hasGetter(keyProperties[0])) {
                //将属性值设置到param中
                setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
              } else {
                //如果没有getter方法就将当前值设置到属性中
                setValue(metaParam, keyProperties[0], values.get(0));
              }
            } else {//处理指定多个key属性场景
              handleMultipleProperties(keyProperties, metaParam, metaResult);
            }
          }
        }
      }
    } catch (ExecutorException e) {
      throw e;
    } catch (Exception e) {
      throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
    }
  }

  private void handleMultipleProperties(String[] keyProperties,
      MetaObject metaParam, MetaObject metaResult) {
    //获取所有key  column
    String[] keyColumns = keyStatement.getKeyColumns();
      //如果key column不存在
    if (keyColumns == null || keyColumns.length == 0) {
      //没有指定key column则直接使用配置到 key property
      for (String keyProperty : keyProperties) {
        setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
      }
    } else {
      //存在key column 但是数量不一致
      if (keyColumns.length != keyProperties.length) {
        throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
      }
      //数量一致,要求keyColumn 和keyProperty一一对应
      for (int i = 0; i < keyProperties.length; i++) {
        setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
      }
    }
  }

  

原文地址:https://www.cnblogs.com/wei-zw/p/8921507.html

时间: 2024-11-09 16:30:28

Mybatis中selectKey源码分析的相关文章

Java中ArrayList源码分析

一.简介 ArrayList是一个数组队列,相当于动态数组.每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保证容量能容纳所有数据. 1.1.ArrayList 的继承与实现接口 ArrayList继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口. public class  ArrayList<E> ex

Java中HashMap源码分析

一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtable大致相同)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Collections.sync

Spark中决策树源码分析

1.Example 使用Spark MLlib中决策树分类器API,训练出一个决策树模型,使用Python开发. """ Decision Tree Classification Example. """from __future__ import print_functionfrom pyspark import SparkContextfrom pyspark.mllib.tree import DecisionTree, DecisionT

深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)

events模块对外提供了一个 EventEmitter 对象,即:events.EventEmitter. EventEmitter 是NodeJS的核心模块events中的类,用于对NodeJS中的事件进行统一管理,使用events可以对特定的API事件进行添加,触发和移除等.我们可以通过 require('events')来访问该模块. 比如如下代码: // 引入 events 模块 const events = require('events'); console.log(events)

MyBatis架构与源码分析&lt;资料收集&gt;

1.架构与源码分析 :https://www.cnblogs.com/luoxn28/p/6417892.html .https://www.cnblogs.com/wangdaijun/p/5296830.html 2.执行器篇:https://blog.csdn.net/qingtian211/article/details/81838042 3.插件篇:https://www.cnblogs.com/xrq730/p/6984982.html 4.缓存篇:https://www.cnblo

mybatis 学习四 源码分析 mybatis如何执行的一条sql

总体三部分,创建sessionfactory,创建session,执行sql获取结果 1,创建sessionfactory 这里其实主要做的事情就是将xml的所有配置信息转换成一个Configuration对象,然后用这个对象组装成factory返回. //mybatis配置文件 String resource = "conf.xml"; InputStream is = TestMybatis.class.getClassLoader().getResourceAsStream(re

浏览器中的源码分析功能

前言:我们一直在用浏览器,你是否真正玩过他?NO! 在IE浏览器中,打开一个网页,右击菜单栏中有一个查看源代码功能,不过这个源代码太长了,不易分析. 之后的浏览器增加了一项功能,叫做审查元素功能. 在谷歌浏览器中,打开一个网页,右击菜单栏中有一项是检查:在搜狗浏览器中,打开一个网页,右击菜单栏有一项是审查元素:其他浏览器笔者未尝试过. 一.审查元素简介 以最常用的百度搜索界面为例,打开审查元素,如下图所示: 右侧出现审查元素界面,也就是网页的Html代码元素 其中包括以下模块 Elements(

JDK1.8 HashMap中put源码分析

一.存储结构      在JDK1.8之前,HashMap采用桶+链表实现,本质就是采用数组+单向链表组合型的数据结构.它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置.HashMap通过key的hashCode来计算hash值,不同的hash值就存在数组中不同的位置,当多个元素的hash值相同时(所谓hash冲突),就采用链表将它们串联起来(链表解决冲突),放置在该hash值所对应的数组位置上.结构图如下:     图中,紫色部分代表哈希表,也称为哈希数组,数组中每个元素

Mybatis 中sqlsession源码解析

一.sqlsession获取过程 1.基础配置 在mybatis框架下进行的数据库操作都需要首先获取sqlsession,在mybatis与spring集成后获取sqlsession需要用到sqlsessionTemplate这个类. 首先在spring对sqlsessionTemplate进行配置,使用到的是 org.mybatis.spring.SqlSessionTemplate 这个类. <!-- SqlSession实例 --> <bean id="sessionTe