mybatis SelectKey解析

1.selectKey介绍及作用

resultType:sql返回的java类型

statementType:STATEMENT|PREPARED|CALLABLE三种默认PREPARED

keyProperty:列名对应的java属性名,可逗号分隔

keyColumn:列名,可逗号分隔

order:BEFORE|AFTER,BEFORE表示

databaseId:数据库Id一般不需要填

mybatis的

2.selectKey测试及解析

测试代码

#mapper
int insert(UserDO userDO);

#mapper.xml
  <insert id="insert">
     <selectkey keyproperty="userId" keycolumn="user_id" order="AFTER" resulttype="integer">
       select last_insert_id()
     </selectkey>
     insert into user(username, password, nickname)
     values(#{username}, #{password}, #{nickname})
  </insert>

#java&#x6D4B;&#x8BD5;&#x4EE3;&#x7801;
public class Test {

  public static void main(String[] args) throws IOException {

    try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
      // &#x6784;&#x5EFA;session&#x5DE5;&#x5382; DefaultSqlSessionFactory
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession sqlSession = sqlSessionFactory.openSession();
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      UserDO userDO = new UserDO();
      userDO.setUsername("monian");
      userDO.setPassword("123");
      userDO.setNickname("monianx");
      userMapper.insert(userDO);
      System.out.println("&#x81EA;&#x589E;&#x4E3B;&#x952E;userId:" + userDO.getUserId());
    }
  }

}

mybatis SelectKey解析

从输出可以看到成功获取到自增主键userId并已经设置到userDO参数对象中了,下面来看看

public class PreparedStatementHandler extends BaseStatementHandler {

  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }
}

当为

public class SelectKeyGenerator implements KeyGenerator {

  public static final String SELECT_KEY_SUFFIX = "!selectKey";
  private final boolean executeBefore;
  private final MappedStatement keyStatement;

