Mybatis,动态代理CRUD源码分析

获取XxxMapper对象

执行增删改查 ,调用queryStudentById(1)如上图可以看到进入到一个invoke()方法中  invoke()方法在MapperProxy类里,该类实现了InvocationHandler接口

 MapperProxy/invoke()➜InvocationHandler : JDK动态代理接口
用到了动态代理模式:增删改查➜代理对象(MapperProxy对象)-➜代理对象帮我们“代理执行”增删改查➜
XxxMapper代理对象实际就是MapperProxy对象


mapperMethod.execute(sqlSession, args):实际调用增删改查的方法,依靠了sqlSession中的configuration和 executor. .还有调用queryStudentById(23)传递的参数args,

先来看MapperMethod对象:

cacheMapperMethod(method);方法:从缓存中拿数据:methodCache.get(method),如果缓存没有再去库里面那,让后put到缓存中:methodCache.put(method,mapperMethod)

 再看mapperMethod调用的execute()方法,传了当前的sqlSession对象和参数args=23


execute()方法先处理处理增删改查方法的参数args,赋值给param,再处理sqlSession

看如何处理args:method.convertArgsToSqLCommandParam(args)处理参数,我的queryStudentById(23),给的参数是23, 参数给了该方法

进入convertArgstoSqlCommandParam()方法

 看到该方法返回的是paramNameResolver类的方法getNamedParams(),所以真正执行参数的是getNamedParams()方法

如果参敷是0个,reutrun null ;如果参数是1个,返回第一个 ﹔如果有多个放到Map中

我的queryStudentById(23)方法参数是1 个所以返回第一个firstKey()

 参数搞定了,下一步就是执行SqlSession.selectOne()方法

 刚刚convertArgsToSqlCommandParam(args)方法处理了的形参args,现在来处理另一个形参:sqlSession

sqlSession调用selectOne()方法去执行,根据我上一篇的分析,我们知道sqlSession对象中保存了configuration和执行器executor.

selectOne()很好理解,就是查询一个,queryStudentById(23)根据id来查,所以只能查到一个

 进入selecOne()方法,

 如下图,可以看到selectOne(statement,parameter)又调用了selectList()方法:看蓝色背景的注解:该selsectList()方法的形参statement的值为“com.lyx.mybatis.dao.StudentMapper.queryStudentById”,形参parameter:23。

 继续进入selectLIst()方法,看看selectList()方法

 如上图,configuration.getMappedStatement(statement)方法获取了一个MappedStatement   ms,即获取到了一个增删改查的对象

【下面可以关注一下该ms对象一直在被传递,看看真正使用ms对象的是哪个方法】

来看下一步,executor.query( ms,this.wrapCollection(parameter),.....)该方法套方法,所以先执行wrapCollection( )方法,先进入该方法,如下:我的参数是23不是collection类型,所以返回了object=23;

 

 出来以后,继续执行进入selectList()方法中的executor.query(ms,param,....)方法

 看到一个query()方法调用了getBoundSql()方法,进入getBoundSql()方法看看如何执行

 得出结论:

boundSql:是将我们写的  SQL  和   参数值  进行了拼接后  的对象,即最终能被真正执行的SQL。

下面回到query()方法进行执行,下面执行createCacheKey(ms,param,.....)方法,createCacheKey()支持不同级别的缓存,默认是二级缓存

 继续执行

当前query()方法是selectList()方法里面的executor.query()里面又一个query()方法,该query(ms,param,....)方法返回值如下

 

 ctrl键,发现delegate是Executor类型

再次进入

selectList()方法里面的executor.query()里面又一个query()方法的delegate.query(ms,param,....)方法

 可以看到调用了queryFromDatabase(ms,param,.....)方法,中文翻译过来就是从数据库里面查

 如果缓存中没有要查询的内容,则进入数据库 ,真实查询的是queryFromDatabase() 方法

进入queryFromDatabase()方法,看看如何从数据库查数据  

 

 进入

selectList()方法里面的executor.query()里面又一个query()方法的delegate.query()方法

的queryFromDatabse()方法中的doQuery()方法

 ms被调用:ms.getConfiguration()获取了Configuration对象:保存了所有的配置信息;

ms.getStatementLog()获取一个Log对象

StatementHandler对象:我们进入newStatementHandler方法中看看如何执行的:

 

 【重点】如上图:interceptorChain.pluginAll()方法拦截statementHandler,把statementHandler对象拦截,并增强,然后返回一个更强大的statementHandler

,我们再看prepareStatement()方法:

 

 this.getConnection(statementLog)获取了数据库连接

handler.prepare()方法第一个形参为连接conn,第二个是事务的超时时间

最后执行handler.parameterize(Statement statement);进入parameterize方法

  【重点】如下图,进入parameterize()方法后,执行下去,dalegate:PreparedStatementHandler

 进入delegate.parameterize()方法可以看到如下图,该parameterize方法经statement对象强转为PreparedStatement

可以得出结论:mybatis的底层使用的jdbc对象的PreparedStatement

prepareStatement执行完以后,

跳出来回到doQuery()方法,接下来执行handler.query()

 

 进入handler.query()方法

上面query()方法可以得出结论:mybatis底层执行增删改查是通过PreparedStatement的execute()方法

【重点】如下图:handler.query()方法返回DefaultResultSetHandler

 到此为止selectList()方法执行完毕,下一步return list.get(0)返回list集合的第一个元素

上面有StatementHandler➜ParameterHandler➜ResultSetHandler

以上处理器分别是处理对象的处理器:StatementHanler

处理参数的:ParameterHandler

处理结果集的:ResultSetHandler

还要一个类型转换的TypeHanlder)(例如把类的boolean类型属性转为int类型存入数据库)

 

回到:

 

mapper对象包含:

  1. sqlSession(configuration、executor、事务)
  2. mapperInterface,代理接口对象,值为com.lyx.mybatis.dao.StudentMapper,也就是我写的StudentMapper.java接口类
  3. methodCache:存放查询的缓存,同时methodCache的底层是ConcurrentHashMap。由于缓存里面有多个查询,可能出现多个线程同时查询,从而导致高并发问题,所以通过并发工具类:ConcurrentHashMap

 

 

 

 


版权声明:本文为m0_47010003原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。