一、背景

群里有个哥们分享了一个mybatis的小”坑“。

”分享一个菜鸡点:mybatis中使用@param注解后,要keyProperty=“注解名.id”,不然拿不到生成的主键值“

那么我们就要看@Param 在什么时候用?为啥不写参数名不行呢?

二、解析

2.1 官方文档大法

user类

public class User {

//...

public User(Integer id, String username, int age) {

//...

}

//...

}

为了将结果注入构造方法,MyBatis 需要通过某种方式定位相应的构造方法。 在下面的例子中,MyBatis 搜索一个声明了三个形参的的构造方法,参数类型以 java.lang.Integer, java.lang.String 和 int 的顺序给出。

当你在处理一个带有多个形参的构造方法时,很容易搞乱 arg 元素的顺序。

从版本 3.4.3 开始,可以在指定参数名称的前提下,以任意顺序编写 arg 元素。

为了通过名称来引用构造方法参数,你可以添加 @Param 注解,或者使用 ‘-parameters’ 编译选项并启用 useActualParamName 选项(默认开启)来编译项目。

下面是一个等价的例子,尽管函数签名中第二和第三个形参的顺序与 constructor 元素中参数声明的顺序不匹配。

如果存在名称和类型相同的属性,那么可以省略 javaType 。剩余的属性和规则和普通的 id 和 result 元素是一样的。

【1】什么情况下用@param注解

一、是参数的顺序和xml映射文件的顺序不匹配时。

二、是参数为两个对象且属性有重名的时候,mybatis无法感知是哪个类的属性。

2.2 源码大法

为什么设置了@Parm的值,keyProperty不加item前缀就不生效呢?(内容略长,要有心理准备哈哈)

源码参考这里

我们加注解

void insert(@Param("item") CuxTodoItems cuxTodoItems);

对应xml

insert into cux_todo_items (user_id,todo_item_title,todo_item_content,priority)

