Mybatis面试总结
一、Mybatis的优缺点?
优点:
- 基于sql编程,相当灵活,不会对应用程序或者数据库现有的设计造成任何影响,sql写在xml里面,解耦了sql和代码,便于维护和统一管理,提供了xml标签、支持编写动态sql语句,并可以重用。
- 提供了标签映射,支持对象与数据库的ORM字段关系映射
- 与JDBC相比消除了冗余代码,不需要手动开关连接
- 很好的与各种数据库兼容,与Spring很好的集成
缺点:
- sql语句编写工作量较大,对开发人员编写sql的功底有一定的要求。
- sql语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
二、Mybatis和Hibernate对比?
- 开发速度:
- 如果项目中用到的复杂查询基本没有,就是简单的CRUD,这样选择Hibernate效率就很快,因为纯ORM框架基本的sql语句已经被封装好了,根本不需要去写sql,这样就节约了大量的时间,但是想对于一个大型项目,有复杂的业务场景(复杂的sql语句较多),选择Mybatis就会快很多,而且语句管理维护比较方便。
- 开发工作量:
- Mybatis和Hibernate都有相应的代码生成工具,可以生成简单的DAO层方法,针对高级复杂查询Mybatis需要手写sql,而Hibernate有良好的映射机制,开发者无需关心sql的生成与结果映射,可以更加专注于业务流程开发。
- sql优化方面:
- Hibernate的查询是会将所有的字段都查询出来,这一点会消耗性能,Hibernate也可以手动编写sql来指定查询字段,但是这样破坏了Hibernate开发的简洁性,Mybatis的sql是手动编写的,所以可以按需查询字段
- 对象管理:
- Hibernate是完整的ORM对象/关系映射解决方案,它提供了对象的状态管理功能(state
management),使开发者不需要理会底层数据库细节。而Mybatis需要开发者对对象进行详细的管理
- Hibernate是完整的ORM对象/关系映射解决方案,它提供了对象的状态管理功能(state
- 缓存机制:
- 都可以使用自己的缓存或者使用其他三方的缓存方案
- Hibernate的二级缓存配置在sessionFactory生成的配置文件中进行配置,然后在具体的表-对象映射中配置是哪种缓存,Mybatis 二级缓存配置都是在每个具体的表-对象映射中进行配置,这样针对不同的表可以自定义不同的缓存机制。
当然SQL和ORM的争论永远不会停止。
三、#{}和${} 区别?
- #{}是预编译是占位符,${} 是字符串替换、拼接
- Mybatis在处理#{}时,会将sql中的#{} 替换为?号,调用ps来赋值
- Mybatis在处理 时 , 会 将 s q l 中 的 {}时,会将sql中的 时,会将sql中的{} 替换为变量的值
- #{} 替换后对应的变量字段加上单引号,${} 只是替换、拼接不会加上单引号
- #{} 可以有效的防止sql注入,提供系统安全性
四、Mybatis 缓存
Mybatis 中有一级缓存和二级缓存:
-
默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条 SQL。
-
第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一个
map(key:MapperID+offset+limit+Sql+所有的入参) -
同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作(修改、添加、删除),本 sqlsession 中的一级缓存区域全部清空,下次再去缓存中查询不到所以要从数据库查询,从数据库查询到再写入缓存。
-
-
二级缓存是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存,对于 mapper 级别的缓存不同的 sqlsession 是可以共享的。
- 二级缓存的范围是 mapper 级别(mapper 同一个命名空间),mapper 以命名空间为单位创建缓存数据结构,结构是
map(key:MapperID+offset+limit+Sql+所有的入参)。 - mybatis 的二级缓存是通过 CacheExecutor 实现的。CacheExecutor其实是 Executor 的代理对象。所有的查询操作,在 CacheExecutor 中都会先匹配缓存中是否存在,不存在则查询数据库。
- 具体使用需要配置:
- Mybatis 全局配置中启用二级缓存配置
- 在对应的 Mapper.xml 中配置 cache 节点
- 在对应的 select 查询节点中添加 useCache=true
- 二级缓存的范围是 mapper 级别(mapper 同一个命名空间),mapper 以命名空间为单位创建缓存数据结构,结构是
五、Mybatis插件的运行原理?如何编写一个插件
Mybatis只支持针对ParameterHander、ResultSetHander、StatementHander、Executor这4个接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法的拦截,每当执行这4个接口对象方法时候,就会进入代理对象的拦截方法,具体的就是InvocationHandler的 invoke()方法,拦截那些指定需要拦截的方法。
编写插件:实现Mybatis的interceptor接口并重写intercept()方法,然后在给插件编写注解,指定要拦截哪个接口的哪些方法即可,在配置文件中配置编写的插件。
六、说一下对Mybatis的连接池的理解
创建Connection过程如下:
-
初始化驱动: 判断driver驱动是否已经加载到内存中,如果还没有加载,则会动态地加载driver类,并实例化一个Driver对象,使用DriverManager.registerDriver()方法将其注册到内存中,以供后续使用。
-
创建Connection对象: 使用DriverManager.getConnection()方法创建连接。
-
配置Connection对象: 设置是否自动提交autoCommit和隔离级别isolationLevel。
-
返回Connection对象。
创建一个Connection对象代价是巨大的,耗时远远大于执行简单SQL的时间。
解决方案:
对于需要频繁地跟数据库交互的应用程序,可以在创建了Connection对象,并操作完数据库后,可以不释放掉资源,而是将它放到内存中,当下次需要操作数据库时,可以直接从内存中取出Connection对象,不需要再创建了,这样就极大地节省了创建Connection对象的资源消耗。由于内存也是有限和宝贵的,这又对我们对内存中的Connection对象怎么有效地维护提出了很高的要求。我们将在内存中存放Connection对象的容器称之为 连接池(Connection Pool)。
Connection对象的回收:
当我们的程序中使用完Connection对象时,如果不使用数据库连接池,我们一般会调用 connection.close()方法,关闭connection连接,释放资源。调用过close()方法的Connection对象所持有的资源会被全部释放掉,Connection对象也就不能再使用。如果使用了连接池,我们在用完了Connection对象时,需要将它放在连接池中,就要使用代理模式,为真正的Connection对象创建一个代理对象,代理对象所有的方法都是调用相应的真正Connection对象的方法实现。当代理对象执行close()方法时,要特殊处理,不调用真正Connection对象的close()方法,而是将Connection对象添加到连接池中。