首页 > 基础资料 博客日记

Mybatis学习记录

2023-08-29 02:16:40基础资料围观283

这篇文章介绍了Mybatis学习记录,分享给大家做个参考,收藏Java资料网收获更多编程知识

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代理类,时序图在下面(画得不是很好)

最底层获取到代理对象的一行代码如图所示

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来执行的。好像有点简单了,不过主要是为了给自己看哈哈哈哈,先记录这样吧。


文章来源:https://www.cnblogs.com/ykmStudy/p/yukangming_learning_com.html
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云