  public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
    // &#x4E3B;sql&#x524D;&#x9762;&#x6267;&#x884C;&#x8FD8;&#x662F;&#x540E;&#x9762;&#x6267;&#x884C;
    this.executeBefore = executeBefore;
    this.keyStatement = keyStatement;
  }

  @Override
  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (!executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  // &#x5904;&#x7406;&#x751F;&#x6210;&#x7684;&#x952E;
  private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
      if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
        // &#x83B7;&#x53D6;&#x9700;&#x8981;&#x8BBE;&#x7F6E;&#x7684;&#x5C5E;&#x6027;&#x503C; &#x5982;id
        String[] keyProperties = keyStatement.getKeyProperties();
        final Configuration configuration = ms.getConfiguration();
        final MetaObject metaParam = configuration.newMetaObject(parameter);
        // Do not close keyExecutor.

        // The transaction will be closed by parent executor.

        Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
        // &#x67E5;&#x8BE2;sql&#x5982; select last_insert_id()&#x83B7;&#x53D6;&#x4E3B;&#x952E;id
        List<object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
        if (values.size() == 0) {
          throw new ExecutorException("SelectKey returned no data.");
        } else if (values.size() > 1) {
          throw new ExecutorException("SelectKey returned more than one value.");
        } else {
          MetaObject metaResult = configuration.newMetaObject(values.get(0));
          // &#x5C06;&#x4E3B;&#x952E;id&#x7684;&#x503C;&#x8BBE;&#x7F6E;&#x5230;parameter&#x53C2;&#x6570;&#x4E2D;
          if (keyProperties.length == 1) {
            if (metaResult.hasGetter(keyProperties[0])) {
              setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
            } else {
              // no getter for the property - maybe just a single value object
              // so try that
              setValue(metaParam, keyProperties[0], values.get(0));
            }
          } else {
            // &#x82E5;&#x67E5;&#x8BE2;&#x7684;&#x5C5E;&#x6027;&#x6709;&#x591A;&#x4E2A;&#x5219;&#x5206;&#x522B;&#x8BBE;&#x7F6E;
            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) {
    String[] keyColumns = keyStatement.getKeyColumns();

    if (keyColumns == null || keyColumns.length == 0) {
      // no key columns specified, just use the property names
      for (String keyProperty : keyProperties) {
        setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
      }
    } else {
      if (keyColumns.length != keyProperties.length) {
        throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
      }
      for (int i = 0; i < keyProperties.length; i++) {
        setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
      }
    }
  }

  private void setValue(MetaObject metaParam, String property, Object value) {
    if (metaParam.hasSetter(property)) {
      metaParam.setValue(property, value);
    } else {
      throw new ExecutorException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");
    }
  }
}</object>

可以看到上述代码会先查询sql获取返回结果之后再把值设置到参数对象中,但可以看到当查询结果value.size() > 1的时候就会抛出异常,因此

3. useGeneratedKeys

那么有什么办法可以获取到批量插入的主键id呢,答案是有的可以使用

#mapper
void batchInsert(@Param("userDOList") List<userdo> userDOList);

#mapper.xml
    insert into user(username, password, nickname)
    values
    <foreach collection="userDOList" item="userDO" separator=",">
      (#{userDO.username}, #{userDO.password}, #{userDO.nickname})
    </foreach>

#java&#x6D4B;&#x8BD5;&#x4EE3;&#x7801;
public class Test {

  public static void main(String[] args) throws IOException {

    try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
      // &#x6784;&#x5EFA;session&#x5DE5;&#x5382; DefaultSqlSessionFactory
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession sqlSession = sqlSessionFactory.openSession();
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      UserDO userDO = new UserDO();
      userDO.setUsername("monian");
      userDO.setPassword("123");
      userDO.setNickname("monianx");
      UserDO userDO1 = new UserDO();
      userDO1.setUsername("monian");
      userDO1.setPassword("123");
      userDO1.setNickname("monianx");
      userMapper.batchInsert(Arrays.asList(userDO, userDO1));
      System.out.println("&#x81EA;&#x589E;&#x4E3B;&#x952E;userId:" + Arrays.asList(userDO.getUserId(), userDO1.getUserId()));
    }
  }

}</userdo>

输出结果可以看到批量插入成功获取到主键userId的值了,原理的话感兴趣的同学可以去阅读下Jdbc3KeyGenerator这个类的源码,这里就不细说啦

mybatis SelectKey解析

4.selectKey和useGeneratedKeys

最后谈谈笔者对这两个的理解,selectKey可以自定义查询的sql更加的灵活不单单只是获取自增主键但查询结果行数不能有多行否则会抛出异常,而useGeneratorKeys主要是获取自动生成主键且能支持多行支持批量插入获取主键值,至于在实际开发中使用哪一种就看业务需求是怎样的了。

Original: https://www.cnblogs.com/monianxd/p/16481802.html
Author: 默念x
Title: mybatis SelectKey解析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/505212/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Isilon 的OneFs常见操作命令(一)

    1背景知识: Isilon的oneFS是基于Free BSD的,FreeBSD 是一种类UNIX操作系统,因此有些类似Linux操作系统的常见命令可以直接使用,但有些又略微差别,需…

    数据库 2023年6月14日
    0343
  • 2022-08-17 DQL—-子查询,日期格式

    子查询、日期格式 DQL查询语言 子查询 根据结果集中的行数,子查询可以分为以下几类: [En] According to the number of rows in the re…

    数据库 2023年5月24日
    094
  • java基础

    java基础知识图解 软件开发 软件开发 软件,即一系列按照特定顺序组织的计算机数据和指令的集合。有系统软件和应用软件之分。 人机交互方式 图形化界面(Graphical User…

    数据库 2023年6月16日
    070
  • Redis的五大数据类型(简单使用)

    redis是基于内存的,内存的读写速度非常快 ; redis是单线程的,省去了很多上下文切换线程的时间; *redis因为是基于内存的 不涉及io操作 所以单线程效率是最高的 回归…

    数据库 2023年6月6日
    0101
  • 2022-8-10 JAVA的反射机制

    反射机制 AVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方…

    数据库 2023年6月14日
    079
  • MySQL SUBSTRING_INDEX截取字符串

    一、SUBSTRING_INDEX 二、示例 Original: https://www.cnblogs.com/woods1815/p/16368248.htmlAuthor: …

    数据库 2023年6月14日
    089
  • 23种设计模式之策略模式

    文章目录 概览 策略模式的优缺点 策略模式的应用场景 策略模式的结构与实现 * 模式的结构 模式的实现 策略模式的扩展 总结 ; 概览 策略模式定义了一系列算法,并将每个算法封装起…

    数据库 2023年6月6日
    0122
  • Ansible简介

    Ansible 是一种常用的自动运维化工具,基于 python 开发,分布式,无需客户端,轻量级,配置语言采用 YAML。 模块化:调用特定的模块,完成特殊的任务。 2.Param…

    数据库 2023年6月14日
    0118
  • 一个小工具帮你搞定实时监控Nginx服务器

    Linux运维工程师的首要职责就是保证业务7 x 24小时稳定的运行,监控Web服务器对于查看网站上发生的情况至关重要。关注最多的便是日志变动,查看实时日志文件变动大家第一反应应该…

    数据库 2023年6月9日
    0154
  • 数据库操作命令

    MySQL Key Command 清屏 (滚屏 ) 登录 切换数据库 查看数据库 查看表 查看表结构 退出 帮助 Key Command 查看可用字符集 查看默认字符集 设置默认…

    数据库 2023年5月24日
    077
  • 对炒股看法

    1. 自己的炒股情况 2017 年左右接触炒股, 陆续入迷. 个人情况输的多. 主要输在阅历浅, 策略不连贯, 心态不稳. 期间翻阅了大量书籍, 学到了很多, 开拓了眼界. 真的是…

    数据库 2023年6月9日
    086
  • 2010最危险的编程错误(转)

    网络无处不在的今天,安全问题日益严峻,攻击事件层出不穷,应该说,软件系统中代码存在安全漏洞是主要的祸因之一。而这实际上反映了软件开发人员在编程的安全性方面缺乏必要的培训和常识。 由…

    数据库 2023年6月11日
    0101
  • 常见的攻击方式以及防护策略

    本文主要给大家介绍一下常见的几种网络攻击方式(包括CC,UDP,TCP)和基础防护策略! 1.0 常见的网络攻击方式 第一种CC攻击 CC攻击( ChallengeCoHapsar…

    数据库 2023年6月9日
    087
  • MySQL45讲之幻读

    前言 本文介绍了什么是虚读,虚读存在的问题和解决方法,以及间隙锁带来的麻烦。 [En] This paper introduces what is phantom reading,…

    数据库 2023年5月24日
    085
  • Asp.NET core/net 5接口返回实体含有long/int64的属性序列后最后几位变为0的解决

    Asp.NET core /net 5接口返回实体含有long/int64的属性时,序列后最后几位变为0的。 不得不吐槽一下MS,这种事还有问题,NND。 解决方案在startup…

    数据库 2023年6月14日
    078
  • 力扣数据库题目176第二高的薪水

    力扣数据库题目176第二高的薪水 题目 编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。 +—-+——&#8…

    数据库 2023年6月9日
    0101
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球