Mybatis源码阅读(三):结果集映射3.3 —— 主键生成策略

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
前言

在前面两篇博客中,我们介绍了对于select语句的简单映射和嵌套映射。mybatis中使用ResultHandler等一系列的类,将查询结果封装到实体类中,可以说是mybatis中最复杂的过程,而剩下的insert、update、delete语句的操作则显得较为简单,没有复杂的映射逻辑。这里需要提的是在insert语句中,关于主键自增的问题。
KeyGenerator

在我们实际的开发中,自增主键还是使用比较多的。而mybatis在处理insert语句时,并不会将自增主键给返回,而是返回插入的条数。当我们有业务需求要获取插入时产生的自增主键(或者其他类型不由程序生成的主键)时,则可以使用KeyGenerator。

KeyGenerator是个接口,定义了processBefore和processAfter两个方法,分别在insert之前和之后执行,代码如下。

/**

  • insert语句不会反回自动生成的主键

  • 该接口用于在插入记录时获取自增主键

  • @author Clinton Begin
    */
    public interface KeyGenerator {

    /**

    • 执行insert之前执行,设置属性order=“BEFORE”
    • @param executor
    • @param ms
    • @param stmt
    • @param parameter
      */
      void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

    /**

    • 执行insert之后执行,设置属性order=“AFTER”
    • @param executor
    • @param ms
    • @param stmt
    • @param parameter
      */
      void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

}

KeyGenerator有三个实现类

其中NoKeyGenerator的方法都是空实现,本文不再讲解。
Jdbc3KeyGenerator

Jdbc3KeyGenerator用于取回数据库生成的自增id,它对应于mybatis-config.xml配置的useGeneratedKeys,以及insert节点中配置的useGeneratedKeys属性。

Jdbc3KeyGenerator的processBefore是空实现,只实现了processAfter方法,该方法会调用processBatch方法将SQL语句执行后生成的主键记录到用户传递的实参中,而不是直接返回。代码如下。

/*** 核心方法,将sql语句生成的主键存放到参数中** @param ms* @param stmt* @param parameter*/
public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {// 获取sql节点配置的keyProperties,指定主键的字段final String[] keyProperties = ms.getKeyProperties();if (keyProperties == null || keyProperties.length == 0) {return;}// 获取数据库生成的主键。如果没有生成主键则结果集为空try (ResultSet rs = stmt.getGeneratedKeys()) {// 获取resultSet元信息final ResultSetMetaData rsmd = rs.getMetaData();// 获取Configurationfinal Configuration configuration = ms.getConfiguration();// 查到的列数小于主键的列数,说明查询有误。mybatis不进行处理if (rsmd.getColumnCount() < keyProperties.length) {// Error?} else {assignKeys(configuration, rs, rsmd, keyProperties, parameter);}} catch (Exception e) {throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);}
}

在assignKeys方法中,处理对主键赋值的逻辑。该方法会判断参数的类型,将Map、集合、对象三种情况分别进行处理。代码如下。

private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,Object parameter) throws SQLException {if (parameter instanceof ParamMap || parameter instanceof StrictMap) {// 参数是mapassignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);} else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()&& ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {// 参数是集合assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, ((ArrayList<ParamMap<?>>) parameter));} else {// 参数是对象assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);}
}

三个方法逻辑类似,这里只对assignKeysToParam方法进行分析,。

该方法会在进入方法时,将参数转为集合,并遍历集合去给主键进行赋值,代码如下。

/*** 当参数是对象时的处理逻辑** @param configuration* @param rs* @param rsmd* @param keyProperties* @param parameter* @throws SQLException*/
private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,String[] keyProperties, Object parameter) throws SQLException {// 将参数转为集合,此时这个集合最多只有一个对象Collection<?> params = collectionize(parameter);if (params.isEmpty()) {return;}// 存放主键属性的信息List<KeyAssigner> assignerList = new ArrayList<>();for (int i = 0; i < keyProperties.length; i++) {assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));}Iterator<?> iterator = params.iterator();while (rs.next()) {if (!iterator.hasNext()) {throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size()));}Object param = iterator.next();assignerList.forEach(x -> x.assign(rs, param));}
}

