热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Mybatis介绍以及面试问题

(一)Mybatis介绍MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apachesoftwarefoundation迁
(一)Mybatis介绍

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)
映射成数据库中的记录。

    Mybatis通过xml或注解的方式将要执行的statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。  

   (二)框架流程:

  1.加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。

  2.SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。

   3.SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。

   4.结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。

 

    (三)MyBatis优缺点

优点:

1、简单易学

       mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

2、灵活

       mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。

3、解除sql与程序代码的耦合

       通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

4、提供xml标签,支持编写动态sql。

    缺点:

1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。

2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。

3、二级缓存机制不佳

 (四)功能架构

    我们把Mybatis的功能架构分为三层:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。


(五)Mybatis工作原理

Mybatis原名Ibatis,在2011年从Ibatis2.x升级到Mybatis 3.X,并将项目地址从Apache迁移到了Google code,事实上我们看MyBatis的类全路径名,还是保留了Apache和Ibatis的的包前缀

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

不过MyBatis的配置文件以及操作类和实现方式都有了很大变化,这里我们重点讲述的是Mybatis,不是Ibatis;


Mybatis的配置文件一共由两类:

一类用于指定数据源、事务属性以及其他一些参数配置信息(通常是一个独立的文件,可以称之为全局配置文件);

另一类则用于 指定数据库表和程序之间的映射信息(可能不止一个文件,我们称之为映射文件)

这些文件的名字并没有确定的要求;只是要最从特定的dtd的xml文件约束,即xml标签需要符合要求;




上述就是MyBatis的数据源,事务属性,以及映射文件的索引;






上面是数据库表与程序之间的映射文件,定义了一个根据id来获取User对象的sql

package com.test.domain;/*** users表所对应的实体类*/
public class User {//实体类的属性和表的字段名称一一对应private int id;private String name;private int age;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User [id=" + id + ", name=" + name + ", age=" + age + "]";}
}

问题:

mybatis是怎么在程序中顺利的找到sqlmapper的,这个的流程是怎么样??

// mybatis的配置文件
String resource = "conf.xml";
// 使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
InputStream is = Test1.class.getClassLoader().getResourceAsStream(resource);
// 构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);

题主问的sqlmapper可以理解为两种组件,一种是mapping映射文件,通过id名来获取相应的sql语句,操作数据库;一种是sql的返回对象,

resultType="com.test.domain.User"

这个就是返回的sql结果映射成为具体的POJO(Plain Ordinary Java Object)对象;

两个重要的类即:

org.apache.ibatis.session.SqlSessionFactory;

org.apache.ibatis.session.SqlSession;

package org.apache.ibatis.session;import java.sql.Connection;public interface SqlSessionFactory {SqlSession openSession();SqlSession openSession(boolean autoCommit);SqlSession openSession(Connection connection);SqlSession openSession(TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType);SqlSession openSession(ExecutorType execType, boolean autoCommit);SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType, Connection connection);Configuration getConfiguration();}

在构建SqlSessionFactory类的时候,将会对数据源及事务配置进行解析,具体在

org.apache.ibatis.builder.xml.XMLConfigBuilder类

org.apache.ibatis.builder.BaseBuilder类

XMLConfigBuilder类是解析产生org.apache.ibatis.Session.Configuration类的的具体类,Configuration类中将保存中所有的配置;

mybatis的源代码解析(1)--xml文件解析 - 王久勇 - 博客园

这篇博客介绍了一些xml文件解析的基本;

具体mybatis的xml解析使用到了XPath方式,具体解析过程参看

zhuanlan.zhihu.com/p/31

其实一般各种轮子都会有一个解析XML后信息的专用存储类,比如Config.Java,xxxConf.java,都是在启动组件时解析XML配置以用作程序中使用的。


通过跟踪源代码可以看到SqlSession通过mapper映射的id来查找数据的方法;

org.apache.ibatis.session.defaults.DefaultSqlSession类

