获取参数值

两种方式

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;
        }
    }
}
posted @   半条咸鱼  阅读(230)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示