SelectKeyGenerator

MySql、PostgreSql等数据库支持自增主键,在执行insert时可以不指定主键,插入的过程中由数据库去生成自增主键。而像Oracle、DB2等数据库产品则需要使用sequence实现自增id,因此在insert之前必须明确指定主键的值。SelectKeyGenerator就用来处理不支持自增主键的数据库。

SelectKeyGenerator主要用于生成主键,它会执行映射配置文件中定义的selectKey节点的sql,该语句会获取insert语句所需要的主键。

SelectKeyGenerator中有两个核心字段,keyStatement用于存放解析后的selectKey节点,executeBefore用于标识该节点是在insert之前还是之后执行。

/*** 标识selectKey是在insert之前还是之后执行* true为之前,false为之后*/
private final boolean executeBefore;
/*** 存放selectKey节点,用于获取insert语句使用的主键*/
private final MappedStatement keyStatement;

SelectKeyGenerator的processBefore和processAfter都是执行processGeneratorKeys方法,根据executeBefore字段的值决定是在insert之前还是之后执行。

@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);}
}

processGeneratorKeys方法是处理主键的核心代码。该方法会先获取selectKey节点的keyProperties配置的属性名称,该属性对应着主键。接着,创建Executor执行器,执行selectKey节点中的sql,查询主键。Executor是mybatis的核心执行器,后面的博客会对其进行介绍。

selectKey执行完毕后,会判断查询结果是否合法。主键的查询结果肯定只会返回一条,因此查询结果条数不为1的全部视为非法查询。该方法代码如下。