public List selectList(String statement, Object parameter, RowBounds rowBounds)
{try{MappedStatement ms = configuration.getMappedStatement(statement);List result = executor. query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);return result;}catch (Exception e){throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);}finally{ErrorContext.instance().reset(); }
}

org.apache.ibatis.session.Configuration类

public MappedStatement getMappedStatement(String id)
{return this.getMappedStatement(id, true);
}


protected final Map mappedStatements =
new StrictMap("Mapped Statements collection");


public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements)
{if (validateIncompleteStatements){buildAllStatements();}return mappedStatements.get(id);
}

其实就是根据一个map映射,key就是定义mapping时候的id来拿到的;

上述org.apache.ibatis.session.defaults.DefaultSqlSession类对象中的 selectList方法中的executor对象,

在默认情况下,即没有设置settings的cache和executor属性时,默认使用的

org.apache.ibatis.executor.CachingExecutor类

public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit)
{executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType){executor = new BatchExecutor(this, transaction);}else if (ExecutorType.REUSE == executorType){executor = new ReuseExecutor(this, transaction);}else{executor = new SimpleExecutor(this, transaction);}if (cacheEnabled){executor = new CachingExecutor(executor, autoCommit);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;
}


所以调用到了

public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)throws SQLException
{BoundSql boundSql = ms.getBoundSql(parameterObject);CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

在真正查询时先查询cache,可以看到这个cache层级在MappedStatement上,也就是在单个Sql上;若查到,则直接返回,无则通过jdbc查询,且返回结果

public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,CacheKey key, BoundSql boundSql) throws SQLException
{Cache cache = ms.getCache();if (cache != null){flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null){ensureNoOutParams(ms, key, parameterObject, boundSql);if (!dirty){cache.getReadWriteLock().readLock().lock();try{@SuppressWarnings("unchecked")List cachedList = (List) cache.getObject(key);if (cachedList != null)return cachedList;}finally{cache.getReadWriteLock().readLock().unlock();}}List list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578. Query must be// not synchronized to// prevent deadlocksreturn list;}}return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

上述的使用方式是未使用代理的方式,这样需要我们自行openSession并且关闭Session;

SqlSession session = null;
try
{session = sessionFactory.openSession();/*** 映射sql的标识字符串, com.test.mapping.userMapper是userMapper.* xml文件中mapper标签的namespace属性的值,* getUser是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL*/String statement = "com.test.mapping.userMapper.getUser";// 映射sql的标识字符串// 执行查询返回一个唯一user对象的sqlUser user = session.selectOne(statement, 1);System.out.println(user);
}
catch (Exception e)
{// TODO: handle exception
}
finally
{if (session != null){session.close();}
}

事实上如果我们使用SqlSessionManager来管理,那么开启和关闭Session操作都不用我们来处理了。

final SqlSessionManager sqlSessionManager = SqlSessionManager.newInstance(sessionFactory);
String statement = "com.test.mapping.userMapper.getUser";// 映射sql的标识字符串
User user = sqlSessionManager.selectOne(statement, 1);
System.out.println(user);

下面是Interceptor类实现,开启和关闭操作都交由了

private class SqlSessionInterceptor implements InvocationHandler
{public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();if (sqlSession != null){try{return method.invoke(sqlSession, args);}catch (Throwable t){throw ExceptionUtil.unwrapThrowable(t);}}else{final SqlSession autoSqlSession = openSession();try{final Object result = method.invoke(autoSqlSession, args);autoSqlSession.commit();return result;}catch (Throwable t){autoSqlSession.rollback();throw ExceptionUtil.unwrapThrowable(t);}finally{autoSqlSession.close();}}}
}

如果使用Mapper方式来操作SQL,就是利用动态代理,可以避免我们手写mapper的id字符串,将查找sql过程和执行sql过程放到了代理处理中,更优雅些,不过大体流程就是这些,改变了查找sql的步骤,通过Mapper的方法名来查找对应的sql的,


 

(五)常见的Mybatis面试问题:


1#{}${}的区别是什么?

答:${}Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver#{}sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql?号占位符设置参数值,比如ps.setInt(0,parameterValue),#{item.name}的取值方式为使用反射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()

2Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签?

答:还有很多其他的标签,,加上动态sql9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签。

3、最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

答:Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatementid值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespacecom.mybatis3.mappers.StudentDao下面id= findStudentByIdMappedStatement。在Mybatis中,每一个标签均会被解析为MappedStatement对象,标签内的sql会被解析为BoundSql对象。

18、为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

答:Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

面试题看似都很简单,但是想要能正确回答上来,必定是研究过源码且深入的人,而不是仅会使用的人或者用的很熟的人,以上所有面试题及其答案所涉及的内容,在我的Mybatis系列博客中都有详细讲解和原理分析

19 Mybatis对象关联实例:

    在单表查询中,属性名和数据库相同的字段可以省略,多表不可省略,省略则为空

    • // 一对一
      <resultMap type&#61;"Orders" id&#61;"orders"><result column&#61;"id" property&#61;"id"/><association property&#61;"user" javaType&#61;"User">//关联另一张表<id column&#61;"id" property&#61;"id"/> // id<result column&#61;"name" property&#61;"name">result>
      // 属性 即查询出来显示的名字association>
      resultMap><select id&#61;"onemany" resultMap&#61;"orders">select u.id ,o.number,o.dic,u.name from orders o left JOIN user u on o.user_id&#61;u.id
      select>

      // 一对多
      <resultMap type&#61;"User" id&#61;"user"><id column&#61;"id" property&#61;"id"/><result column&#61;"name" property&#61;"name"/><collection property&#61;"list2" ofType&#61;"Orders"> // ofType : 每个属性的类型<id column&#61;"oid" property&#61;"id"/> //需要设置id,如果两个对象属性相同,则会视为一条记录<result column&#61;"number" property&#61;"number"/>collection>

      resultMap>
      <select id&#61;"selectUserList" resultMap&#61;"user">select u.id,o.id as oid,o.number,o.dic,u.name from user u left JOIN orders o on o.user_id&#61;u.id
      select>

  1. resultType resultMap的区别

    • 类的名字和数据库相同时&#xff0c;可以直接设置resultType参数为Pojo类

    • 若不同&#xff0c;需要设置resultMap 将结果名字和Pojo名字进行转换&#xff0c;

  2. Map 直接#{key}就可以取得对应的值

&#xff08;20&#xff09;  在mapper中如何传递多个参数

  直接在方法中传递参数&#xff0c;xml文件用#{0} #{1}来获取

  使用 &#64;param 注解:这样可以直接在xml文件中通过#{name}来获取

&#xff08;21&#xff09;如何获取自动生成的(主)键值
配置文件设置usegeneratedkeys 为true



参考&#xff1a;&#xff08;1&#xff09;百度百科

         &#xff08;2&#xff09;https://www.zhihu.com/question/25007334/answer/266187562

         &#xff08;3&#xff09;https://www.cnblogs.com/huajiezh/p/6415388.html

         &#xff08;4&#xff09;https://www.cnblogs.com/liyuhui-Z/p/7832866.html



推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 如何实现JDK版本的切换功能,解决开发环境冲突问题
    本文介绍了在开发过程中遇到JDK版本冲突的情况,以及如何通过修改环境变量实现JDK版本的切换功能,解决开发环境冲突的问题。通过合理的切换环境,可以更好地进行项目开发。同时,提醒读者注意不仅限于1.7和1.8版本的转换,还要适应不同项目和个人开发习惯的需求。 ... [详细]
  • 2016 linux发行版排行_灵越7590 安装 linux (manjarognome)
    RT之前做了一次灵越7590黑苹果炒作业的文章,希望能够分享给更多不想折腾的人。kawauso:教你如何给灵越7590黑苹果抄作业​zhuanlan.z ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
author-avatar
冰的简笔画
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有