获取参数值
两种方式
1、${}
(1)本质:字符串拼接
(2)使用字符串拼接的方式拼接 SQL,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号
(3)存在 SQL 注入风险
2、#{}
(1)本质:占位符赋值
(2)使用占位符赋值的方式拼接 SQL,若为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
(3)优先使用
3、分情况处理
(1)实体类类型的参数
(2)使用 @Param 标识参数
单个字面量类型的参数
1、Mapper 接口中的方法参数,为单个的字面量类型
3、${} 需要手动加单引号
4、示例
(1)#{}
<!-- User getUserByUsername(String username); -->
<select id="getUserByUsername" resultType="User">
select * from users where username = #{username}
</select>
(2)${}
<!-- User getUserByUsername(String username); -->
<select id="getUserByUsername" resultType="User">
select * from users where username = '${username}'
</select>
多个字面量类型的参数
1、若 Mapper 接口中的方法参数为多个时,此时 MyBatis 会自动将这些参数放在一个 Map 集合中
2、键值对的两种存储方式
(1)以 arg0,arg1……为 key,以参数为 value
(2)以 param1,param2……为 key,以参数为 value
(3)两者本质相同,索引不同,使用 arg 或 param 都可以,可以混用
(4)注意:arg 是从 arg0 开始,param 从 param1 开始
3、只需要通过 $
5、示例
(1)#{}
<!-- User checkLogin(String username, String password); -->
<select id="checkLogin" resultType="User">
select * from users where username = #{arg0} and password = #{arg1}
</select>
(2)${}
<!--User checkLogin(String username, String password);-->
<select id="checkLogin" resultType="User">
select * from users where username = '${param1}' and password = '${param2}'
</select>
Map 集合类型的参数
1、若 Mapper 接口中的方法需要的参数为多个时,可以手动创建 Map 集合,将数据放在 Map 中
2、只需要通过 ${} 和 #{} 访问 Map 集合的 key,就可以获取相对应的 value
3、${} 需要手动加单引号
4、示例
(1)设置参数
//设置Map键值对,直接传入Map,key对应value作为SQL语句参数
Map<String,Object> map = new HashMap<>();
map.put("usermane","value1");
map.put("password","value2");
(2)#{}
<!-- User checkLoginByMap(Map<String,Object> map); -->
<select id="checkLoginByMap" resultType="User">
<!-- {}中的内容为Map集合自定义的key -->
select * from users where username = #{username} and password = #{password}
</select>
(3)${}
<!-- User checkLoginByMap(Map<String,Object> map); -->
<select id="checkLoginByMap" resultType="User">
<!-- {}中的内容为Map集合自定义的key -->
select * from users where username = '${username}' and password = '${password}'
</select>
实体类类型的参数
1、Mapper 接口中的方法参数,为实体类对象
2、使用 $
3、${} 需要手动加单引号
4、示例
(1)#{}
<!-- int insertUser(User user); -->
<insert id="insertUser">
insert into users values(#{username}, #{password}, #{id})
</insert>
(2)${}
<!-- int insertUser(User user); -->
<insert id="insertUser">
insert into users values('${username}', '${password}, '${id}')
</insert>
@Param
1、标识 Mapper 接口中的方法参数,此时,会将这些参数放在 Map 集合中
2、键值对的两种存储方式
(1)以 @Param 注解的 value 属性为键,以参数为值
(2)在默认情况下,即不命名参数,除 RowBounds 以外的参数,会以 param 加参数位置被命名,以 param1,param2……为键,以参数为值
3、只需要通过 $
4、${}需要手动加单引号
5、示例
(1)#{}
<!-- User checkLoginByParam(@Param("username") String username, @Param("password") String password); -->
<select id="checkLoginByParam" resultType="User">
select * from users where username = #{username} and password = #{password}
</select>
(2)${}
<!-- User checkLoginByParam(@Param("username") String username, @Param("password") String password); -->
<select id="checkLoginByParam" resultType="User">
select * from users where username = '${username}' and password = '${password}'
</select>
源码
1、Mapper 接口的代理类
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
}
}
2、Mapper 接口方法的执行
public class MapperMethod {
private final SqlCommand command;
public static class SqlCommand {
//当前执行的SQL,SQL的唯一标识:命名空间(Mapper接口全类名)+ SQL的id(Mapper接口中的方法名)
private final String name;
//当前执行SQL的类型
private final SqlCommandType type;
}
public Object execute(SqlSession sqlSession, Object[] args) {//args为方法实参
Object result;
switch (command.getType()) {
case INSERT: {//插入SQL
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {//更新SQL
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {//删除SQL
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT://查询SQL
//没有返回值
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//返回多条数据
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//返回Map
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
//将方法参数值转换为SQL语句的参数,与形参名无关
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
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);
}
}
public class ParamNameResolver {
private final SortedMap<Integer, String> names;
private boolean hasParamAnnotation;
//命名参数解析器,获取names
public ParamNameResolver(Configuration config, Method method) {
this.useActualParamName = config.isUseActualParamName();
//Mapper接口中的方法method,getParameterTypes()反射获取参数的Class对象,Class<?>[]表明可能存在多个参数
final Class<?>[] paramTypes = method.getParameterTypes();
//方法参数的注解,Annotation[][]表明多个参数的多个注解:一维数组:参数,二维数组:注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
//paramCount参数的个数
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]) {
//注解是否为@Param
if (annotation instanceof Param) {
hasParamAnnotation = true;
//注解强转为Param,获取其value
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
if (useActualParamName) {
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);
}
//map、names存放相同数据
names = Collections.unmodifiableSortedMap(map);
}
public Object getNamedParams(Object[] args) {//args方法的实参
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
Object value = args[names.firstKey()];
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
} else {
//创建Map集合param
final Map<String, Object> param = new ParamMap<>();
int i = 0;
//遍历names键值对
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//param键值对第一种存储方式:key为names的value,value为args的value
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
//判断key是否为param形式
if (!names.containsValue(genericParamName)) {
//param键值对第二种存储方式:key为GENERIC_NAME_PREFIX + (i + 1),value为args的value
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战