天天看点

MyBatis执行一次数据库操作的原理以及顺序

1、初始化SqlSessionFactory,默认实现是DefultSqlSessionFactory,这个一般是一个应用一个实例就够了,例如单例模式; 2、通过SqlSessionFactory获得SqlSession,默认实现是DefaultSqlSession,这个应用范围是一次数据库操作,可以简单理解为JDBC操作的Connection,数据库操作执行完,需要调用close方法手动关闭连接; 3、通过SqlSession可以直接执行数据库操作,这个接口已经定义了许多的常用的操作,例如查询一个对象,查询列表数据,更新操作等等,这里先不做详细介绍。现在MyBatis经常就是跟Spring进行结合使用,MyBatis比ibatis最显著的一个升级,应该就是MyBatis可以不用写Dao接口的实现类,直接通过SqlSession.getMapper(Class clz)方法获得Dao接口的代理类(通过Spring的IOC框架管理Dao接口,实际上依赖注入的时候也是通过MyBatis内部的该方法获得实际的Dao代理对象); 4、生成的Dao接口的代理类是通过MapperProxy.newMapperProxy(Class daoInterface,SqlSession sqlSession)生成的,这个是个静态方法,实现原理是使用了JDK的动态代理机制。SO,MapperProxy是实现了InvocationHandler的类,执行数据库操作的入口就是MapperProxy的invoke方法触发的,再看下该方法,可以知道最终是通过MapperMethod.execute(Object[] params)执行数据库操作; 5、在MapperMethod的构造方法里面,会进行被代理接口的执行的那个方法的信息的读取,比如方法名称(以便后来对应到具体的配置文件的具体语句sqlId)、方法参数(以便对具体的sql语句进行参数替换)等等。然后看下execute方法,根据刚刚初始化的配置信息,判断该次执行是select还是update或者delete操作,转了半天发现,最终还是调用了SqlSession的select、update等方法; 6、好吧,现在回到 DefaultSqlSession,以一次查询为例子,最终执行的方法是selectList( String statement, Object parameter, RowBounds rowBounds),参数分别是接口对应的方法名称(会对应到具体的sql上),接口方法参数,返回记录范围(一般没啥用了,分页啥的都是分页语句实现了,不会把所有数据都查出来然后再进行数据范围的筛选)。然后发现最终执行查询的是Executor的query方法,这个执行器是DefaultSqlSession的一个成员变量,默认实现是SimpleExecutor,继承了BaseExecutor,query方法就是在该基类中的。 7、现在到BaseExecutor的query( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)方法里面来,这个是执行查询的入口,这里的第一个参数要说明下,MapperStatement,直接翻译就是映射声明,是对映射信息的封装对象,用来存储记录要映射的sql语句的id、sql语句、传参等等,这个类有个比较重要的方法就是getBoundSql( Object  parameterObject),也就是获取绑定的sql,如果要开发一个分页的插件,要利用反射机制,对这个BoundSql进行修改。 8、再回到BaseExecutor的query( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)方法里面来,可以看到最终是调用了内部的query( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)方法,在该方法里面发现,如果这次查询是有缓存,则只需从缓存里面取出结果集返回,那这次的查询就结束了。如果没有缓存,那就从数据库里面查询,层层跟踪下去,发现执行的是一个抽象方法doQuery( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql),这个方法是由子类实现的,也就是说,我们终于可以回到SimpleExecutor了; 9、回到SimpleExecutor的doQuery( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql),在这个方法里面,会选择具体的执行StatementHandler,这是个接口,是通过Configuration的newStatementHandler( Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)方法获取具体的处理器,并且根据配置文件中的插件,加工StatementHandler,实际上使用的也是JDK的代理机制,如果有添加插件的话,这个StatementHandler也是个代理对象(最常见的分页插件); 10、介绍下StatementHandler,看见Statement,想必有用过java的JDBC操作的都很熟悉这个名字,那实际上Statementhandler就是MyBatis的自己的Statement,用来对数据库进行操作用的。这个接口的是实现类有5个: RoutingStatementHandler, SimpleStatementHandler, PreparedStatementHandler, CallableStatementHandler,BaseStatementHandler。

  • RoutingStatementHandler:StatementHandler的路由选择,根据配置文件里面的信息,判断具体选择SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler三个里面的哪个StatementHandler,它有个成员变量StatemenHandler delegate,这个delegate就是选择的具体的StatementHandler;
  • SimpleStatementHandler:即使用JDBC的Statement进行数据库操作;
  • PreparedStatementHandler:即使用JDBC的PreparedStatement进行数据库操作,一般的配置都是使用这个,预处理语句进行数据库操作;
  • CallableStatementHandler:即使用JDBC的CallableStatement进行数据库操作;
  • BaseStatementHandler:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler的父类。

11、现在获取了的具体的StatementHandler后,我们以常用的 PreparedStatementHandler为例,然后就是根据配置的信息,参数等对这个handler内部的PreparedStatement进行操作了,也就是JDBC中我们经常做的参数set啥的了,这里就不做介绍了,可以认为StatementHandler就是对JDBC的Statement 以符合MyBatis配置的要求 进行一次包装,最终对数据库进行操作的,还是JDBC中的Statement( PreparedStatement继承自 Statement )。 12、数据库操作就执行结束了,然后就是关闭连接了,就不说了~ PS:大家可以结合该文章,对源代码进行跟踪,可以更方便的理解其中的原理