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)

大家都在看

  • 软件测试基础理论(2)

    一, 为什么要进行软件测试 &#x4E3A;&#x4E86;&#x901A;&#x8FC7;&#x8F6F;&#x4EF6;&amp…

    数据库 2023年6月16日
    089
  • Linux快速安装流量监控工具(实用版)

    前言: Linux流量监控工具,在此我推荐两种分别为: 1、nload(推荐)因为个人看着舒服点😂 2、iftop 以上两种任选其一即可,在此对两种都有介绍和安装教程,我写了,大家…

    数据库 2023年6月16日
    087
  • [spring]spring静态代理和aop

    10.代理模式 代理模式的分类: 静态代理 动态代理 关系分析 抽象角色:一般会使用接口或者抽象类 真实角色:被代理的角色 代理角色:代理真实的角色,做一些附属的操作 客户:访问代…

    数据库 2023年6月16日
    093
  • 响应式编程-入门介绍

    概念 与传统编程模型对比 传统编程模型,主要特点是 同步阻塞式-Blocking; 而响应式编程(Reactive Programming) 主要特点是 异步非阻塞 Non-Blo…

    数据库 2023年6月11日
    0113
  • 有道云笔记迁移到为知笔记

    背景 &#x4E4B;&#x524D;&#x4E00;&#x76F4;&#x7528;&#x7684;&#x6709;&am…

    数据库 2023年6月9日
    093
  • 台湾停电公司竟要求手写代码,网友:就无语

    背景 3月3日上午9点左右,中国台湾省突然毫无预警地大面积停电,高雄市车流量最多的街道里,至少有191处红绿灯不亮,此事严重影响了人民的生活。 不少台湾民众在推特上吐槽,乌克兰战乱…

    数据库 2023年6月9日
    097
  • SQLZOO练习二–SELECT from Nobel Tutorial

    We continue practicing simple SQL queries on a single table. This tutorial is concerned wi…

    数据库 2023年6月16日
    083
  • java通过内存流去掉多行文本中的空行

    对于多行文本,你直接通过replace,replaceAll是不能将空行删除的,你需要遍历这些行,对每行文本进行操作,最后把返回新的文本才行。 public static Stri…

    数据库 2023年6月6日
    094
  • 数据库操作

    数据库操作 数据库基础数据库是一种 存储结构, 允许使用各种格式 输入、处理、检索 数据,且不用在每次需要数据时 重新输入数据。 select 语句: select语句 用于查询数…

    数据库 2023年6月16日
    0104
  • MySQL–排序检索数据(ORDER BY)

    检索出的数据并不是以纯粹的随机顺序显示的。如果不排序,数据一般将以它在底层表中出现的顺序显示。这可以是数据最初添加到表中的顺序。但是,如果数据后来进行过更新或删除,则此顺序将会受到…

    数据库 2023年6月16日
    080
  • 省去跨表联查与注释查询的存储过程

    1 — 打印query存储过程的帮助信…

    数据库 2023年5月24日
    070
  • 设计 | ClickHouse 分布式表实现数据同步

    作者:吴帆 青云数据库团队成员主要负责维护 MySQL 及 ClickHouse 产品开发,擅长故障分析,性能优化。 在多副本分布式 ClickHouse 集群中,通常需要使用 D…

    数据库 2023年5月24日
    0100
  • 中文技术文档写作规范

    使用 markdown 格式书写文档 只使用一二三级标题,三级标题下面的并列性内容使用列表展示 二级标题前使用行分隔符表示分隔 段落之间使用一个空行隔开 一句话或者以逗号分隔的句子…

    数据库 2023年6月6日
    083
  • MySQL实战45讲 9

    09 | 普通索引和唯一索引,应该怎么选择? 每个人都有一个唯一的身份证号,而且业务代码已经保证了不会写入两个重复的身份证号。如果市民系统需要按照身份证号查姓名,就会执行类似这样的…

    数据库 2023年6月16日
    0110
  • python-django框架中使用FastDFS分布式文件系统

    一、安装FastDFS 1-1:执行docker命令安装 bash;gutter:true; 安装tracker docker run -dti –network=host –…

    数据库 2023年6月6日
    0101
  • 第八章:变量、常量和基础类型

    本篇翻译自《Practical Go Lessons》 Chapter 8: Variables, constants and basic types 1 你将在本章中学到什么? …

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