values ( #{item.userId},#{item.todoItemTitle},#{item.todoItemContent},#{item.priority});

先执行这里构造参数名解析对象

public ParamNameResolver(Configuration config, Method method) {

final Class>[] paramTypes = method.getParameterTypes();

final Annotation[][] paramAnnotations = method.getParameterAnnotations();

final SortedMap map = new TreeMap();

int paramCount = paramAnnotations.length;

// get names from @Param annotations

for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {

if (isSpecialParameter(paramTypes[paramIndex])) {

// skip special parameters

continue;

}

String name = null;

for (Annotation annotation : paramAnnotations[paramIndex]) {

if (annotation instanceof Param) {

hasParamAnnotation = true;

name = ((Param) annotation).value();

break;

}

}

if (name == null) {

// @Param was not specified.

if (config.isUseActualParamName()) {

name = getActualParamName(method, paramIndex);

}

if (name == null) {

// use the parameter index as the name ("0", "1", ...)

// gcode issue #71

name = String.valueOf(map.size());

}

}

map.put(paramIndex, name);

}

names = Collections.unmodifiableSortedMap(map);

}

执行插入语句需要执行到

org.apache.ibatis.binding.MapperMethod#execute

public Object execute(SqlSession sqlSession, Object[] args) {

Object result;

switch (command.getType()) {

case INSERT: {

// 获取待的参数插入的对象

Object param = method.convertArgsToSqlCommandParam(args);

// 执行插入并计算结果

result = rowCountResult(sqlSession.insert(command.getName(), param));

break;

}

case UPDATE: {

Object param = method.convertArgsToSqlCommandParam(args);

result = rowCountResult(sqlSession.update(command.getName(), param));

break;

}

case DELETE: {

Object param = method.convertArgsToSqlCommandParam(args);

result = rowCountResult(sqlSession.delete(command.getName(), param));

break;

}

case SELECT:

if (method.returnsVoid() && method.hasResultHandler()) {

executeWithResultHandler(sqlSession, args);

result = null;

} else if (method.returnsMany()) {

result = executeForMany(sqlSession, args);

} else if (method.returnsMap()) {

result = executeForMap(sqlSession, args);

} else if (method.returnsCursor()) {

result = executeForCursor(sqlSession, args);

} else {

Object param = method.convertArgsToSqlCommandParam(args);

result = sqlSession.selectOne(command.getName(), param);

}

break;

case FLUSH:

result = sqlSession.flushStatements();

break;

default:

throw new BindingException("Unknown execution method for: " + command.getName());

}

if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {

throw new BindingException("Mapper method '" + command.getName()

+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");

}

return result;

}

public Object convertArgsToSqlCommandParam(Object[] args) {

return paramNameResolver.getNamedParams(args);

}

底层调用

org.apache.ibatis.reflection.ParamNameResolver#getNamedParams

public Object getNamedParams(Object[] args) {

final int paramCount = names.size();

if (args == null || paramCount == 0) {

return null;

} else if (!hasParamAnnotation && paramCount == 1) {

return args[names.firstKey()];

} else {

final Map param = new ParamMap();

int i = 0;

for (Map.Entry entry : names.entrySet()) {

param.put(entry.getValue(), args[entry.getKey()]);

// add generic param names (param1, param2, ...)

final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);

// ensure not to overwrite parameter named with @Param

if (!names.containsValue(genericParamName)) {

param.put(genericParamName, args[entry.getKey()]);

}

i++;

}

return param;

}

}

我们看到参数名解析这一块,如果有@param注解则取这个注解的值,否则根据参数的索引取参数名,如果取不到则映射为0,1…

然后调用插入后

执行对GeneratedKeys的处理

org.apache.ibatis.executor.keygen.SelectKeyGenerator#processGeneratedKeys

org.apache.ibatis.executor.statement.PreparedStatementHandler#update

org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#processAfter

org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#processBatch

最重要的是两个函数

第一个是:org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#getTypeHandlers

判断keyProperties是否存在,如果存在类型是啥

这里存在且类型为Integer

执行org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#populateKeys填充完毕

发现核心就是用了@Parm设置了名字为item后,如果只是设置基本属性不带item的话,从ObjectWrapper里找不到这个属性。

如果不用注解啥情况呢?

insert into cux_todo_items (user_id,todo_item_title,todo_item_content,priority)

values ( #{userId},#{todoItemTitle},#{todoItemContent},#{priority});

设置参数名解析

org.apache.ibatis.reflection.ParamNameResolver#ParamNameResolver

插入后调用获取类型解析器

不设置@Param注解时,objectWrapper直接是一个Object,所以直接可以找到todoItemId属性。

这是关键。

三、总结

遇到问题优先看官方文档,一般大多数用法都可以找到。

另外通过源码调试时学习的一个非常重要方法,大家遇到类似问题可以通过调试来研究。

如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

另外欢迎加入我的知识星球,知识星球ID:15165241 一起交流学习。

https://t.zsxq.com/Z3bAiea  申请时标注来自CSDN。

原文链接:https://blog.csdn.net/w605283073/article/details/90739604

本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。

mybatis 不生效 参数_MyBatis参数使用@Param注解获取不到自增id问题相关推荐

  1. mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法

    转载自  mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法 useActualParamName配置 useActualParamName 允许使用方法签名中的名称作为 ...

  2. get占位符传多个参数_mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法...

    useActualParamName配置 useActualParamName 允许使用方法签名中的名称作为语句参数名称. 为了使用该特性,你的工程必须采用Java 8编译,并且加上-paramete ...

  3. @Param注解在dao层的使用

    有时在前台用ajax传过来许多参数,不知道在mybatis如何封装,就要用到@Param注解了,这时就不需要在映射文件写传入参数了,这种方法虽然比较取巧,但还是很实用的,如下图: 转载于:https: ...

  4. @Param注解的使用和解析

    作用:用注解来简化xml配置的时候(比如Mybatis的Mapper.xml中的sql参数引入),@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中(一 ...

  5. 【MyBatis使用】 mapper文件未编译 + statementType使用 + 返回结果字段顺序不一致 + 获取自增ID + 一个update标签批量更新记录

    1. mapper 文件未编译 如果mapper文件未编译,会报绑定异常. <build><resources><resource><directory> ...

  6. mybatis传递多个参数_Mybatis传递多个参数的4种方式(干货)

    现在大多项目都是使用Mybatis了,但也有些公司使用Hibernate.使用Mybatis最大的特性就是sql需要自己写,而写sql就需要传递多个参数.面对各种复杂的业务场景,传递参数也是一种学问. ...

  7. mybatis 取查询值_Mybatis --- 映射文件、参数处理、参数值的获取、select元素

    这样就可以在insert函数中获取新添加的用户的 id主键,否则获取不到 select * from student where id = #{id} insert into student(name ...

  8. mybatis复习02,简单的增删改查,@Param注解多个参数,resultType与resultMap的区别,#{}预编译参数

    mybatis复习02,简单的增删改查 创建数据表 user_info 在项目中创建数据表对应的实体类 UserInfo.java 在pom.xml文件中引入依赖 创建核心配置文件mybatis-co ...

  9. 关于mybatis的@Param注解和参数

    1,使用@Param注解 当以下面的方式进行写SQL语句时: @Select("select column from table where userid = #{userid} " ...

最新文章

  1. 12年后,人工智能和人类会是什么样?这是900位专家的看法|报告
  2. cad2016中选择全图字体怎么操作_DNF手游快速升级攻略 DNF手游怎么快速升级
  3. emlog链接html,emlog如何做站内外链跳转优化教程
  4. ThreadPoolExecutor源码学习(2)-- 在thrift中的应用
  5. VSCode 多开、环境对比
  6. 进程间的通信——无名管道
  7. Silverlight 解谜游戏 之十七 胜利界面优化
  8. 利用中继攻击解锁并开走汽车,本田不打算修复(含视频)
  9. 计算机房在五楼英语,“我住在五楼”怎么用英语表达?
  10. ElasticJob3.0整合SpringBoot,ElasticJob-Lite【ElasticJob入门篇】
  11. 基于深度学习的图像修复—心中无码
  12. 百度ai人体关键点识别
  13. 《合约星期五》OKEx BTC季度合约 0726周报
  14. 2019年最新手游脚本开发教程
  15. linux开机dracut界面_linux开机启动步骤详解
  16. 使用 Task.Wait()?立刻死锁(deadlock)
  17. 2019河南省第十二届ACM省赛原题题目及省赛榜单
  18. nvidia驱动升级和nvidia-docker2安装
  19. 雪峰磁针石博客]渗透测试简介2入侵工具
  20. AutoCAD 2021 for Mac(cad2021)中文版

热门文章

  1. 统计文件夹下音频文件时长
  2. 格兰因果模型可以分析哪些东西_相关不等于因果,深度学习让AI问出“十万个为什么”...
  3. 什么是集群?看完这篇你就知道啦!
  4. VSCode格式化保存HTML的标签名前(head,body,/html)自动空出一行的解决办法
  5. C#中File和FileStream的简单介绍和用法
  6. 黑客攻防技术宝典(二十一)
  7. MySQL 如何优化慢查询?
  8. HDU 1847 Good Luck in CET-4 Everybody! (sg函数)
  9. ID2022 for Mac安装教程
  10. mac u盘文件过大 拷贝不进去_Mac大文件无法复制到U盘怎么办