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/620886/

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

(0)

大家都在看

  • HA: FORENSICS靶机练习

    ubuntu拿到手,没有恢复模式,不好绕密码,仿真软件又会更改所有用户的密码,怕影响后续操作,先不采用,先试试用john跑一下看看能不能跑出一两个来。 刚好跑出来一个,用户 &lt…

    数据库 2023年6月11日
    076
  • MySQL45讲之order工作原理

    本文介绍 order 的三种排序方式,全字段排序、rowid 排序和索引树排序,以及每种排序方式具体是如何工作的。 当使用 explain 查看执行计划时,如果 extra 中有 …

    数据库 2023年5月24日
    0111
  • 项目要实现多数据源动态切换,咋搞?

    文章首发于公众号:BiggerBoy 原名:编程大道 在做项目的时候,几乎都会用到数据库,很多时候就只连一个数据库,但是有时候我们需要一个项目操作多个数据库,不同的业务功能产生的数…

    数据库 2023年6月11日
    0103
  • JAVA中如何取得一个数组中最大值和最小值呢?

    数组是日常开发中,常用的数据结构, 它可用于存储同一类型的数据,如:(基础类型,引用类型) 那么我们如何获取一个数组中的最大值和最小值呢? 对一些基础类型,我们可以直接使用比较, …

    数据库 2023年6月11日
    074
  • 微服务架构项目浅析

    微服务架构的演变 最初的需求 业务发展后需要克服的问题 微服务架构使用的组件 Nginx Redis Rabbitmq Mysql jar jdk * 总结 ​ 这个章节主要介绍微…

    数据库 2023年6月6日
    068
  • 23种设计模式之分类总结

    关于设计模式的学习要告一段落了,学习的这一路上,也收到了不少小伙伴的留言,以及点赞给了我莫大的鼓励,我在这里谢谢大家的鼓励。。。 我会再接再厉,嘿嘿。。。 以上的话虽是真心话,但是…

    数据库 2023年6月6日
    0270
  • mysql多实例部署

    在MySQL中配置多实例 1.软件下载 2.配置用户和组并解压二进制程序至/usr/local下 3.创建各实例数据存放的目录 4.初始化各示例 5.配置配置文件/etc/my.c…

    数据库 2023年5月24日
    084
  • Linux 7安装Mysql5.7版本

    Mysql 5.7的安装搭建 首先去到官方网站的下载链接中找到对应你Linux服务器版本的mysql软件包 https://dev.mysql.com/downloads/repo…

    数据库 2023年5月24日
    087
  • 慢SQL,压垮团队的最后一根稻草!

    一、什么是慢 SQL 什么是慢SQL? 顾名思义,运行时间较长的 SQL 语句即为慢 SQL! 然后,问题就出现了。需要多长时间才能慢下来? [En] Then the quest…

    数据库 2023年5月24日
    075
  • MYSQL–>索引

    概述 索引是帮助MYSQL 高效获取数据的 有序数据结构 数据库维护着满足特定查找算法的数据结构,这种数据结构以某种方式指向数据。 这样就可以在数据结构上实现高级查找方法,这种数据…

    数据库 2023年6月14日
    076
  • Linux指令_入门基础

    2.pwd指令 : 用法:#pwd (print working directory ,打印当前工作目录) 3.cd指令 : 命令:# cd (change directory,改…

    数据库 2023年6月11日
    088
  • Spring常见问题

    Spring常见问题 问渠那得清如许?为有源头活水来。 Spring 是个 java 企业级应用的开源开发框架。Spring 主要用来开发 Java 应用,但是有些扩展是针对构建 …

    数据库 2023年6月14日
    064
  • 2018年最新JAVA面试题总结之JavaWeb(2)

    转自于:https://zhuanlan.zhihu.com/p/39522575 1、tomcat的优化方式?回答:Tomcat的优化我准备从三方面来说: 第一部分: 内存优化T…

    数据库 2023年6月16日
    079
  • 使用Docker安装FastDFS

    1. 获取镜像 可以利用已有的FastDFS Docker镜像来运行FastDFS。 获取镜像可以通过下载: sudo docker image pull delron/fastd…

    数据库 2023年6月14日
    085
  • Redis——数据操作

    2022-09-20 Redis——select Redis数据库中的数据库的个数为: 16个,使用0号数据库开始的,到第15个数据库结束。 在ubantu中,进入Redis客户端…

    数据库 2023年6月14日
    060
  • ATM系统开发(Java版)

    ATM系统Java模拟开发总结 ATM系统开发 技术点分析 1.面向对象编程 每个用户的账户都是一个对象,所以需要设计账户类Accent用于创建账户对象封装账户信息。 2.使用集合…

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