全面介绍Mybatis的历史以及发展

前言

这篇文章具体来说一下关于mybatis的历史发展,以及变更的区别,不同的项目所使用的技术框架都是不一样的,会相比较选择最适合的那一个,关于mybatis的前身和后期发展框架个人都是有用到的,以下就总结一下使用时的一个主要区别

简单介绍

自始至终,ibatis,mybatis,mybatisplus它们都是半ORM(对象关系映射)框架,它们的发展也是从越来越人性化,便利化方面去考虑,去衡量。

ibatis和mybatis实际上是比较相似的,主要是mybatis发展起来后,可以动态地拼接sql语句,非常人性化,更适合逻辑复杂的sql,于是ibatis相比较下,就比较不人性化设计了,它们之间的差别也主要是拼接SQL语句的差别。

mybatisplus和mybatis就不太相同了,mybatisplus是一个Mybatis的增强工具,它在Mybatis的基础上做了增强,不做改变,相当于扩展。所以如果开发过程中已经使用了mybatis框架,那么引用mybatisplus也不会对你现有的框架有影响。其实mybatisplus的存在就是在弥补mybatis的不足,mybatis需要我们自己手动在xml中写好sql语句,mybatisplus有着代码生成器,可以减少很多开发重复的工作,比如生成简单的增删改查数据库操作语句。

iBatis和MyBatis的区别

(1)参数关键字:

iBatis的传入参数关键字是:parameterClass,而MyBatis是可以不写的,也可以用parameterType;
iBatis的传出参数关键字是:resultClass,而MyBatis是resultMap。

iBatis:   
    <select id="selectDeviceByWhere" parameterClass="Map"  resultClass="BaseResultMap">  </select>
MyBatis:
    <select id="selectDeviceByWhere" parameterType="Map"  resultMap="BaseResultMap">   </select>

(2)接受参数

#预处理参数形式
$内联参数形式:SQL注入有机可乘,而且过度使用还会有性能问题,模糊查询使用 $ 比较方便

iBatis # #或者$ $
MyBatis# #=#{ }, $ $ = ${ }

(3)判断语句:

iBatis:
1)isNull:判断property字段是否是null

<isNull prepend="and" property="id">   </isNull>

2)isEqual相当于equals,判断状态值。

<isEqual property="state" compareValue="0">  </isEqual><isEqual property="state" compareProperty="nextState">  </isEqual>

3)isEmpty判断参数是否为Null或者空,满足其中一个条件则其true。
isNotEmpty相反,当参数既不为Null也不为空是其为true。

MyBatis:
只要在where 或者if 的标签里面添加test=""就可以了,里面写判断条件了

(4)MyBatis中一条sql结束后可以有“;”,而iBatis会报错

Mybatis-Plus和Mybatis的区别

详细请看:https://blog.csdn.net/qq_34508530/article/details/88943858
.在这里插入图片描述
在这里插入图片描述

常问的Mybatis问题

什么是Mybatis?

