mybatis 不生效 参数_MyBatis参数使用@Param注解获取不到自增id问题
一、背景
群里有个哥们分享了一个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问题相关推荐
- mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法
转载自 mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法 useActualParamName配置 useActualParamName 允许使用方法签名中的名称作为 ...
- get占位符传多个参数_mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法...
useActualParamName配置 useActualParamName 允许使用方法签名中的名称作为语句参数名称. 为了使用该特性,你的工程必须采用Java 8编译,并且加上-paramete ...
- @Param注解在dao层的使用
有时在前台用ajax传过来许多参数,不知道在mybatis如何封装,就要用到@Param注解了,这时就不需要在映射文件写传入参数了,这种方法虽然比较取巧,但还是很实用的,如下图: 转载于:https: ...
- @Param注解的使用和解析
作用:用注解来简化xml配置的时候(比如Mybatis的Mapper.xml中的sql参数引入),@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中(一 ...
- 【MyBatis使用】 mapper文件未编译 + statementType使用 + 返回结果字段顺序不一致 + 获取自增ID + 一个update标签批量更新记录
1. mapper 文件未编译 如果mapper文件未编译,会报绑定异常. <build><resources><resource><directory> ...
- mybatis传递多个参数_Mybatis传递多个参数的4种方式(干货)
现在大多项目都是使用Mybatis了,但也有些公司使用Hibernate.使用Mybatis最大的特性就是sql需要自己写,而写sql就需要传递多个参数.面对各种复杂的业务场景,传递参数也是一种学问. ...
- mybatis 取查询值_Mybatis --- 映射文件、参数处理、参数值的获取、select元素
这样就可以在insert函数中获取新添加的用户的 id主键,否则获取不到 select * from student where id = #{id} insert into student(name ...
- mybatis复习02,简单的增删改查,@Param注解多个参数,resultType与resultMap的区别,#{}预编译参数
mybatis复习02,简单的增删改查 创建数据表 user_info 在项目中创建数据表对应的实体类 UserInfo.java 在pom.xml文件中引入依赖 创建核心配置文件mybatis-co ...
- 关于mybatis的@Param注解和参数
1,使用@Param注解 当以下面的方式进行写SQL语句时: @Select("select column from table where userid = #{userid} " ...
最新文章
- 12年后,人工智能和人类会是什么样?这是900位专家的看法|报告
- cad2016中选择全图字体怎么操作_DNF手游快速升级攻略 DNF手游怎么快速升级
- emlog链接html,emlog如何做站内外链跳转优化教程
- ThreadPoolExecutor源码学习(2)-- 在thrift中的应用
- VSCode 多开、环境对比
- 进程间的通信——无名管道
- Silverlight 解谜游戏 之十七 胜利界面优化
- 利用中继攻击解锁并开走汽车,本田不打算修复(含视频)
- 计算机房在五楼英语,“我住在五楼”怎么用英语表达?
- ElasticJob3.0整合SpringBoot,ElasticJob-Lite【ElasticJob入门篇】
- 基于深度学习的图像修复—心中无码
- 百度ai人体关键点识别
- 《合约星期五》OKEx BTC季度合约 0726周报
- 2019年最新手游脚本开发教程
- linux开机dracut界面_linux开机启动步骤详解
- 使用 Task.Wait()?立刻死锁(deadlock)
- 2019河南省第十二届ACM省赛原题题目及省赛榜单
- nvidia驱动升级和nvidia-docker2安装
- 雪峰磁针石博客]渗透测试简介2入侵工具
- AutoCAD 2021 for Mac(cad2021)中文版
热门文章
- 统计文件夹下音频文件时长
- 格兰因果模型可以分析哪些东西_相关不等于因果,深度学习让AI问出“十万个为什么”...
- 什么是集群?看完这篇你就知道啦!
- VSCode格式化保存HTML的标签名前(head,body,/html)自动空出一行的解决办法
- C#中File和FileStream的简单介绍和用法
- 黑客攻防技术宝典(二十一)
- MySQL 如何优化慢查询?
- HDU 1847 Good Luck in CET-4 Everybody! (sg函数)
- ID2022 for Mac安装教程
- mac u盘文件过大 拷贝不进去_Mac大文件无法复制到U盘怎么办