傲渊山岳 发表于 2023-9-2 07:27:39

Mybatis学习记录

Mybatis学习笔记

今天开始复习一下mybatis的知识,虽然在学校的时候简单用过,但是随着工作中使用的是jpa就逐渐耽搁了。。。现在又要重新复习一下简单用法,顺便再浅浅地看一下源码。
mybatis其实是一种帮助我们转换java对象和数据库表的一种框架,所以我们直接通过一个例子来剖析mybatis是如何帮我们生成实际的sql语句并且返回结果的。
public void SessionTest() {
      try (InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml")) {
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user = new User();
            user.setId(1L);;
            user.setUserName(null);
            user.setPassword("America");
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User byCondition = mapper.selectByCondition(user);
            System.out.println(byCondition);

            User byCondition2 = mapper.selectByCondition(user);
            System.out.println(byCondition2);
            sqlSession.close();
      } catch (IOException e) {
            throw new RuntimeException(e);
      }
    }


//UserMapper
public interface UserMapper {
    User selectByCondition(User user);
}mybatis的用法和一些标签就不在这里介绍了,因为我也不怎么了解(在工作中不怎么使用,在这里主要看一下mybatis的核心功能)。从上面的示例代码可以看出,mybatis的查询逻辑多半就是在我们生成的SqlSession对象中,有同学看到了第一行代码
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml")这里就是使用mybatis中的一个Resources对象读取我们的一些配置,配置也可以贴出来,这个配置是我从官网直接粘贴的,把配置改成自己的。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="jdbc.properties"/>

    <environments default="development">
      <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
      </environment>
    </environments>
    <mappers>
      <mapper resource="mapper/UserMapper.xml"/>
    </mappers>

</configuration>这些是最基本的配置标签,不过已经足够我们来使用mybatis的查询功能咯。
通过读取配置并且得到了SqlSession后,可以看到
UserMapper mapper = sqlSession.getMapper(UserMapper.class);这里是通过jdk动态代理生成的我们定义的UserMapper代理类,时序图在下面(画得不是很好)
https://img2023.cnblogs.com/blog/1793443/202308/1793443-20230822020125972-1446251993.png
最底层获取到代理对象的一行代码如图所示
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}获取到代理对象之后,接着执行mapper中定义的方法就会执行sql语句了,这里要注意,UserMapper定义的方法始终是要对应到一个mapper文件里面真正想要执行的语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.UserMapper">
    <select id="selectUser" resultType="org.example.entity.User">
      select * from User where id = #{id}
    </select>

    <insert id="insertUser" parameterType="org.example.entity.User">
      insert into user values(#{id}, #{userName}, #{password})
    </insert>

    <select id="selectByCondition" parameterType="org.example.entity.User" resultType="org.example.entity.User">
      select * from user
      <where>
            <if test="id!=0">
                and id = #{id}
            </if>
            <if test="userName!=null">
                and userName = #{userName}
            </if>
      </where>
    </select>

</mapper>接下来就是查询方法的执行逻辑咯。
执行语句就是下面这一句
User byCondition = mapper.selectByCondition(user);上面我们知道了mybatis使用jdk动态代理帮我们生成了一个代理对象,jdk动态代理执行方法实际上就是执行了参数中继承了InvocationHandler的对象的invoke方法,也就是MapperProxy的invoke方法,我们看一下MapperProxy的invoke方法
@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 MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {
      if (m.isDefault()) {
          try {
            if (privateLookupInMethod == null) {
            return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
            return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
            | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
      } else {
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
}这里又调用了cacheInvoker方法构建了一个内部对象MapperMethodInvoker的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);
    }
}接着往下走,进入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);
          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;
}这里可以看到基本都是执行sqlSession的对应方法,而sqlSession相关的方法都是其内部的Executor来执行的。好像有点简单了,不过主要是为了给自己看哈哈哈哈,先记录这样吧。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Mybatis学习记录