private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {try {if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {// 获取selectKey节点的keyProperties配置的属性名称,表示主键对应的属性String[] keyProperties = keyStatement.getKeyProperties();final Configuration configuration = ms.getConfiguration();// 创建用户传入的实参对应的MetaObject对象final MetaObject metaParam = configuration.newMetaObject(parameter);// 创建Executor对象,执行keyStatement记录的sql。Executor是mybatis中的执行器,后面会讲Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);// 执行selectKey节点的sql,查询主键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 {// 主键查询出来肯定只有一条,因此values的size不等于1的情况都是错误的// 创建主键对象对应的MetaObjectMetaObject metaResult = configuration.newMetaObject(values.get(0));if (keyProperties.length == 1) {if (metaResult.hasGetter(keyProperties[0])) {// 取出主键的值,设置到用户参数对应的属性中setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));} else {// 如果主键对象不包含get方法,就可能是基本类型或者String,直接复制setValue(metaParam, keyProperties[0], values.get(0));}} else {// 处理主键有多列的情况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);}
}

该方法中有一个handleMultipleProperties方法,用于处理一张表多个主键的情况。由于复合主键耦合性高、影响性能,并且操作起来较为繁琐,因此不推荐数据库中给一张表设置多个主键,这里也就不对该方法进行介绍。
结语

KeyGenerator的代码较为简单,阅读起来也不会吃力。本周我公司已经开始了上班,因此博客也需要开始写了,mybatis的源码已经阅读了一大半,争取在7月份之前把剩下代码的博客全部编写完毕
*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536968.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

list最大容量_Java 基础(四)集合源码解析 List

List 接口前面我们学习了Iterator、Collection&#xff0c;为集合的学习打下了基础&#xff0c;现在我们来学习集合的第一大体系 List。List 是一个接口&#xff0c;定义了一组元素是有序的、可重复的集合。List 继承自 Collection&#xff0c;较之 Collection&#xff0c;List…

Mybatis源码阅读(四):核心接口4.1——StatementHandler

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Shell学习之结合正则表达式与通配符的使用(五)

Shell学习之结合正则表达式与通配符的使用 目录 通配符 正则表达式与通配符通配符通配符的使用正则表达式 正则表达式正则表达式的使用通配符 正则表达式与通配符 正则表达式用来在文件中匹配符合条件的字符串&#xff0c;正则是包含匹配。grep、awk、sed等命令可以支持正则表达…

Mybatis源码阅读(四):核心接口4.2——Executor(上)

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

接收xml参数_SpringBoot实战(二):接收xml请求

强烈推荐一个大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在对接一个第三方系统&#xff0c;需要接收第三方系统的回调&#xff0c;而且格式为XML形式&#xff0c;之前自己一般接收的参数是Json形式&#xff0c;于是乎做个实验验证一下使用…

报错 插入更新_window如何解决mysql数据量过大导致的报错

window如何解决报错“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中输入mysql -hlocalhost -uroot -p #如果设置了密码直接接在p 后面 show variables like %storage_engine%以下为结果可以看到InnoDB是MySQL的默认引…

Mybatis源码阅读(四):核心接口4.2——Executor(下)

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Mybatis源码阅读(五 ):接口层——SqlSession

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

插入公式_一个小工具,彻底帮你搞定在Markdown中插入公式的问题

在编辑Markdown文档时&#xff0c;插入公式是一个挺麻烦的活儿。需要掌握LaTex语法。我自己看完语法后&#xff0c;直接放弃&#xff0c;这绝对是反人类的语法。&#xff08;好吧&#xff0c;是我不会用...&#xff09;但是&#xff0c;我相信你看了这篇文章后&#xff0c;绝对…

Mybatis源码阅读(一):Mybatis初始化1.2 —— 解析别名、插件、对象工厂、反射工具箱、环境

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Google 修改 Chrome API,防止隐身模式检测

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; 在使用 Chrome 浏览网页时&#xff0c;某些网站会使用某种方法来确定访问者是否处于隐身模式&#xff0c;这是一种隐私泄漏行为。Google 目前正在考虑修改 Chrome 的相关 API&#xff0c;来杜绝…

Mybatis源码阅读(一):Mybatis初始化1.1 解析properties、settings

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

JavaScript异步基础

唯一比不知道代码为什么崩溃更可怕的事情是&#xff0c;不知道为什么一开始它是工作的&#xff01;在 ECMA 规范的最近几次版本里不断有新成员加入&#xff0c;尤其在处理异步的问题上&#xff0c;更是不断推陈出新。然而&#xff0c;我们在享受便利的同时&#xff0c;也应该了…

Flutter、ReactNative、uniapp对比

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

多线程中ThreadLocal的使用

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

注解版poi操作工具

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Kali Linux 2019.1 发布,Metasploit 更新到 5.0 版本

百度智能云 云生态狂欢季 热门云产品1折起>>> Kali Linux 2019.1 发布了&#xff0c;Kali 前身 BackTrack&#xff0c;它是一个基于 Debian 的 Linux 发行版&#xff0c;主要用于信息安全行业&#xff0c;其包含了一系列安全、渗透测试和取证工具。此版本 Linux 内核…

peewee mysql_scrapy中利用peewee插入Mysql

前两天老大布置一个任务&#xff0c;说爬下来的数据要存入数据库中&#xff0c;丢给我一个peewee&#xff0c;说用这个。当时的我两眼一抹黑&#xff0c;这是个什么东西呀&#xff0c;我知道scrapy的数据存入数据库是在pipelines中进行设置但是peewee是什么东西呢。经过两天不懈…

Java版数据结构与算法——线性表

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

基于 CODING 的 Spring Boot 持续集成项目

本文作者&#xff1a;CODING 用户 - 廖石荣 持续集成的概念 持续集成(Continuous integration,简称 CI&#xff09;是一种软件开发实践&#xff0c;即团队开发成员经常集成他们的工作&#xff0c;通常每个成员每天至少集成一次&#xff0c;也就意味着每天可能会发生多次集成。每…