Mybatis原理初探
开篇略谈
谈到Mybatis,对于我们猿们来说是熟悉不过了。但是有没有兴趣去探一下其实现原理呢?是的,请往下看 ↓ come on...
Mybatis综述
Mybatis一个数据持久层轻量级框架,回顾我们原始的开发即没有持久层框架的年代。话不多说上代码 ↓
Connection con = DriverManager.getConnection(url, "...", "..."); // 首先我们得获得一个数据库连接 Statement stmt = con.createStatement(); // 不管你是获得statement还是preparedStatement,总之在项目越来越大得时候这些代码会有点累赘// PreparedStatement prestmt = con.prepareStatement(...);
而我们得Mybatis则将其封装了起来,构成持久层框架,我们只要按照它的规定去配置就可以了,而无需在关注上面得代码,只需关注sql就行了,接下来入正题
Mybatis源码解析
提到Mybatis我们总会想起那个耳熟能详的东西sqlSession,是的,这是Mybatis的核心所在,sqlSession是由单例sqlSessionFactory创建而来的,而sqlSessionFactory又是由sqlSessionFactoryBuilder创建而来的,因此sqlSession得老祖宗就是它。在这里不管它的父辈,我们就来研究一下sqlSession的默认defaultSqlSession,多说无益看代码
// 可以看到 DefaultSqlSession 实现了SqlSession,这很容易让我们想起模板方法模式,请往下看 public class DefaultSqlSession implements SqlSession {// 下面是一些变量private Configuration configuration; // 这个东西是核心关注点也是此次重点讲解点,它包含了Mybatis的所有配置信息,我们此次所要研究的就是configuration是如何将sql获取到的private Executor executor;private boolean autoCommit;private boolean dirty;private List<Cursor<?>> cursorList;// 以下是对SqlSession方法的实现 @Overridepublic <T> T selectOne(String statement) {return this.<T>selectOne(statement, null);}@Overridepublic <T> T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.List<T> list = this.<T>selectList(statement, parameter);if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}}// 这个是获取configuration的mapper的,也就是获取sql语句的,这是重点讲解的 @Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);} public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); }
// 获取数据库连接 @Override public Connection getConnection() { try { return executor.getTransaction().getConnection(); } catch (SQLException e) { throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e); } } } // 而SqlSession又继承了Closeable,很明显Closeable就是关闭connection的,这里就不详细说明 public interface SqlSession extends Closeable { <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); .......此处省略,详情可自己看源码 }
以上是DefaultSqlSession的方法介绍,此次要从addMapper方法入手弄明白Mybatis它是如何获取我们写的SQL语句的,come on
// 以下是mapperRegistry public class MapperRegistry {public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<T>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse(); // 这是使用了MapperAnnotationBuilder的parse方法进行了解析,那么它是如何解析的呢,解析的又是什么?loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}..... 其他代码就省略了 }
接下来我们看它是如何解析的
public class MapperAnnotationBuilder {public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) { // 首先会判断这个接口即Mapper是否加载过了loadXmlResource(); // 这是对mapper的xml文件进行解析configuration.addLoadedResource(resource); // 加载进去 assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods(); // 获得当前Mapper接口的所有方法for (Method method : methods) {try {// issue #237if (!method.isBridge()) {parseStatement(method); // 对方法进行解析 }} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();} }
// 这个方法也是MapperAnotationBuilder的 void parseStatement(Method method) {Class<?> parameterTypeClass = getParameterType(method); // 获得参数类型LanguageDriver languageDriver = getLanguageDriver(method); SqlSource sqlSource = getSqlSourceFromAnnotations(method, // 这里是获取sqlSource parameterTypeClass, languageDriver);if (sqlSource != null) {Options options = method.getAnnotation(Options.class);final String mappedStatementId = type.getName() + "." + method.getName();Integer fetchSize = null;Integer timeout = null;StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;SqlCommandType sqlCommandType = getSqlCommandType(method);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;KeyGenerator keyGenerator;String keyProperty = "id";String keyColumn = null;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {// first check for SelectKey annotation - that overrides everything elseSelectKey selectKey = method.getAnnotation(SelectKey.class);if (selectKey != null) {keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);keyProperty = selectKey.keyProperty();} else if (options == null) {keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;} else {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();}} else {keyGenerator = NoKeyGenerator.INSTANCE;}if (options != null) {if (FlushCachePolicy.TRUE.equals(options.flushCache())) {flushCache = true;} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {flushCache = false;}useCache = options.useCache();fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348timeout = options.timeout() > -1 ? options.timeout() : null;statementType = options.statementType();resultSetType = options.resultSetType();}String resultMapId = null;ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);if (resultMapAnnotation != null) {String[] resultMaps = resultMapAnnotation.value();StringBuilder sb = new StringBuilder();for (String resultMap : resultMaps) {if (sb.length() > 0) {sb.append(",");}sb.append(resultMap);}resultMapId = sb.toString();} else if (isSelect) {resultMapId = parseResultMap(method);}assistant.addMappedStatement(mappedStatementId,sqlSource,statementType,sqlCommandType,fetchSize,timeout,// ParameterMapIDnull,parameterTypeClass,resultMapId,getReturnType(method),resultSetType,flushCache,useCache,// TODO gcode issue #577false,keyGenerator,keyProperty,keyColumn,// DatabaseIDnull,languageDriver,// ResultSetsoptions != null ? nullOrEmpty(options.resultSets()) : null);}}private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {try {Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method); // 这里会判断方法是否使用了select等注解Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method); // 是否使用了provider注解if (sqlAnnotationType != null) {if (sqlProviderAnnotationType != null) {throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());}Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);return buildSqlSourceFromStrings(strings, parameterType, languageDriver);} else if (sqlProviderAnnotationType != null) {Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);}return null;} catch (Exception e) {throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);}} private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {try {Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);if (sqlAnnotationType != null) {if (sqlProviderAnnotationType != null) {throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());}Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);return buildSqlSourceFromStrings(strings, parameterType, languageDriver);} else if (sqlProviderAnnotationType != null) {Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);}return null;} catch (Exception e) {throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);}}
从上面两个方法我们可以解释为什么我们使用provider和直接在mapper接口方法上加select语句会有效
Mybatis原理初探相关推荐
- 2021年大数据Flink(九):Flink原理初探
Flink原理初探 Flink角色分工 在实际生产中,Flink 都是以集群在运行,在运行的过程中包含了两类进程. JobManager: 它扮演的是集群管理者的角色,负责调度任务.协调 checkp ...
- C#之CLR内存原理初探
C#之CLR内存原理初探 投稿:shichen2014 字体:[增加 减小] 类型:转载 时间:2014-08-04 我要评论 这篇文章主要介绍了C#之CLR内存原理初探,有助于读者进一步理解C#的运 ...
- 《深入理解mybatis原理》 MyBatis缓存机制的设计与实现
本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论. MyBatis将数据缓存设计成两级结构,分为一级缓存 ...
- SpringBoot运行原理初探
运行原理初探 其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件! <parent><groupId>org.springframework.boot</grou ...
- 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)
文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ...
- mybatis 原理_深入理解MyBatis原理 MyBatis数据源与连接池
点击上方"程序开发者社区"关注,选择"设为星标" 第一时间送达实用干货 对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文 ...
- 5.Flink原理初探\角色分工\执行流程图生成\DataFlow,Operator,Partition,Parallelism,SubTask\OperatorChain和Task\任务槽\槽共享
本文来自:Flink1.12-2021黑马程序员贺岁视频 的学习笔记 5.Flink原理初探 5.1.角色分工 5.2.执行流程 5.3.DataFlow 5.3.1.DataFlow.Operato ...
- java解析sql语句简书,Mybatis原理解析(一)--java.sql数据库操作的基本实现方式
在研究Mybatis原理之前,先看一下java中是如何实现一次对数据库的访问的: public void testSql() { Connection connection = null; State ...
- 【mybatis原理工作原理】
文章目录 mybatis原理: mybatis缓存机制 mybatis原理: mybatis的工作原理就是:先封装sql,接着调用jdbc操作数据库,最后把数据库返回的表结果封装成java类. 通过代 ...
最新文章
- centos6.4安装nagios—4.0.8
- 进程间通信的方式(三):消息队列
- Docker实战:Docker安装部署RabbitMQ
- mysql主从复制周期_Mysql主从复制的实现
- ubuntu18安装微信
- Redis学习笔记001---Windows下安装Redis
- Android 调试技巧之快速重启生效
- Oracle Database Documentation
- 07、基于ADC0808/ADC0809的多通道电压采集程序设计
- 解决 error: Raw kernel process exited code: 3221226505
- EXFAT文件系统DBR的完美恢复
- MIT 线性代数导论 第十九、二十讲:行列式公式、代数余子式、克拉默法则
- java是牌子的眼镜多少钱一副_一副好眼镜的成本都去哪儿了?
- linux之ls -l命令详解
- 递归概述与递归能解决的问题和规则 [数据结构][Java]
- Excel在统计分析中的应用—第二章—描述性统计-分组数据的中位数的求解方法(组离散数据)
- 手机图片压缩大小的方法,用什么软件压缩
- mysql 汉字字母拼音_mysql 汉字按拼音字母排序、获取拼音首字母、拼音全拼
- 对象(构造函数)和类(class)
- 【zephyr】apds9660 接近(Proximity)传感器 驱动模型实现方式(一)