Java八股文 - MyBatis
文章目录
整理的MyBatis框架相关知识点和面试题,部分内容摘自网络,如有侵权请联系我~
什么是MyBatis?
- MyBatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
- 通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 SQL 的动态参数进行映射生成最终执行的 SQL 语句,最后由 mybatis 框架执行 SQL 并将结果映射为 java 对象并返回。(从执行 SQL 到返回 result 的过程)。
ORM是什么
ORM(Object Relational Mapping),对象关系映射,就是把数据库表和实体类及实体类的属性对应起来
让我们可以操作实体类就实现操作数据库表。
为什么说MyBatis是半自动ORM映射工具?它与全自动的区别在哪里?
MyBatis 在查询关联对象或关联集合对象时,需要手动编写 SQL 来完成,所以,称之为半自动 ORM 映射工具。
JDBC编程有哪些不足之处,MyBatis是如何解决的?
- 数据库连接创建、释放频繁,造成系统资源浪费从而影响系统性能
- SQL 语句写在代码中造成代码不易维护
- 向 SQL 语句传参麻烦
- 对结果集解析麻烦
MyBatis编程步骤是什么样的?
- 读取配置文件,创建 SqlSessionFactory 工厂
- 通过 SqlSessionFactory 工厂创建 SqlSession 对象
- 使用 SqlSession 创建 Dao 接口的代理对象
- 使用代理对象执行方法
- 调用 session.commit()提交事务
- 调用 session.close()关闭会话
#{}和${}的区别是什么?
mybatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 号,调用 PreparedStatement 的 set 方法来赋值。 这是预编译处理,可以提高 SQL 执行效率,更重要的是可以防止SQL注入;
mybatis 在处理 ${} 时,就是把 ${} 使用字符串替换。使用该方式如果传入 id 的话,会把 id=1 转换为 id=“1"字符串形式,而数据库中 id 实际为数值型这样就不匹配了,查询失败。(${}是字符串替换,通常在 EL 表达式中使用,用来展示后端传递到前端的值)。
当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
- 通过在查询的 SQL 语句中定义数据库字段名的别名,让数据库字段名的别名和实体类的属性名一致。
- 通过
<resultMap>
来映射字段名和实体类属性名的一一对应的关系。
模糊查询like语句该怎么写?
-
在 Java 代码中添加 SQL 通配符。
1 2 3 4 5 6
string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like #{value} </select>
``
-
在 SQL 语句中拼接通配符%,会引起 SQL 注入
1 2 3 4 5 6
string wildcardname = “smi”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like "%"#{value}"%" </select>
``
MyBatis都有哪些Executor执行器?它们之间的区别是什么?
MyBatis 有三种基本的 Executor 执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
- SimpleExecutor :每次开启一个 Statement 对象,用完立刻关闭 Statement 对象。
- ReuseExecutor :重复使用创建的 Statement 对象。
- BatchExecutor :缓存了多个 Statement 对象,一起执行批量更新。
通常一个xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
Dao 接口,就是人们常说的 Mapper
接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中MappedStatement
的 id 值,接口方法内的参数,就是传递给 SQL 的参数。Mapper
接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个MappedStatement
,举例:com.yaxing.dao.IUserDao.findAll
,可以唯一找到 namespace 为com.yaxing.dao.IUserDao
下面id = findAll
的MappedStatement
。在 MyBatis 中,每一个<select>
、<insert>
、<update>
、<delete>
标签,都会被解析为一个MappedStatement
对象,然后执行后续的操作。
|
|
Dao 接口里的方法可以重载,但是 MyBatis 的 XML 里面的 ID 不允许重复,也就是说对于同一个接口方法名,mapper 文件中只能有一个该 id。 可以使用动态 SQL 实现接口重载对应的 maper。
|
|
然后在 StuMapper.xml
中利用 MyBatis 的动态 SQL 就可以实现。
|
|
能正常运行,并能得到相应的结果,这样就实现了在 Dao 接口中写重载方法。
MyBatis 的 Dao 接口可以有多个重载方法,但是多个接口对应的映射必须只有一个,否则启动会报错。
MyBatis是如何将SQL执行结果封装为目标对象并返回的?都有哪些映射形式?
-
使用
<resultMap>
标签,逐一定义数据库列名和对象属性名之间的映射关系。 -
使用 SQL 列的别名功能,将列的别名属性对应为对象属性名。
有了列名与属性名的映射关系后,MyBatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
在mapper中如何传递多个参数?
- 顺序传参法,#{} 里面的数字代表传入参数的顺序
- @Param 注解传参法,#{} 里面的名称对应的是注解@Param 括号里面修饰的名称
- map 传参,#{} 里面的名称对应的是 Map 里面的 key 名称。
- Java Bean 传参法,#{} 里面的名称对应的是 User 类里面的成员属性。
MyBatis动态SQL有什么用?执行原理?有哪些动态SQL?
MyBatis 动态 SQL 可以在 xml 映射文件内,以标签的形式编写动态 SQL.
执行原理是根据表达式的值 完成逻辑判断并动态拼接 SQL。
MyBatis 提供了 9 种动态 SQL 标签: 常用的有 if|where|foreach
|
|
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的一级、二级缓存
一级缓存:一级缓存是 SqlSession 范围的缓存
当我们执行查询之后,查询的结果会同时存入到 SqlSession 为我们提供的一块区域中。该区域的结构是一个 Map。 当我们再次查询同样的数据时,MyBatis 会先去 SqlSession 中查询是否有,有的话直接拿出来用。
当调用 SqlSession 的添加,修改,删除,commit(),close()、clearCache()等方法时,就会清空一级缓存。
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。 如果SQLSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
二级缓存:二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 SQL 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
MyBatis 中 SqlSessionFactory 对象的缓存
由同一个 SqlSessionFactory 对象创建的 SqlSession 共享其缓存。
二级缓存存放的是数据,而不是对象。
SQLSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。 如果SqlSession3去执行相同 mapper映射下SQL,执行commit提交,将会清空该 mapper映射下的二级缓存区域的数据。 SQLSession2去查询与SQLSession1相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存;
2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态),可在它的映射文件中配置 <cache/>
;
3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存 Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
什么是MyBatis的接口绑定?有哪些实现方式?
接口绑定,就是在 MyBatis 中任意定义接口,然后把接口里面的方法和 SQL 语句绑定, 我们直接调用接口方法就可以,这样比起原来了 SqlSession 提供的方法我们可以有更加灵活的选择和设置。
接口绑定有两种实现方式:
- 通过注解绑定,就是在接口的方法上面加上 @Select、@Update 等注解,里面包含 Sql 语句来绑定;
- 通过 xml 里面写 SQL 来绑定, 在这种情况下,要指定 xml 映射文件里面的 namespace 必须为接口的全路径名。当 Sql 语句比较简单时候,用注解绑定, 当 SQL 语句比较复杂时候,用 xml 绑定,一般用 xml 绑定的比较多。