展开
拓展 关闭
订阅号推广码
GitHub
视频
公告栏 关闭

Mybatis入门(二):@Param源码

  • 编写获取sqlSession的工具类
public class SqlSessionUtils {

    public static SqlSession getSqlSession(){
        SqlSession sqlSession = null;
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sqlSession;
    }

}

# 测试方法
    @Test
    public void testCheckLoginByParam(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        User user = mapper.checkLoginByParam("admin", "123456");
        System.out.println(user);
    }
  • MyBatis获取参数值的两种方式:${}和#{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号
但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
  • 获取单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型
此时xml映射文件中,可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号

User getUser(String name);

select * from user where username = #{name}
select * from user where username = #{aaa}
select * from user where username = '${name}'
select * from user where username = '${aaa}'
  • 多个字面量类型的参数
若mapper接口中的方法参数为多个时
此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1...为键,以参数为值;以
param1,param2...为键,以参数为值;因此只需要通过${}和#{}访问map集合的键就可以获取相对应的
值,注意${}需要手动加单引号

User getUser(String name, String pwd);

select * from user where username = #{arg0} and pwd = #{arg1}
select * from user where username = #{arg0} and pwd = #{param2}
select * from user where username = '${arg0}' and pwd = '${param2}'
  • map集合类型的参数
# 之前都是mybatis自动将数据放入map集合,我们也可以手动将数据放入map集合
若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中
只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

map.put("username", "goudan");
map.put("pwd", "123");

User getUser(Map<String, Object> map);

select * from user where username = #{username} and pwd = #{pwd}
  • 实体类类型的参数
若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号

int addUser(User user);

insert into user values(#{id}, #{name}, #{price})
  • 使用@Param标识参数
可以通过@Param注解标识mapper接口中的方法参数
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以
param1,param2...为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取相对应的值,
注意${}需要手动加单引号

User getUser(@Param("name") String name, @Param("pwd") String pwd);

select * from user where username = #{name} and pwd = #{pwd}
  • 查看@Param源码
# 打开MapperProxy.java

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

# 查看invoke方法
  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);
    }
  }

# 进入execute方法
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {          // 判断sql的类型是否是insert
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {          // 判断sql的类型是否是update
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {          // 判断sql的类型是否是delete
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:          // 判断sql的类型是否是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);          // 将方法的参数转换为sql语句的参数
          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;
  }

# 查看convertArgsToSqlCommandParam方法
    public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }

# 再进入getNamedParams方法
  public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();        // 查看name的来源
    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 {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        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
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

# 查看name的来源
  private final SortedMap<Integer, String> names;

  private boolean hasParamAnnotation;

  public ParamNameResolver(Configuration config, Method method) {
    this.useActualParamName = config.isUseActualParamName();
    final Class<?>[] paramTypes = method.getParameterTypes();      // 获取参数的类型
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();      // 获取参数的注解
    final SortedMap<Integer, String> 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) {      // 如果注解是Param,则将hasParamAnnotation 设置为true
          hasParamAnnotation = true;
          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);      // 最后将将索引作为key,值作为value
    }
    names = Collections.unmodifiableSortedMap(map);
  }

# 回到getNamedParams方法
  public Object getNamedParams(Object[] 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 {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {        // 创建map集合
        param.put(entry.getValue(), args[entry.getKey()]);      // 往map集合放参数名称,以及真实参数
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
posted @ 2022-04-24 22:32  DogLeftover  阅读(105)  评论(0编辑  收藏  举报