(1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高
(2)MyBatis 可以使用XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
(3)通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。
Mybaits的优点:
(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
(4)能够与Spring很好的集成;
(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
MyBatis框架的缺点:
(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis框架适用场合:
(1)MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择

MyBatis 中 #{}和 ${}的区别是什么?

#{}是预编译处理,${}是字符串替换。

Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。

使用#{}可以有效的防止SQL注入,提高系统安全性。

当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

 <select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”>
       select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
    </select>

第2种: 通过来映射字段名和实体类属性名的一一对应的关系。

<select id="getOrder" parameterType="int" resultMap="orderresultmap">
        select * from orders where order_id=#{id}
</select>
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
        <!–用id属性来映射主键字段–>
        <id property=”id” column=”order_id”>
        <!–用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性–>
        <result property = “orderno” column =”order_no”/>
        <result property=”price” column=”order_price” />
</reslutMap>

模糊查询like语句该怎么写?

在Java代码中添加sql通配符

string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”>
 select * from foo where bar like #{value}
</select>

MyBatis 有几种分页方式?

(1)逻辑分页: 使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。
(2)物理分页: 自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。
MyBatis 逻辑分页和物理分页的区别是什么?
逻辑分页是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题
MyBatis 分页插件的实现原理是什么?
分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种是使用 标签,逐一定义数据库列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

如何获取自动生成的(主)键值?

insert 方法总是返回一个int值 ,这个值代表的是插入的行数。
如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。

<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
     insert into names (name) values (#{name})
</insert>

Mybatis动态sql有什么用?执行原理?有哪些动态sql?

Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。
Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

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

<resultMap><parameterMap><sql><include><selectKey>,加上动态sql的9个标签,
其中
<sql>为sql片段标签,通过<include>标签引入sql片段,
<selectKey>为不支持自增的主键生成策略标签。

附:
在这里插入图片描述

Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

不同的Xml映射文件,如果配置了namespace,那么id可以重复;
如果没有配置namespace,那么id不能重复;
原因就是namespace+id是作为Map<String, MapperStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。
但是,在以前的Mybatis版本的namespace是可选的,不过新版本的namespace已经是必须的了。

一对一、一对多的关联查询 ?

<mapper namespace="com.lcb.mapping.userMapper">  

    <!--association  一对一关联查询 -->  
    <select id="getClass" parameterType="int" resultMap="ClassesResultMap">  
        select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}  
    </select>  
    <resultMap type="com.lcb.user.Classes" id="ClassesResultMap">  
        <!-- 实体类的字段名和数据表的字段名映射 -->  
        <id property="id" column="c_id"/>  
        <result property="name" column="c_name"/>  
        <association property="teacher" javaType="com.lcb.user.Teacher">  
            <id property="id" column="t_id"/>  
            <result property="name" column="t_name"/>  
        </association>  
    </resultMap>  

    
    <!--collection  一对多关联查询 -->  
    <select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">  
        select * from class c,teacher t,student s where c.teacher_id=t.t_id and 		c.c_id=s.class_id and c.c_id=#{id}  
    </select>  
    <resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">  
        <id property="id" column="c_id"/>  
        <result property="name" column="c_name"/>  
        <association property="teacher" javaType="com.lcb.user.Teacher">  
            <id property="id" column="t_id"/>  
            <result property="name" column="t_name"/>  
        </association>  
 
        <collection property="student" ofType="com.lcb.user.Student">  
            <id property="id" column="s_id"/>  
            <result property="name" column="s_name"/>  
        </collection>  
    </resultMap>  
  
</mapper> 

MyBatis实现一对一有几种方式?具体怎么操作的?

有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置association节点配置一对一的类就可以完成;
嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。

MyBatis实现一对多有几种方式,怎么操作的?

有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。

Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。
在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

说一下 MyBatis 的一级缓存和二级缓存?

两者都是基于PerpetualCache 的 HashMap 本地缓存;
一级缓存:基于 PerpetualCache 的 HashMap 本地缓存,它的声明周期是和 SQLSession 一致的,有多个 SQLSession 或者分布式的环境中数据库操作,可能会出现脏数据。
当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认一级缓存是开启的
二级缓存:也是基于 PerpetualCache 的 HashMap 本地缓存,不同在于其存储作用域为 Mapper 级别的,如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存,并且二级缓存可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态)。
开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库
缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

使用MyBatis的mapper接口调用时有哪些要求?

①Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
②Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;
③Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
④Mapper.xml文件中的namespace即是mapper接口的类路径。

RowBounds 是一次性查询全部结果吗?为什么?

RowBounds 表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,
它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next()的时候,去查询更多的数据。就好比你去自动取款机取 10000 元,
但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当你调用 next()的时候会自动帮你完成查询工作。这样做的好处可以有效的防止内存溢出
Fetch Size 官方相关文档:http://t. cn/EfSE2g3

MyBatis 有哪些执行器(Executor)?

SimpleExecutor: 每执行一次 update 或 select 就开启一个 Statement 对象,用完立刻关闭 Statement 对象;
ReuseExecutor: 执行update 或 select,以 SQL 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后不关闭 Statement 对象,而是放置于 Map 内供下一次使用。
简言之,就是重复使用 Statement 对象;
BatchExecutor: 执行 update(没有 select,jdbc 批处理不支持 select),将所有 SQL 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),
它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理,与 jdbc 批处理相同。

MyBatis 如何编写一个自定义插件?

(1)自定义插件实现原理
MyBatis 自定义插件针对 MyBatis 四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)进行拦截:
Executor:拦截内部执行器,它负责调用 StatementHandler 操作数据库,并把结果集通过 ResultSetHandler 进行自动映射,另外它还处理了二级缓存的操作;
StatementHandler:拦截 SQL 语法构建的处理,它是 MyBatis 直接和数据库执行 SQL 脚本的对象,另外它也实现了 MyBatis 的一级缓存;
ParameterHandler:拦截参数的处理
ResultSetHandler:拦截结果集的处理

(2)自定义插件实现关键:
MyBatis 插件要实现 Interceptor 接口,接口包含的方法,如下:

	public interface Interceptor {   
   	  Object intercept(Invocation invocation) throws Throwable;       
   	  Object plugin(Object target);    
   	  void setProperties(Properties properties);
	}

setProperties 方法是在 MyBatis 进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置;
plugin 方法是插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,
官方提供了示例:return Plugin. wrap(target, this);
intercept 方法就是要进行拦截的时候要执行的方法

(3)自定义插件实现示例
官方插件实现:

@Intercepts({@Signature(type = Executor. class, method = "query",
        args = ({MappedStatement. class, Object. class, RowBounds. class, ResultHandler. class})})
public class TestInterceptor implements Interceptor {
   public Object intercept(Invocation invocation) throws Throwable {
     Object target = invocation. getTarget(); //被代理对象
     Method method = invocation. getMethod(); //代理方法
     Object[] args = invocation. getArgs(); //方法参数
     // do something . . . . . .  方法拦截前执行代码块
     Object result = invocation. proceed();
     // do something . . . . . . . 方法拦截后执行代码块
     return result;
   }
   public Object plugin(Object target) {
     return Plugin. wrap(target, this);
   }
}

1


  1. 编写不易,如果有帮助到的话,可以关注,点赞和收藏哦~ ↩︎

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值