Mybatis学习记录

打印 上一主题 下一主题

主题 938|帖子 938|积分 2814

Mybatis学习笔记

今天开始复习一下mybatis的知识,虽然在学校的时候简单用过,但是随着工作中使用的是jpa就逐渐耽搁了。。。现在又要重新复习一下简单用法,顺便再浅浅地看一下源码。
mybatis其实是一种帮助我们转换java对象和数据库表的一种框架,所以我们直接通过一个例子来剖析mybatis是如何帮我们生成实际的sql语句并且返回结果的。
  1. public void SessionTest() {
  2.         try (InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml")) {
  3.             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
  4.             SqlSession sqlSession = sqlSessionFactory.openSession();
  5.             User user = new User();
  6.             user.setId(1L);;
  7.             user.setUserName(null);
  8.             user.setPassword("America");
  9.             UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  10.             User byCondition = mapper.selectByCondition(user);
  11.             System.out.println(byCondition);
  12.             User byCondition2 = mapper.selectByCondition(user);
  13.             System.out.println(byCondition2);
  14.             sqlSession.close();
  15.         } catch (IOException e) {
  16.             throw new RuntimeException(e);
  17.         }
  18.     }
  19. //UserMapper
  20. public interface UserMapper {
  21.     User selectByCondition(User user);
  22. }
复制代码
mybatis的用法和一些标签就不在这里介绍了,因为我也不怎么了解(在工作中不怎么使用,在这里主要看一下mybatis的核心功能)。从上面的示例代码可以看出,mybatis的查询逻辑多半就是在我们生成的SqlSession对象中,有同学看到了第一行代码
  1. InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml")
复制代码
这里就是使用mybatis中的一个Resources对象读取我们的一些配置,配置也可以贴出来,这个配置是我从官网直接粘贴的,把配置改成自己的。
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3.         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4.         "https://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6.     <properties resource="jdbc.properties"/>
  7.     <environments default="development">
  8.         <environment id="development">
  9.             <transactionManager type="JDBC"/>
  10.             <dataSource type="POOLED">
  11.                 <property name="driver" value="${jdbc.driver}"/>
  12.                 <property name="url" value="${jdbc.url}"/>
  13.                 <property name="username" value="${jdbc.username}"/>
  14.                 <property name="password" value="${jdbc.password}"/>
  15.             </dataSource>
  16.         </environment>
  17.     </environments>
  18.     <mappers>
  19.         <mapper resource="mapper/UserMapper.xml"/>
  20.     </mappers>
  21. </configuration>
复制代码
这些是最基本的配置标签,不过已经足够我们来使用mybatis的查询功能咯。
通过读取配置并且得到了SqlSession后,可以看到
  1. UserMapper mapper = sqlSession.getMapper(UserMapper.class);
复制代码
这里是通过jdk动态代理生成的我们定义的UserMapper代理类,时序图在下面(画得不是很好)

最底层获取到代理对象的一行代码如图所示
  1. protected T newInstance(MapperProxy<T> mapperProxy) {
  2.     return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  3.   }
