一、MyBatis基本概念
- 什么是MyBatis?
- MyBatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
- MyBatis的优缺点是什么?
- 优点:
- 简单易学:MyBatis的设计简单直观,容易上手。
- 灵活性强:允许开发人员编写自定义SQL语句,能够根据项目需求进行灵活配置和扩展。
- 可读性好:使用简洁的XML或注解配置方式,可以清晰地表达SQL语句和数据映射关系。
- 性能高:提供了高效的缓存机制,能够有效地减少数据库操作的次数,并支持批量操作和分页查询等功能。
- 易于集成:可以与多种数据库和Web框架无缝集成。
- 缺点:
- 学习成本较高:需要开发人员熟悉SQL语句的编写和调优。
- 配置较为繁琐:MyBatis的配置文件较多,需要开发人员仔细配置,否则容易出现错误。
- SQL语句调试困难:MyBatis将SQL语句和Java代码分离,当SQL语句出现问题时,调试起来相对困难。
- 不适合小型项目:对于小型项目而言,MyBatis的优势可能不够明显,反而会增加项目的开发成本和复杂度。
- 优点:
二、MyBatis核心组件与配置
- MyBatis有哪些核心组件?
- SqlSessionFactory:创建和管理SqlSession对象的工厂。
- SqlSession:执行SQL语句的接口,管理数据库会话。
- Executor:负责执行SQL语句,管理缓存和事务。
- Mapper接口:映射SQL语句与Java对象的方法接口。
- Configuration:全局配置类,包含MyBatis的所有配置信息。
- MyBatis的编程步骤是什么样的?
- 引入MyBatis依赖。
- 配置MyBatis环境,编写mybatis-config.xml,配置数据库连接信息、别名、映射文件等。
- 使用XML文件编写SQL映射(或用注解方式)。
- 定义接口(Mapper),与SQL映射文件中的SQL语句进行绑定。
- 创建SqlSessionFactory,通过SqlSessionFactoryBuilder读取配置文件,生成SqlSessionFactory。
- 通过SqlSessionFactory获取SqlSession。
- 调用Mapper接口执行增删改查操作。
- 关闭SqlSession以释放资源。
- MyBatis的Xml映射文件中,有哪些标签?
- CRUD操作标签:包括select、insert、update、delete等标签,用于定义对数据库的增、删、改、查操作。
- 结果集映射标签:包括<resultMap>和<result>标签,用于定义Java对象和数据库表之间的映射关系。
- SQL片段标签:包括<sql>和<include>标签,用于定义可重用的SQL代码片段。
- 动态SQL标签:包括<if>、<choose>、<when>、<otherwise>、<where>、<set>、<foreach>和<bind>等标签,用于动态生成SQL语句。
三、MyBatis高级特性
- #{}和${}的区别是什么?
- #{}:是sql的参数占位符,MyBatis会将sql中的#{}替换为问号占位符(?),然后使用PreparedStatement进行预编译,最后将实际的参数值设置到预编译语句中。使用#{}可以有效地防止SQL注入等安全问题,同时也可以避免一些数据类型转换的问题。
- **∗∗:是字符串替换,直接将参数值替换到SQL语句中。这种方式的好处是可以直接拼接字符串,但也带来了一些安全问题,如SQL注入风险。因此,使用{}时需要开发人员自行保证参数的合法性。
- MyBatis是如何进行分页的?分页插件的原理是什么?
- MyBatis可以使用RowBounds对象进行分页,这是针对ResultSet结果集执行的内存分页,而非物理分页。此外,也可以在SQL语句中直接书写带有物理分页的参数来完成物理分页功能,或者使用分页插件来完成物理分页。分页插件的基本原理是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的SQL,然后重写SQL,根据数据库方言添加对应的物理分页参数。
- MyBatis是如何将sql执行结果封装为目标对象并返回的?有哪些映射形式?
- MyBatis通过两种方式将SQL执行结果封装为目标对象并返回:一是使用标签逐一定义数据库列名和对象属性名之间的映射关系;二是使用SQL列的别名功能,将列的别名书写为对象属性名。有了列名与属性名的映射关系后,MyBatis通过反射创建对象,并使用反射给对象的属性逐一赋值并返回。
- MyBatis动态SQL是做什么的?有哪些动态SQL?能简述一下动态SQL的执行原理吗?
- MyBatis动态SQL允许在XML映射文件内以标签的形式编写动态SQL,完成逻辑判断和动态拼接SQL的功能。MyBatis提供了9种动态SQL标签,包括<if>、<choose>、<when>、<otherwise>、<where>、<set>、<foreach>和<bind>等。动态SQL的执行原理是使用OGNL从SQL参数对象中计算表达式的值,根据表达式的值动态拼接SQL,以此来完成动态SQL的功能。
- MyBatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别?
- MyBatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一、多对多的关联查询。关联对象查询有两种方式:一种是单独发送一个SQL去查询关联对象,赋给主对象,然后返回主对象;另一种是使用嵌套查询(即使用join查询),一部分列是A对象的属性值,另外一部分是关联对象B的属性值。好处是只发一个SQL查询,就可以把主对象和其关联对象查出来。
四、MyBatis缓存机制
- MyBatis的一级、二级缓存是什么?
- 一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session flush或close之后,该Session中的所有Cache就将清空。默认打开一级缓存。
- 二级缓存:与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口。
- MyBatis中的缓存有哪些实现类型?
- MyBatis中的缓存实现类型主要包括一级缓存(本地缓存)和二级缓存(全局缓存)。一级缓存是PerpetualCache的HashMap实现,存储作用域为Session;二级缓存也可以是PerpetualCache的HashMap实现,但存储作用域为Mapper(Namespace),并且可自定义存储源。
五、其他问题
- MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?
- MyBatis支持延迟加载。延迟加载的实现原理是在需要用到数据时才进行加载,而不是在初始化时就加载所有数据。这可以通过配置Mapper XML文件中的<association>和<collection>元素来实现。当访问这些元素时,MyBatis会动态地生成SQL语句并加载相关数据。
- MyBatis的xml映射文件中,不同的xml映射文件的id是否可以重复?
- 在MyBatis中,不同的xml映射文件如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复。因为namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么id重复会导致数据互相覆盖。
#{} 和 ${} 的区别是什么?
在MyBatis中,#{}
和${}
都是用来替换SQL语句中的参数的,但它们之间存在显著的区别。以下是对这两者的详细对比:
1. 功能与执行过程
#{}
:是sql的参数占位符。MyBatis会将sql中的#{}
替换为问号占位符(?),然后使用PreparedStatement
进行预编译,最后将实际的参数值设置到预编译语句中。这种方式可以有效地防止SQL注入攻击,因为参数值在传递过程中会被自动转义。${}
:是字符串替换。MyBatis会直接将参数值替换到SQL语句中,不进行预编译。这种方式的好处是可以直接拼接字符串,但也带来了一些安全问题,如SQL注入风险。因为参数值直接嵌入到SQL语句中,如果参数值包含恶意SQL代码,就可能导致SQL注入攻击。
2. 使用场景
#{}
:通常用于传递普通参数,如数字、字符串等。由于它使用预编译的方式,所以能够有效地防止SQL注入攻击,并且可以提高数据库的性能。${}
:通常用于传递SQL命令或SQL关键字,如列名、表名、排序字段等。但需要注意的是,在使用${}
时需要开发人员自行保证参数的合法性,以避免SQL注入风险。此外,${}
还可以用于动态拼接SQL语句,但同样需要谨慎使用。
3. 安全性
#{}
:由于使用预编译的方式,所以能够有效地防止SQL注入攻击,是更安全的选择。${}
:直接将参数值替换到SQL语句中,不进行预编译,存在SQL注入风险。因此,在使用时需要特别注意参数的合法性验证。
4. 示例
- 使用
#{}
:SELECT * FROM users WHERE id = #{userId}
如果传入的
userId
为1,则解析后的SQL语句为:SELECT * FROM users WHERE id = ?
此时,数据库会预编译这条SQL语句,并将
?
替换为实际的参数值1。 - 使用
${}
:SELECT * FROM users WHERE id = ${userId}
如果传入的
userId
为1,则解析后的SQL语句为:SELECT * FROM users WHERE id = 1
此时,参数值直接替换到了SQL语句中,没有进行预编译。如果传入的
userId
包含恶意SQL代码,就可能导致SQL注入攻击。
Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
Dao 接口的工作原理
Dao(Data Access Object)接口是Java中用于访问数据库的一种设计模式。在MyBatis等持久层框架中,Dao接口通常与XML映射文件或注解一起使用,以定义和执行SQL语句。Dao接口的工作原理大致如下:
-
接口定义:开发者首先定义一个Java接口,接口中声明了用于数据库操作的方法,如增删改查等。
-
SQL映射:然后,开发者需要在XML映射文件或使用注解的方式中,为Dao接口中的每个方法指定对应的SQL语句。这些SQL语句定义了如何与数据库进行交互。
-
框架代理:在运行时,MyBatis等框架会通过Java动态代理技术为Dao接口创建一个代理对象。当开发者调用Dao接口中的方法时,实际上是在调用这个代理对象。
-
SQL执行:代理对象会根据方法的签名和XML映射文件或注解中的SQL语句,构建出完整的SQL命令,并通过JDBC与数据库进行交互。
-
结果映射:执行SQL命令后,代理对象还会将数据库返回的结果集映射为Java对象,并返回给开发者。
Dao 接口里的方法重载
在Java中,方法重载是指在一个类中可以有多个方法具有相同的名称,但它们的参数列表不同(参数个数或类型不同)。对于Dao接口来说,方法重载同样是适用的。
然而,在MyBatis中,方法重载有一些需要注意的地方:
-
XML映射:如果使用XML映射文件来定义SQL语句,那么每个
<select>
、<insert>
、<update>
、<delete>
标签都需要一个唯一的id
属性来标识。因此,对于Dao接口中重载的方法,你需要在XML映射文件中为每个方法指定不同的id
。通常,可以通过方法的全限定名(包括包名、类名和方法名)或自定义的唯一标识符来实现这一点。 -
注解映射:如果使用注解来定义SQL语句,那么MyBatis会根据方法的签名(包括方法名和参数列表)来找到对应的SQL语句。在这种情况下,方法重载是自然支持的,因为每个重载的方法都有唯一的签名。
-
参数处理:对于重载的方法,如果它们的参数列表不同,MyBatis需要能够正确地识别并处理这些参数。这通常是通过Java的反射机制来实现的。因此,在定义重载方法时,需要确保参数的类型和顺序是清晰的,以避免混淆。