文章目录
- 1.什么是Mybatis?
- 2.Mybatis的优缺点?
- 3.Mybatis使用场合?
- 4.#{}和${}的区别是什么?
- 5.当实体类的属性名和表种字段名不一致怎么办?
- 6.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
- 7.如何获取自动生成的(主)键值?
- 8.Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?
- 3、最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
- 9. Mybatis动态SQL?
- 10.说一下resultMap和resultType?
- 11.Mybatis全局配置文件中有哪些标签?分别代表什么意思?
- Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
- 12.Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。
- 13.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
- 4、Mybatis是如何进行分页的?分页插件的原理是什么?
- 5、简述Mybatis的插件运行原理,以及如何编写一个插件。
- 6、Mybatis执行批量插入,能返回数据库主键列表吗?
- 12、Mybatis中如何执行批处理?
- 13、Mybatis都有哪些Executor执行器?它们之间的区别是什么?
- 14、Mybatis中如何指定使用哪一种Executor执行器?
- 15、Mybatis是否可以映射Enum枚举类?
- 16、Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?
1.什么是Mybatis?
jdbc
+ sql
+ 两个兼容
Mybatis是一个优秀的持久层框架,它使得开发者只需要专注于SQL语句本身,而不用去关心注册驱动,创建connection等;而且SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;很好的与各种数据库兼容;能够与Spring很好的集成;
2.Mybatis的优缺点?
Mybaits的优点:jdbc
+ sql
+ 两个兼容
(1)与JDBC相比,使得开发者只需要专注于SQL语句本身,而不用去关心注册驱动,创建connection等;
(2)SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
(4)能够与Spring很好的集成;
(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
MyBatis框架的缺点:
(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis框架适用场合:
(1)MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
3.Mybatis使用场合?
专注于sql本身,是一个足够灵活的dao层解决方案.,对性能的要求很高,或者需求多变的项目,
4.#{}和${}的区别是什么?
#{}喜欢和符号打交道,要么是问号要么是单引号;而${}不会替换为?也不会加上单引号
1)#{}是预编译处理,$ {}是字符串替换。mybatis在处理#{}时,会将sql中的#{}替换为?号;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
2)变量替换后,#{} 对应的变量自动加上单引号 ;变量替换后,${} 对应的变量不会加上单引号
3)能用#{}尽量不要用${}
,在某些特殊场合下只能用${}
,不能用#{}。
例如:表名作参数时,必须用 ${}。如:select * from ${tableName}
例如:order by 时,必须用 ${}。如:select * from t_user order by ${columnName}。
(为什么上面两种情况不能使用#{}呢?我觉得很可能就是“变量替换后,#{} 对应的变量自动加上单引号”惹得鬼)
4)使用 #{} 可以有效的防止SQL注入,提高系统安全性。
#{} 的预编译的机制。预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。
5.当实体类的属性名和表种字段名不一致怎么办?
实体类通常是userName,而数据库中的字段通常是user_name
1、写SQL时给变量起别名(SELECT user_name userName FROM XXX
)
2、在MyBatis的全局配置文件中开启驼峰命名规则<setting name="mapUnderscoreToCameLCase" value="true" />
3、使用ResultMap手动转换,逐一定义数据库列名和对象属性名之间的映射关系。
<!-- 自定义映射-->
<resultMap type="com.atguigu.pojo.Employee" id="myMap">
<!-- 映射主键 -->
<id cloumn="id" property="id"/>
<!-- 映射其他列 -->
<result column="last_name" property="lastName" />
<result column="email" property="email" />
<result column="salary" property="salary" />
<result column="dept_id" property="deptId" />
</resultMap>
6.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
1、写SQL时给变量起别名(SELECT user_name userName FROM XXX
)
2、在MyBatis的全局配置文件中开启驼峰命名规则<setting name="mapUnderscoreToCameLCase" value="true" />
3、使用ResultMap手动转换,逐一定义数据库列名和对象属性名之间的映射关系。
7.如何获取自动生成的(主)键值?
MySQL:Mapper文件insert语句设置 useGeneratedKeys=“true” keyProperty=“id”
<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
insert into names (name) values (#{name})
</insert>
8.Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复
(原因就是 namespace+id 是作为 Map<String, MapperStatement>的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。)
3、最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
答:Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement。
举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一个<select>
、<insert>
、<update>
、<delete>
标签,都会被解析为一个MappedStatement对象。
参数不同时,方法能重载吗?
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。(重载的两个方法在同一个包下,而且它们的名一样,那不就混淆了嘛)
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
9. Mybatis动态SQL?
1)、传统的JDBC的方法,写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。Mybatis的动态SQL就是为了解决这种问题而产生的;Mybatis的动态SQL语句通过 if, choose, where, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
2)、Mybatis的动态SQL标签主要有以下几类:
If语句(简单的条件判断)
Where(主要是用来简化SQL语句中where条件判断,能智能的处理and/or 不用担心多余的语法导致的错误)
Foreach(一般使用在 mybatis in语句查询时特别有用)
…
10.说一下resultMap和resultType?
MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。
resultType:当使用resultType做SQL语句返回结果类型处理时,对于SQL语句查询出的字段在相应的pojo中必须有和它相同的字段对应,而resultType中的内容就是pojo在本项目中的位置(比如resultType="com.someapp.model.User"
)。
因此对于单表查询的话用resultType是最合适的。但是,如果在写pojo时,不想用数据库表中定义的字段名称,也是可以使用resultMap进行处理对应的。多表连接查询时,若是一对一的连接查询,那么需要新建一个pojo,pojo中包括两个表中需要查询出的所有的字段,这个地方的处理方式通常为创建一个继承一个表字段的pojo,再在里面添加另外一个表内需要查询出的字段即可。若是一对多查询时,若是使用内连接查询,则很可能出现查询出的字段有重复。使用双重for循环嵌套处理即可。
使用resultType
<select id="selectUsers" parameterType="int" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
使用resultMap:
11.Mybatis全局配置文件中有哪些标签?分别代表什么意思?
参考博文:Day11_MyBatis—mybatis快速入门
configuration 配置
properties 属性:可以加载外部properties文件
settings 设置:可以设置mybatis的全局属性
typeAliases 类型命名
environments 环境
environment 环境变量
transactionManager 事务管理器
dataSource 数据源
mappers 映射器
typeHandlers 类型处理器
objectFactory 对象工厂
plugins 插件
Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
<resultMap>
、<parameterMap>
、<sql>
、<include>
、<selectKey>
,加上动态 sql 的 9 个标签trim|where|set|foreach|if|choose|when|otherwise|bind等,
其中为 sql 片段标签,通过<include>
标签引入 sql 片段,<selectKey>
为不支持自增的主键生成策略标签。
12.Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。
(1)MyBatis实现一对一有几种方式?具体怎么操作的?
有联合查询和嵌套查询,
联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association
节点配置一对一的类就可以完成;
嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association
配置,但另外一个表的查询通过select属性配置。
(2)MyBatis实现一对多有几种方式,怎么操作的?
有联合查询和嵌套查询。
联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection
节点配置一对多的类就可以完成;
嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection
,但另外一个表的查询通过select节点配置。
一个订单对应一个用户(一对一)
一个用户对应多个订单(一对多)
13.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
1.什么是延迟加载?(了解)
在一对多中,我们有一个用户,他有100个账户。
问题1:在查询用户的时候,要不要把关联的账户查出来?
解答:在查询用户的时候,用户下的账户信息应该是我们什么时候使用,什么时候去查询。
延迟加载:在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫按需查询(懒加载)
2.关联对象加载时机(背诵)
直接加载:遇到代码中查询语句,马上到DB中执行select语句进行查询。(在一对一和多对一的情况下会进行直接加载)
侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情时,就会马上执行关联对象的select查询。即对关联对象的查询执行,侵入到了主加载对象的详情访问中。也可以这样理解:将关联对象的详情侵入到了主加载对象的详情中,即将关联对象的详情作为主加载对象的详情的一部分出现了。
深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的select查询。
3.如何开启延迟加载(背诵)
侵入式延迟加载:将延迟加载开关lazyLoadingEnabled开启(置为true),将侵入式延迟加载开关aggressiveLazyLoading也开启(置为true,默认为true)
<settings>
<!-- 延迟加载总开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 侵入式延迟加载开关 -->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
深度延迟加载:将延迟加载开关lazyLoadingEnabled开启(置为true),将侵入式延迟加载开关aggressiveLazyLoading关闭(置为false)。
<settings>
<!-- 延迟加载总开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 侵入式延迟加载开关 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
4.延迟加载的机理(背诵)
术语:使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,拦截器执行时发现关联对象的详情为空时会把关联对象的详情加载进来。
大白话:比如A和B是一对多模型然后开启了延迟加载,当代码执行查询A表的断点处会立即查询A表,但没查询A表中关联的B表。当对A对象的详情进行访问时,调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值(因为刚刚没有查询B只查了A),那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
5.使用延迟加载的目的
减轻DB服务器的压力,因为我们延迟加载只有在用到需要的数据才会执行查询操作。
4、Mybatis是如何进行分页的?分页插件的原理是什么?
可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,每当执行接口对象的方法时,就会进入拦截方法,在拦截方法内拦截待执行的sql,然后重写sql,添加对应的物理分页语句和物理分页参数。
举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10
5、简述Mybatis的插件运行原理,以及如何编写一个插件。
简述Mybatis的插件运行原理:
Mybatis使用JDK的动态代理,每当执行接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
如何编写一个插件:
实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
6、Mybatis执行批量插入,能返回数据库主键列表吗?
答:能,JDBC都能,Mybatis当然也能。
12、Mybatis中如何执行批处理?
答:使用BatchExecutor完成批处理。
13、Mybatis都有哪些Executor执行器?它们之间的区别是什么?
答:Mybatis有三种基本的Executor执行器,SimpleExecutor(简单的)、ReuseExecutor(复用)、BatchExecutor(批处理)。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
14、Mybatis中如何指定使用哪一种Executor执行器?
答:在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型
15、Mybatis是否可以映射Enum枚举类?
答:Mybatis可以映射枚举类,映射方式为自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler有两个作用,一是完成从javaType至jdbcType的转换,二是完成jdbcType至javaType的转换,体现为setParameter()和getResult()两个方法,分别代表设置sql问号占位符参数和获取列查询结果。
16、Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?
答:虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以正确识别。
原理是,Mybatis解析A标签,发现A标签引用了B标签,但是B标签尚未解析到,尚不存在,此时,Mybatis会将A标签标记为未解析状态,然后继续解析余下的标签,包含B标签,待所有标签解析完毕,Mybatis会重新解析那些被标记为未解析的标签,此时再解析A标签时,B标签已经存在,A标签也就可以正常解析完成了。