复制代码
获取到代理对象之后,接着执行mapper中定义的方法就会执行sql语句了,这里要注意,UserMapper定义的方法始终是要对应到一个mapper文件里面真正想要执行的语句
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.         "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="org.example.mapper.UserMapper">
  6.     <select id="selectUser" resultType="org.example.entity.User">
  7.         select * from User where id = #{id}
  8.     </select>
  9.     <insert id="insertUser" parameterType="org.example.entity.User">
  10.         insert into user values(#{id}, #{userName}, #{password})
  11.     </insert>
  12.     <select id="selectByCondition" parameterType="org.example.entity.User" resultType="org.example.entity.User">
  13.         select * from user
  14.         <where>
  15.             <if test="id!=0">
  16.                 and id = #{id}
  17.             </if>
  18.             <if test="userName!=null">
  19.                 and userName = #{userName}
  20.             </if>
  21.         </where>
  22.     </select>
  23. </mapper>
复制代码
接下来就是查询方法的执行逻辑咯。
执行语句就是下面这一句
  1. User byCondition = mapper.selectByCondition(user);
复制代码
上面我们知道了mybatis使用jdk动态代理帮我们生成了一个代理对象,jdk动态代理执行方法实际上就是执行了参数中继承了InvocationHandler的对象的invoke方法,也就是MapperProxy的invoke方法,我们看一下MapperProxy的invoke方法
  1. @Override
  2.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3.     try {
  4.       if (Object.class.equals(method.getDeclaringClass())) {
  5.         return method.invoke(this, args);
  6.       } else {
  7.         return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
  8.       }
  9.     } catch (Throwable t) {
  10.       throw ExceptionUtil.unwrapThrowable(t);
  11.     }
  12.   }
  13. private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
  14.     try {
  15.       return MapUtil.computeIfAbsent(methodCache, method, m -> {
  16.         if (m.isDefault()) {
  17.           try {
  18.             if (privateLookupInMethod == null) {
  19.               return new DefaultMethodInvoker(getMethodHandleJava8(method));
  20.             } else {
  21.               return new DefaultMethodInvoker(getMethodHandleJava9(method));
  22.             }
  23.           } catch (IllegalAccessException | InstantiationException | InvocationTargetException
  24.               | NoSuchMethodException e) {
  25.             throw new RuntimeException(e);
  26.           }
  27.         } else {
  28.           return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  29.         }
  30.       });
  31.     } catch (RuntimeException re) {
  32.       Throwable cause = re.getCause();
  33.       throw cause == null ? re : cause;
  34.     }
  35.   }
复制代码
这里又调用了cacheInvoker方法构建了一个内部对象MapperMethodInvoker的invoke方法,该对象有两个实现列,我们选择其中一个看一下。
  1. private static class PlainMethodInvoker implements MapperMethodInvoker {
  2.     private final MapperMethod mapperMethod;
  3.     public PlainMethodInvoker(MapperMethod mapperMethod) {
  4.       super();
  5.       this.mapperMethod = mapperMethod;
  6.     }
  7.     @Override
  8.     public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
  9.       return mapperMethod.execute(sqlSession, args);
  10.     }
  11.   }
复制代码
接着往下走,进入MapperMethod的execute方法
  1. public Object execute(SqlSession sqlSession, Object[] args) {
  2.     Object result;
  3.     switch (command.getType()) {
  4.       case INSERT: {
  5.         Object param = method.convertArgsToSqlCommandParam(args);
  6.         result = rowCountResult(sqlSession.insert(command.getName(), param));
  7.         break;
  8.       }
  9.       case UPDATE: {
  10.         Object param = method.convertArgsToSqlCommandParam(args);
  11.         result = rowCountResult(sqlSession.update(command.getName(), param));
  12.         break;
  13.       }
  14.       case DELETE: {
  15.         Object param = method.convertArgsToSqlCommandParam(args);
  16.         result = rowCountResult(sqlSession.delete(command.getName(), param));
  17.         break;
  18.       }
  19.       case SELECT:
  20.         if (method.returnsVoid() && method.hasResultHandler()) {
  21.           executeWithResultHandler(sqlSession, args);
  22.           result = null;
  23.         } else if (method.returnsMany()) {
  24.           result = executeForMany(sqlSession, args);
  25.         } else if (method.returnsMap()) {
  26.           result = executeForMap(sqlSession, args);
  27.         } else if (method.returnsCursor()) {
  28.           result = executeForCursor(sqlSession, args);
  29.         } else {
  30.           Object param = method.convertArgsToSqlCommandParam(args);
  31.           result = sqlSession.selectOne(command.getName(), param);
  32.           if (method.returnsOptional()
  33.               && (result == null || !method.getReturnType().equals(result.getClass()))) {
  34.             result = Optional.ofNullable(result);
  35.           }
  36.         }
  37.         break;
  38.       case FLUSH:
  39.         result = sqlSession.flushStatements();
  40.         break;
  41.       default:
  42.         throw new BindingException("Unknown execution method for: " + command.getName());
  43.     }
  44.     if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
  45.       throw new BindingException("Mapper method '" + command.getName()
  46.           + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  47.     }
  48.     return result;
  49.   }
复制代码
这里可以看到基本都是执行sqlSession的对应方法,而sqlSession相关的方法都是其内部的Executor来执行的。好像有点简单了,不过主要是为了给自己看哈哈哈哈,先记录这样吧。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

傲渊山岳

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表