🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
🎉欢迎 👍点赞✍评论⭐收藏
Mybatis知识专栏学习
Mybatis知识云集 | 访问地址 | 备注 |
---|---|---|
Mybatis知识点(1) | https://blog.csdn.net/m0_50308467/article/details/134122119 | Mybatis专栏 |
Mybatis知识点(2) | https://blog.csdn.net/m0_50308467/article/details/134450807 | Mybatis专栏 |
Mybatis知识点(3) | https://blog.csdn.net/m0_50308467/article/details/134450920 | Mybatis专栏 |
文章目录
- 01、什么是 Mybatis,优点和缺点?
- 02、MyBatis 框架适用场合?
- 03、MyBatis 与 Hibernate 有哪些不同?
- 04、MyBatis 中 #{}和 的区别是什么?
- 05、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
- 06、 MyBatis 模糊查询 like 语句该怎么写?
- 07、通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
- 08、Mybatis 是如何进行分页的?分页插件的原理是什么?
- 09、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
- 10、Mybatis 如何执行批量插入?
- 11、Mybatis 如何获取自动生成的(主)键值?
- 12、在 mapper 中如何传递多个参数?
- 13、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?
- 14、Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
- 15、为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
- 16、Mybatis 一对一、一对多的关联查询 ?
- 17、MyBatis 实现一对一有几种方式?具体怎么操作的?
- 18、MyBatis 实现一对多有几种方式,怎么操作的?
- 19、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
- 20、Mybatis 的一级、二级缓存是什么?
- 21、什么是 MyBatis 的接口绑定?有哪些实现方式?
- 22、使用 MyBatis 的 mapper 接口调用时有哪些要求?
- 23、Mapper 编写有哪几种方式?
- 24、简述 Mybatis 的插件运行原理,以及如何编写一个插件?
- 25. MyBatis 逻辑分页和物理分页的区别是什么?
- 26.MyBatis 是否支持延迟加载?延迟加载的原理是什么?
- 27.MyBatis 有哪些执行器(Executor)?
- 28. MyBatis 分页插件的实现原理是什么?
01、什么是 Mybatis,优点和缺点?
MyBatis是一个开源的Java持久化框架,它提供了简化数据库访问的方式。以下是MyBatis的优点和缺点:
优点:
1. 简化SQL编写
:MyBatis允许开发人员直接编写和优化SQL语句,可以更好地控制SQL的执行计划和性能。
2. 灵活性
:MyBatis相对于全自动的ORM框架更加灵活,可以根据具体的需求进行灵活的配置和扩展。
3. 易于集成
:MyBatis可以与现有的SQL代码无缝集成,不需要对现有代码进行大量改动。
4. 良好的性能
:由于可以手动优化SQL语句,MyBatis可以提供更好的性能和查询效率。
缺点:
1. 学习成本较高
:相比于全自动ORM框架,MyBatis的学习曲线较陡峭,需要了解SQL语句和XML配置的相关知识。
2. 配置复杂
:MyBatis的配置文件相对复杂,需要编写大量的XML配置来定义映射关系和SQL语句。
3. 对象-关系映射相对较弱
:相比于全自动ORM框架,MyBatis在对象和数据库表之间的映射方面相对较弱,需要手动编写和配置映射关系。
总的来说,MyBatis是一个强大而灵活的持久化框架,它提供了更好的SQL控制和性能优化能力。然而,使用MyBatis需要一定的学习成本,并且在对象-关系映射和配置方面相对复杂。根据具体的项目需求和开发团队的偏好,选择合适的持久化框架是很重要的。
02、MyBatis 框架适用场合?
MyBatis框架适用于各种Java应用程序的数据库访问场景。以下是一些适合使用MyBatis的场合:
1. 需要灵活的SQL控制
:如果你的应用程序需要根据不同的条件生成不同的SQL语句,那么MyBatis是一个不错的选择。MyBatis提供了动态SQL的支持,可以根据条件动态生成SQL语句,使得查询更加灵活和高效。
2. 对象-关系映射需求不复杂
:相对于全自动的ORM框架,MyBatis在对象和数据库表之间的映射方面相对较弱。如果你的对象模型与数据库模式之间的映射关系比较简单,或者你希望手动控制映射关系,那么MyBatis是一个不错的选择。
3. 需要直接访问和优化SQL语句
:MyBatis允许开发人员直接编写和优化SQL语句。如果你对SQL语句有较高的要求,希望直接控制SQL的执行计划或者使用数据库特定的优化技巧,那么MyBatis可以满足你的需求。
4. 需要与现有SQL代码无缝集成
:如果你的项目中已经存在大量的SQL代码,而你希望将这些代码与Java代码解耦,那么MyBatis是一个理想的选择。MyBatis可以将现有的SQL代码与Java对象进行关联,从而实现数据库访问的统一管理。
总的来说,MyBatis适用于各种规模的Java应用程序,特别是对于需要灵活控制SQL语句、直接访问和优化SQL、与现有SQL代码无缝集成的场景来说,它是一个强大而灵活的选择。
03、MyBatis 与 Hibernate 有哪些不同?
MyBatis和Hibernate是两种常见的Java持久化框架,它们在一些方面有一些不同之处。以下是它们的主要区别:
- SQL控制方式:MyBatis采用基于SQL的控制方式,开发人员需要手动编写SQL语句。而Hibernate采用基于对象的控制方式,开发人员不需要编写SQL语句,框架会自动将对象操作转换为相应的SQL语句。
举例说明:
使用MyBatis进行查询,需要手动编写SQL语句:
@Select("SELECT * FROM users WHERE age > #{age}")
List<User> getUsersByAge(int age);
使用Hibernate进行查询,不需要编写SQL语句:
List<User> getUsersByAge(int age) {
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root).where(builder.gt(root.get("age"), age));
return session.createQuery(query).getResultList();
}
- 对象-关系映射:MyBatis需要开发人员手动编写对象和数据库表之间的映射关系,通过XML或注解配置来实现。而Hibernate采用全自动的对象-关系映射(ORM),通过注解或XML配置来实现对象和数据库表之间的映射关系。
举例说明:
使用MyBatis进行对象-关系映射,需要手动编写映射配置:
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
</resultMap>
使用Hibernate进行对象-关系映射,可以通过注解实现:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "age")
private int age;
// ...
}
总的来说,MyBatis更加灵活,适用于对SQL控制有较高要求、需要与现有SQL代码无缝集成的场景。而Hibernate更加自动化,适用于快速开发、对象-关系映射复杂的场景。选择使用哪种框架取决于具体的项目需求和开发团队的偏好。
04、MyBatis 中 #{}和 的区别是什么?
在MyBatis中, #{}
和 ${}
都是用于参数替换的占位符,但它们在处理方式和安全性方面有所不同。
#{}
:使用#{}
可以实现预编译的参数替换,MyBatis会将参数值以安全的方式传递给数据库,可以防止SQL注入攻击。#{}
会将参数值作为占位符的一部分,最终会被转换为预编译的参数,例如SELECT * FROM users WHERE age = #{age}
。
举例说明:
使用 #{}
进行参数替换:
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(int id);
${}
:使用${}
可以实现简单的字符串替换,它会将参数值直接拼接到SQL语句中,不会进行预编译。${}
在某些情况下可能存在安全风险,因为参数值直接拼接到SQL语句中,如果参数值来自用户输入,可能会导致SQL注入攻击。
举例说明:
使用 ${}
进行参数替换:
@Select("SELECT * FROM users WHERE name = '${name}'")
User getUserByName(String name);
总的来说, #{}
是更安全和推荐的参数替换方式,它可以防止SQL注入攻击,并支持预编译参数。而 ${}
在某些情况下可能会存在安全风险,需要谨慎使用。
05、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
当实体类中的属性名和表中的字段名不一样时,可以使用MyBatis的映射配置来解决这个问题。MyBatis提供了多种方式来映射实体类属性和表中字段的对应关系,以下是几种常见的解决方法:
使用注解
:可以在实体类的属性上使用注解来指定属性与字段的对应关系。例如,使用@Column
注解指定字段名:
public class User {
@Column(name = "user_name")
private String username;
// ...
}
使用XML配置
:可以使用XML配置文件来指定属性与字段的对应关系。在XML中使用<resultMap>
元素定义映射关系,例如:
<resultMap id="userResultMap" type="User">
<result property="username" column="user_name"/>
<!-- ... -->
</resultMap>
使用别名
:可以在SQL语句中使用别名来将字段名映射到属性名。例如,在查询语句中使用AS
关键字指定别名:
SELECT user_name AS username FROM users
这样,查询结果中的 user_name
字段会被映射到实体类的 username
属性。
总的来说,MyBatis提供了多种方式来解决实体类属性名和表字段名不一致的问题。可以根据具体的情况选择合适的方式来进行映射配置。
06、 MyBatis 模糊查询 like 语句该怎么写?
在MyBatis中,可以使用 LIKE
语句进行模糊查询。 LIKE
语句通常与通配符 %
结合使用,表示匹配任意字符。以下是在MyBatis中编写模糊查询的示例:
假设有一个 User
实体类,其中包含一个 name
属性,我们希望根据用户的姓名进行模糊查询。
- 使用
#{}
占位符:
<select id="getUserByName" resultType="User">
SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')
</select>
在上述示例中, CONCAT
函数用于拼接字符串,将 %
放在 #{name}
的前后,实现模糊查询。
- 使用
${}
占位符:
<select id="getUserByName" resultType="User">
SELECT * FROM users WHERE name LIKE '%${name}%'
</select>
在上述示例中, ${name}
会直接替换成实际的参数值,实现模糊查询。
需要注意的是,使用 ${}
占位符存在安全风险,可能会导致SQL注入攻击,因此建议使用 #{}
占位符来进行参数替换。
总的来说,以上示例展示了如何在MyBatis中使用
LIKE
语句进行模糊查询。可以根据具体的需求和数据库的语法规则进行相应的调整。
07、通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
通常情况下,一个XML映射文件在MyBatis中会对应一个DAO(Data Access Object)接口。DAO接口定义了与数据库交互的方法,通过调用这些方法来执行数据库操作。
DAO接口的工作原理是通过MyBatis的动态代理机制来实现的。在运行时,MyBatis会根据DAO接口的定义动态生成一个代理对象,该代理对象会拦截DAO接口方法的调用,并将其转发给对应的SQL映射文件进行执行。
在DAO接口中的方法可以重载,只要方法签名(方法名和参数列表)不同即可。MyBatis会根据方法名和参数列表来确定具体执行哪个SQL语句。这样可以方便地定义多个具有不同参数的查询或操作方法。
举例说明:
public interface UserDao {
User getUserById(Long id);
List<User> getUsersByAge(int age);
void insertUser(User user);
void updateUser(User user);
}
在上述示例中, UserDao
接口定义了根据用户ID获取用户、根据年龄获取用户列表、插入用户和更新用户的方法。这些方法的参数不同,可以根据具体的需求进行重载。
总的来说,DAO接口是用于定义与数据库交互的方法,通过动态代理机制实现与SQL映射文件的绑定,方便地进行数据库操作。方法可以根据参数的不同进行重载,以满足不同的查询或操作需求。
08、Mybatis 是如何进行分页的?分页插件的原理是什么?
MyBatis提供了一种简单的分页机制来处理查询结果的分页。在SQL语句中使用 LIMIT
关键字来指定返回结果的起始位置和数量,结合分页参数来实现分页查询。
具体的分页步骤如下:
- 在SQL语句中使用
LIMIT
关键字来指定返回结果的起始位置和数量。通常使用#{offset}
表示起始位置,#{limit}
表示每页的数量。
SELECT * FROM users LIMIT #{offset}, #{limit}
- 在Java代码中,通过传递分页参数来指定起始位置和数量。通常使用
offset
表示起始位置,limit
表示每页的数量。
List<User> getUsersByPage(int offset, int limit);
分页插件是一种MyBatis的扩展机制,可以进一步简化分页操作。分页插件的原理是通过拦截器(Interceptor)来拦截执行的SQL查询,并在查询过程中自动添加分页相关的SQL语句。
具体的分页插件原理如下:
-
自定义一个实现了MyBatis的
Interceptor
接口的分页拦截器。 -
在拦截器中重写
intercept
方法,拦截SQL执行,并根据分页参数自动添加分页的SQL语句。 -
将拦截器配置到MyBatis的配置文件中,使其生效。
分页插件可以根据数据库的不同,自动适配不同的分页语法,并提供了更加灵活的配置选项,如分页参数的命名、是否启用物理分页等。
总的来说,MyBatis可以通过手动编写SQL语句和使用分页插件两种方式实现分页查询。分页插件通过拦截器机制,自动添加分页的SQL语句,简化了分页操作的配置和使用。
09、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
MyBatis通过映射配置将SQL执行结果封装为目标对象并返回。在MyBatis中,有以下几种映射形式:
1. 基本类型映射
:将查询结果的单个列映射到Java对象的基本类型属性上。例如,将查询结果的整数列映射到Java对象的int属性。
2. 简单类型映射
:将查询结果的单个列映射到Java对象的简单类型属性上,如String、Date等。MyBatis会根据列名和属性名的匹配关系自动进行映射。
3. 嵌套对象映射
:将查询结果的多个列映射到Java对象的嵌套对象中。通过定义嵌套的resultMap,可以将查询结果拆分为多个对象,并通过关联关系组装起来。
4. 集合映射
:将查询结果的多行数据映射到Java对象的集合属性上。可以使用 <collection>
标签定义集合属性的映射关系,支持List、Set、Map等集合类型。
5. 关联映射
:将查询结果的多个表的数据映射到Java对象的关联关系中。可以使用 <association>
和 <collection>
标签定义关联关系的映射关系。
6. 枚举类型映射
:将查询结果的列映射到Java对象的枚举类型属性上。可以使用 <typeHandler>
标签定义枚举类型的映射方式。
以上是MyBatis中常见的映射形式,通过合理的映射配置,可以将SQL执行结果准确地封装为Java对象并返回。根据具体的需求和查询结果的结构,可以选择合适的映射形式来进行配置。
10、Mybatis 如何执行批量插入?
MyBatis提供了批量插入的功能,可以一次性插入多条数据,提高插入效率。以下是使用MyBatis执行批量插入的示例:
- 在Mapper XML文件中定义批量插入的SQL语句,使用
foreach
标签来遍历插入的数据。
<insert id="batchInsertUsers" parameterType="java.util.List">
INSERT INTO users (id, name, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.id}, #{user.name}, #{user.age})
</foreach>
</insert>
在上述示例中, batchInsertUsers
是批量插入的SQL语句, list
是传入的参数, user
是遍历时的临时变量,通过 #{}
占位符来获取对应的属性值。
- 在对应的DAO接口中定义批量插入的方法,将数据作为参数传递给SQL语句。
void batchInsertUsers(List<User> userList);
在上述示例中, batchInsertUsers
方法接受一个 List<User>
类型的参数,用于传递要插入的数据。
- 在Java代码中调用批量插入的方法,传入要插入的数据。
List<User> userList = new ArrayList<>();
// 添加要插入的数据到userList中
userDao.batchInsertUsers(userList);
在上述示例中,通过调用 batchInsertUsers
方法,将要插入的数据传递给MyBatis执行批量插入操作。
通过以上步骤,就可以使用MyBatis执行批量插入操作了。注意在配置文件中开启批量插入的支持,可以提高插入效率。
11、Mybatis 如何获取自动生成的(主)键值?
MyBatis提供了多种方式来获取自动生成的主键值。以下是几种常见的获取自动生成主键的方法:
- 使用
useGeneratedKeys
属性:在插入操作时,可以在插入语句中添加useGeneratedKeys="true"
属性,然后通过selectKey
元素定义获取主键的方式。
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
在上述示例中, useGeneratedKeys="true"
表示启用自动生成主键的功能, keyProperty="id"
表示将生成的主键值赋给 id
属性。
- 使用
selectKey
元素:可以使用selectKey
元素在插入操作后执行额外的查询语句来获取主键值。
<insert id="insertUser" parameterType="User">
<selectKey keyProperty="id" resultType="Long" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
在上述示例中, selectKey
元素定义了一个查询语句,通过 LAST_INSERT_ID()
函数获取最后插入的主键值,并将其赋给 id
属性。
- 使用数据库特定的函数:根据不同的数据库,可以使用特定的函数来获取自动生成的主键值。例如,MySQL可以使用
LAST_INSERT_ID()
函数,Oracle可以使用SEQUENCE_NAME.CURRVAL
来获取当前序列值。
总的来说,通过以上方法,可以在MyBatis中获取自动生成的主键值。根据具体的数据库和需求,选择合适的方式来获取主键值,并将其赋给对应的属性。
12、在 mapper 中如何传递多个参数?
在MyBatis的Mapper中,可以通过多种方式传递多个参数。以下是几种常见的方式:
- 使用注解:可以在Mapper接口的方法参数上使用
@Param
注解来指定参数的名称,然后在SQL语句中通过${}
占位符引用参数。
@Select("SELECT * FROM users WHERE name = #{name} AND age = #{age}")
User getUserByNameAndAge(@Param("name") String name, @Param("age") int age);
在上述示例中,使用 @Param
注解指定了 name
和 age
参数的名称,并在SQL语句中通过 #{}
占位符引用参数。
- 使用Map类型参数:可以将多个参数封装到一个Map中,然后将Map作为方法的参数传递给Mapper接口方法。在SQL语句中通过Map的键来引用对应的值。
@Select("SELECT * FROM users WHERE name = #{name} AND age = #{age}")
User getUserByNameAndAge(Map<String, Object> params);
在上述示例中,将 name
和 age
作为Map的键值对传递给方法,然后在SQL语句中通过 #{}
占位符引用对应的值。
- 使用@Param注解和Map类型参数的组合:可以结合使用
@Param
注解和Map类型参数,将部分参数通过@Param
注解指定名称,剩余的参数封装到Map中。
@Select("SELECT * FROM users WHERE name = #{name} AND age = #{params.age}")
User getUserByNameAndAge(@Param("name") String name, @Param("params") Map<String, Object> params);
在上述示例中, name
通过 @Param
注解指定了名称, age
通过 params.age
的方式引用Map中的值。
通过以上方式,可以在MyBatis的Mapper中传递多个参数。根据具体的需求和参数的类型,选择合适的方式来进行参数传递。
13、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?
MyBatis动态SQL是一种根据不同条件动态生成SQL语句的功能,它可以根据不同的条件来拼接SQL语句,以满足不同的查询需求。动态SQL的执行原理是通过MyBatis的OGNL表达式解析器,在运行时动态生成最终的SQL语句。
以下是几种常见的动态SQL:
- if元素:可以根据条件动态生成SQL语句的一部分。
<select id="getUserByNameAndAge" resultType="User">
SELECT * FROM users
WHERE 1=1
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</select>
在上述示例中,根据传入的name和age参数,动态生成查询条件。
- choose、when、otherwise元素:类似于Java中的switch语句,可以根据不同的条件选择不同的SQL语句块。
<select id="getUserByCondition" resultType="User">
SELECT * FROM users
<choose>
<when test="name != null">
WHERE name = #{name}
</when>
<when test="age != null">
WHERE age = #{age}
</when>
<otherwise>
WHERE 1=1
</otherwise>
</choose>
</select>
在上述示例中,根据传入的name和age参数,选择不同的查询条件。
- foreach元素:用于遍历集合或数组,生成重复的SQL片段。
<select id="getUsersByIdList" resultType="User">
SELECT * FROM users
WHERE id IN
<foreach collection="idList" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
在上述示例中,根据传入的idList参数,动态生成IN子句。
通过以上动态SQL的使用,可以根据不同的条件动态生成SQL语句,灵活地满足不同的查询需求。根据具体的业务需求,选择合适的动态SQL元素来进行配置。
14、Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
除了常见的 select
、 insert
、 update
、 delete
标签之外,MyBatis的XML映射文件还提供了其他一些标签,用于定义更复杂的数据库操作逻辑。以下是几个常见的标签:
-
<resultMap>
:用于定义结果集的映射关系,将查询结果映射到Java对象中。 -
<parameterMap>
:已经被废弃,不推荐使用。用于定义参数对象的映射关系。 -
<sql>
:用于定义可重用的SQL片段,可以在其他SQL语句中引用。 -
<include>
:用于引入其他的SQL片段,实现SQL语句的复用。 -
<if>
:用于在SQL语句中根据条件动态生成SQL片段。 -
<choose>
、<when>
、<otherwise>
:类似于Java中的switch语句,根据不同的条件选择不同的SQL片段。 -
<trim>
、<where>
、<set>
、<foreach>
等:用于对SQL语句进行动态的修剪、拼接、循环等操作。 -
<selectKey>
:用于在插入操作后执行额外的查询语句来获取自动生成的主键值。 -
<bind>
:用于将一个Ognl表达式的结果绑定到一个变量上,方便后续的引用。
以上是MyBatis XML映射文件中的一些常见标签,通过合理地使用这些标签,可以实现更复杂的数据库操作逻辑。根据具体的需求,选择合适的标签来进行配置。
15、为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
MyBatis被称为半自动ORM映射工具是因为它在对象和数据库表之间的映射方面相对较弱,需要开发人员手动编写和配置映射关系。与全自动的ORM框架相比,MyBatis在以下几个方面存在区别:
1. 映射配置
:在全自动ORM框架中,对象和数据库表之间的映射关系通常是通过注解或约定进行自动推断和生成的。而在MyBatis中,开发人员需要手动编写和配置映射关系,通过XML或注解来指定对象属性和数据库表字段之间的对应关系。
2. SQL控制
:全自动ORM框架通常会自动生成SQL语句,开发人员无需关心具体的SQL语句细节。而在MyBatis中,开发人员需要手动编写SQL语句,可以更加灵活地控制SQL的编写和执行过程。
3. 查询性能
:由于全自动ORM框架会自动生成SQL语句,可能无法满足某些特定的查询需求或优化要求。而MyBatis允许开发人员直接编写和优化SQL语句,可以更好地满足复杂查询和性能优化的需求。
4. 灵活性
:全自动ORM框架通常提供了更高的开发效率,可以快速进行数据库操作。而MyBatis相对更加灵活,可以根据具体的需求进行灵活的配置和扩展。
总的来说,MyBatis相对于全自动的ORM框架而言,需要开发人员手动编写和配置映射关系,同时提供了更灵活的SQL控制和查询优化能力。这使得MyBatis更适合于对SQL控制有较高要求、需要与现有SQL代码无缝集成的场景。
16、Mybatis 一对一、一对多的关联查询 ?
在MyBatis中,可以使用嵌套查询或者关联查询来实现一对一和一对多的关联查询。
- 一对一关联查询:
- 嵌套查询:通过在主查询中使用子查询来获取关联对象的信息。
- 关联查询:使用JOIN语句将主表和关联表进行连接查询。
举例说明一对一关联查询:
- 使用嵌套查询:
<select id="getUserWithProfile" resultType="User">
SELECT u.id, u.name, u.age, p.address, p.phone
FROM users u
INNER JOIN profiles p ON u.id = p.user_id
WHERE u.id = #{id}
</select>
- 使用关联查询:
<select id="getUserWithProfile" resultMap="UserResultMap">
SELECT u.id, u.name, u.age, p.address, p.phone
FROM users u
LEFT JOIN profiles p ON u.id = p.user_id
WHERE u.id = #{id}
</select>
<resultMap id="UserResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="profile" javaType="Profile">
<result property="address" column="address"/>
<result property="phone" column="phone"/>
</association>
</resultMap>
- 一对多关联查询:
- 嵌套查询:通过在主查询中使用子查询来获取关联对象的集合。
- 关联查询:使用JOIN语句将主表和关联表进行连接查询,并使用GROUP BY或者DISTINCT来去重。
举例说明一对多关联查询:
- 使用嵌套查询:
<select id="getUserWithOrders" resultType="User">
SELECT u.id, u.name, u.age, o.order_id, o.order_name
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
- 使用关联查询:
<select id="getUserWithOrders" resultMap="UserResultMap">
SELECT u.id, u.name, u.age, o.order_id, o.order_name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
<resultMap id="UserResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<collection property="orders" ofType="Order">
<id property="orderId" column="order_id"/>
<result property="orderName" column="order_name"/>
</collection>
</resultMap>
以上是一对一和一对多关联查询的示例,可以根据具体的数据表结构和查询需求进行适当的配置。
17、MyBatis 实现一对一有几种方式?具体怎么操作的?
MyBatis实现一对一关联查询有以下几种方式:
- 嵌套查询(Nested Queries):通过在主查询中使用子查询来获取关联对象的信息。
具体操作步骤:
- 在Mapper XML文件中编写主查询的SQL语句,通过传入的参数进行查询。
- 在主查询的结果集中使用子查询,通过关联字段查询关联对象的信息。
- 将主查询和子查询的结果进行组装,形成一对一的关联关系。
举例说明:
假设有两个表,users和profiles,其中users表包含id、name和age字段,profiles表包含user_id和address字段。要实现一对一关联查询,可以按照以下方式操作:
在Mapper XML文件中编写SQL语句:
<select id="getUserWithProfile" resultType="User">
SELECT u.id, u.name, u.age, p.address
FROM users u
INNER JOIN profiles p ON u.id = p.user_id
WHERE u.id = #{id}
</select>
在Java代码中调用Mapper方法:
User user = userMapper.getUserWithProfile(1);
通过以上方式,可以实现一对一关联查询,获取用户及其对应的地址信息。
- 延迟加载(Lazy Loading):在需要时才加载关联对象的信息,避免一次性查询所有关联对象的数据。
具体操作步骤:
- 在Mapper XML文件中编写主查询的SQL语句,只查询主对象的信息,不包含关联对象。
- 将关联对象的查询延迟加载,即在需要访问关联对象时才执行额外的查询操作。
举例说明:
假设有两个表,users和orders,其中users表包含id、name和age字段,orders表包含order_id和order_name字段。要实现一对一关联查询,可以按照以下方式操作:
在Mapper XML文件中编写SQL语句:
<select id="getUserWithOrders" resultType="User">
SELECT u.id, u.name, u.age
FROM users u
WHERE u.id = #{id}
</select>
<select id="getOrdersByUserId" resultType="Order">
SELECT order_id, order_name
FROM orders
WHERE user_id = #{userId}
</select>
在Java代码中调用Mapper方法:
User user = userMapper.getUserWithOrders(1);
List<Order> orders = orderMapper.getOrdersByUserId(user.getId());
user.setOrders(orders);
通过以上方式,可以实现一对一关联查询,先查询主对象的信息,然后在需要时再查询关联对象的信息。
需要根据具体的业务需求和数据表结构选择合适的方式来实现一对一关联查询。每种方式都有其适用的场景和优缺点,根据具体情况选择最合适的方式。
18、MyBatis 实现一对多有几种方式,怎么操作的?
MyBatis实现一对多关联查询有以下几种方式:
- 嵌套查询(Nested Queries):通过在主查询中使用子查询来获取关联对象的集合。
具体操作步骤:
- 在Mapper XML文件中编写主查询的SQL语句,通过传入的参数进行查询。
- 在主查询的结果集中使用子查询,通过关联字段查询关联对象的集合。
- 将主查询和子查询的结果进行组装,形成一对多的关联关系。
举例说明:
假设有两个表,users和orders,其中users表包含id、name和age字段,orders表包含order_id和order_name字段。要实现一对多关联查询,可以按照以下方式操作:
在Mapper XML文件中编写SQL语句:
<select id="getUserWithOrders" resultType="User">
SELECT u.id, u.name, u.age, o.order_id, o.order_name
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
在Java代码中调用Mapper方法:
User user = userMapper.getUserWithOrders(1);
通过以上方式,可以实现一对多关联查询,获取用户及其对应的订单集合。
- 延迟加载(Lazy Loading):在需要时才加载关联对象的集合,避免一次性查询所有关联对象的数据。
具体操作步骤:
- 在Mapper XML文件中编写主查询的SQL语句,只查询主对象的信息,不包含关联对象。
- 将关联对象的查询延迟加载,即在需要访问关联对象时才执行额外的查询操作。
举例说明:
假设有两个表,users和orders,其中users表包含id、name和age字段,orders表包含order_id和order_name字段。要实现一对多关联查询,可以按照以下方式操作:
在Mapper XML文件中编写SQL语句:
<select id="getUser" resultType="User">
SELECT id, name, age
FROM users
WHERE id = #{id}
</select>
<select id="getOrdersByUserId" resultType="Order">
SELECT order_id, order_name
FROM orders
WHERE user_id = #{userId}
</select>
在Java代码中调用Mapper方法:
User user = userMapper.getUser(1);
List<Order> orders = orderMapper.getOrdersByUserId(user.getId());
user.setOrders(orders);
通过以上方式,可以实现一对多关联查询,先查询主对象的信息,然后在需要时再查询关联对象的集合。
需要根据具体的业务需求和数据表结构选择合适的方式来实现一对多关联查询。每种方式都有其适用的场景和优缺点,根据具体情况选择最合适的方式。
19、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
是的,MyBatis支持延迟加载(Lazy Loading)。延迟加载是指在需要访问关联对象时才执行额外的查询操作,避免一次性查询所有关联对象的数据,提高性能和效率。
MyBatis的延迟加载实现原理如下:
1. 当查询主对象时,MyBatis只会加载主对象的数据,不会立即加载关联对象的数据。
2. 当访问关联对象时,MyBatis会触发额外的查询操作,根据关联关系查询关联对象的数据。
3. MyBatis使用动态代理(Dynamic Proxy)技术,对关联对象进行代理,拦截关联对象属性的访问。
4. 当访问关联对象属性时,代理对象会判断是否已经加载过关联对象的数据,如果未加载,则执行额外的查询操作,并将查询结果赋值给关联对象属性。
通过以上机制,MyBatis实现了延迟加载的功能。它可以根据需要动态地加载关联对象的数据,避免一次性查询所有关联对象的数据,提高查询效率和性能。
需要注意的是,延迟加载只能在存在关联关系的情况下使用,且需要在配置文件中进行相应的配置才能生效。同时,延迟加载也可能引发懒加载异常(LazyInitializationException),需要在合适的时机进行加载操作,以避免异常的发生。
20、Mybatis 的一级、二级缓存是什么?
MyBatis的一级缓存和二级缓存是用于提高查询性能的缓存机制。
-
一级缓存(Local Cache):
- 一级缓存是MyBatis默认开启的缓存机制,它是基于SqlSession的缓存。
- 在同一个SqlSession中,如果执行相同的查询语句,MyBatis会将查询结果缓存在一级缓存中。
- 当再次执行相同的查询语句时,MyBatis会直接从一级缓存中获取结果,避免了再次查询数据库。
- 一级缓存的作用域是SqlSession级别的,当SqlSession关闭或清空缓存时,一级缓存也会被清除。
-
二级缓存(Second Level Cache):
- 二级缓存是在SqlSessionFactory级别的缓存,多个SqlSession共享同一个二级缓存。
- 二级缓存可以跨越多个SqlSession,当多个SqlSession执行相同的查询语句时,可以从二级缓存中获取结果。
- 二级缓存需要在Mapper XML文件中进行配置,开启并指定缓存的实现方式。
- 默认情况下,MyBatis使用PerpetualCache作为二级缓存的实现,也可以使用其他的缓存实现,如Ehcache、Redis等。
- 二级缓存的作用域是Mapper级别的,可以通过配置来设置是否开启二级缓存以及缓存的刷新策略。
需要注意的是,一级缓存和二级缓存是两个独立的缓存机制,一级缓存是默认开启的,而二级缓存需要进行配置才能生效。在使用缓存时,需要根据具体的业务需求和数据访问模式来选择合适的缓存策略。
21、什么是 MyBatis 的接口绑定?有哪些实现方式?
MyBatis的接口绑定是指将Mapper接口与对应的Mapper XML文件进行绑定,实现接口中定义的方法与对应的SQL语句的映射关系。通过接口绑定,可以使用Java接口来定义数据库操作的方法,使代码更加清晰和易于维护。
MyBatis提供了多种方式来实现接口绑定:
- XML配置文件:在MyBatis的配置文件中,使用
<mapper>
元素来引入Mapper XML文件,将接口与XML文件进行绑定。
<mappers>
<mapper resource="com/example/UserMapper.xml"/>
</mappers>
- 注解:在Mapper接口上使用注解,直接将接口方法与SQL语句进行绑定。
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(Long id);
- Java API:通过Java API来实现接口绑定,动态创建Mapper接口的实现类。
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1L);
通过以上方式,可以实现接口绑定,将Mapper接口与对应的SQL语句进行映射。根据具体的项目需求和开发习惯,选择合适的实现方式。
22、使用 MyBatis 的 mapper 接口调用时有哪些要求?
使用MyBatis的Mapper接口调用时有以下要求:
1. 定义Mapper接口
:需要定义一个Java接口,用于声明数据库操作的方法。
2. 方法命名规范
:Mapper接口中的方法名称应与对应的Mapper XML文件中定义的SQL语句的id保持一致。
3. 方法参数
:方法的参数应与SQL语句中的参数一致,可以使用注解或者XML配置来指定参数的名称。
4. 返回值类型
:方法的返回值类型应与SQL语句查询结果的类型一致,可以是实体类、Map、List等。
5. Mapper XML文件
:需要编写对应的Mapper XML文件,定义SQL语句和结果映射等。
6. 接口与XML文件的绑定
:接口与对应的XML文件需要进行绑定,可以通过XML配置文件、注解或者Java API来实现。
7. 数据库连接配置
:需要在MyBatis的配置文件中配置数据库连接信息,包括数据库驱动、连接URL、用户名、密码等。
8. 使用SqlSession执行
:通过SqlSession对象获取Mapper接口的实例,然后调用接口中的方法执行数据库操作。
需要遵循以上要求,才能正确地使用MyBatis的Mapper接口进行数据库操作。根据具体的业务需求和开发习惯,选择合适的方式来定义Mapper接口和编写对应的SQL语句。
23、Mapper 编写有哪几种方式?
Mapper编写有以下几种方式:
-
XML方式:使用Mapper XML文件来编写SQL语句和映射配置。在XML文件中定义SQL语句和结果映射关系。
示例:
<!-- Mapper XML文件 --> <mapper namespace="com.example.UserMapper"> <select id="getUserById" resultType="User"> SELECT * FROM users WHERE id = #{id} </select> </mapper>
-
注解方式:使用注解在Mapper接口的方法上直接编写SQL语句和映射配置。
示例:
// Mapper接口 public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{id}") User getUserById(Long id); }
-
基于Java API方式:使用MyBatis提供的Java API来动态地构建SQL语句和映射配置。
示例:
// Mapper接口 public interface UserMapper { @SelectProvider(type = UserSqlProvider.class, method = "getUserByIdSql") User getUserById(Long id); } // SQL构建类 public class UserSqlProvider { public String getUserByIdSql() { return "SELECT * FROM users WHERE id = #{id}"; } }
以上是Mapper编写的几种常见方式,每种方式都有其适用的场景和优缺点。根据具体的需求和开发习惯,选择合适的方式来编写Mapper。
24、简述 Mybatis 的插件运行原理,以及如何编写一个插件?
MyBatis的插件运行原理是基于动态代理和拦截器链的机制。当MyBatis执行数据库操作时,会使用JDK动态代理生成Mapper接口的代理对象。在生成代理对象时,会根据配置的插件信息,将插件应用到代理对象上。当执行Mapper方法时,代理对象会依次调用拦截器链中的拦截器方法,从而实现对SQL语句和结果的拦截和修改。
编写一个MyBatis插件的步骤如下:
-
创建一个实现了Interceptor接口的类,该类用于定义插件的具体逻辑。
示例:
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 在这里编写插件的逻辑
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// 使用Plugin.wrap方法生成代理对象
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 设置插件的属性
}
}
-
在MyBatis配置文件中配置插件。
示例:
<plugins>
<plugin interceptor="com.example.MyPlugin">
<!-- 设置插件的属性 -->
<property name="property1" value="value1"/>
<property name="property2" value="value2"/>
</plugin>
</plugins>
-
注册插件到SqlSessionFactory或Configuration中。
示例:
Interceptor myPlugin = new MyPlugin();
sqlSessionFactory.getConfiguration().addInterceptor(myPlugin);
通过以上步骤,就可以编写一个自定义的MyBatis插件。插件可以在SQL语句执行前后、参数处理前后等环节进行拦截和修改,实现一些自定义的功能,如日志记录、性能监控、分页等。插件的编写需要根据具体需求来实现相应的逻辑。
25. MyBatis 逻辑分页和物理分页的区别是什么?
MyBatis的逻辑分页和物理分页是两种不同的分页方式,它们的区别如下:
1. 逻辑分页(Logical Pagination)
:逻辑分页是在查询结果集中进行分页操作,即在查询结果已经返回的情况下,通过程序对结果进行分页处理。逻辑分页是在内存中进行的,不需要额外的数据库操作。
2. 物理分页(Physical Pagination)
:物理分页是在数据库层面进行分页操作,即通过SQL语句的LIMIT或者ROWNUM关键字来限制查询结果的数量和起始位置。物理分页是通过数据库的查询优化来实现的。
区别:
- 执行时机:逻辑分页是在查询结果已经返回后进行分页处理,而物理分页是在数据库执行查询时就进行分页操作。
- 数据量:逻辑分页是将整个结果集加载到内存中,然后进行分页处理,适用于数据量较小的情况。物理分页是在数据库层面进行分页,只返回指定页的数据,适用于处理大数据量的情况。
- 性能:逻辑分页对于小数据量的分页效果较好,但对于大数据量的分页可能导致性能问题。物理分页通过数据库的查询优化,可以在数据库层面高效地返回指定页的数据。
在实际应用中,根据具体的需求和数据量大小选择合适的分页方式。如果数据量较小且要求灵活的分页操作,可以选择逻辑分页;如果数据量较大且需要高效的分页操作,可以选择物理分页。
26.MyBatis 是否支持延迟加载?延迟加载的原理是什么?
支持的,MyBatis支持延迟加载(Lazy Loading)。
延迟加载是一种延迟加载关联对象的策略,在需要访问关联对象时才真正执行查询操作,避免一次性查询所有关联对象的数据,提高查询效率和性能。
延迟加载的原理是通过MyBatis的动态代理机制实现的。当查询主对象时,关联对象的属性会被代理为延迟加载对象,只有当访问关联对象属性时,才会触发额外的查询操作,获取关联对象的数据。
具体实现步骤如下:
1. 在Mapper XML文件中编写主查询的SQL语句,只查询主对象的信息,不包含关联对象。
2. 在Java代码中调用Mapper方法,查询主对象时,关联对象的属性会被代理为延迟加载对象。
3. 当访问关联对象属性时,MyBatis会通过代理机制,执行额外的查询操作,获取关联对象的数据。
延迟加载可以有效地减少不必要的查询操作,提高查询效率,特别适用于关联对象数据量较大、访问频率较低的场景。
需要注意的是,延迟加载只能在存在关联关系的对象之间进行,且需要配置合适的映射关系和查询语句,以支持延迟加载的操作。
27.MyBatis 有哪些执行器(Executor)?
MyBatis有三种执行器(Executor):
1. SimpleExecutor
:简单执行器,每次请求都会创建一个Statement对象,执行完毕后立即关闭Statement对象。
2. ReuseExecutor
:可重用执行器,会重用已经创建的Statement对象,如果存在相同的SQL语句,则复用已经存在的Statement对象。
3. BatchExecutor
:批处理执行器,用于执行批量操作,将多个SQL语句一起提交给数据库执行。
这些执行器在MyBatis中负责执行SQL语句并处理查询结果。每个执行器都有不同的特点和适用场景,可以根据具体的需求和性能要求选择合适的执行器。默认情况下,MyBatis使用SimpleExecutor作为默认的执行器。
28. MyBatis 分页插件的实现原理是什么?
MyBatis分页插件的实现原理是通过拦截器(Interceptor)来拦截SQL查询,并在查询过程中自动添加分页的SQL语句。
具体的实现步骤如下:
-
自定义一个实现了MyBatis的
Interceptor
接口的分页拦截器。 -
在拦截器中重写
intercept
方法,拦截SQL查询,并根据分页参数自动添加分页的SQL语句。 -
将拦截器配置到MyBatis的配置文件中,使其生效。
在 intercept
方法中,拦截器会获取到执行的SQL语句,并根据分页参数动态生成相应的分页SQL语句。拦截器还可以根据具体的数据库类型,使用不同的分页语法,如MySQL的 LIMIT
语句,Oracle的 ROWNUM
语句等。
拦截器还可以根据配置的参数,自动计算总记录数,并设置到分页结果对象中,方便后续的分页信息展示。
通过分页插件的实现,可以在不修改原始SQL语句的情况下,实现分页查询的功能,提供更加便捷的分页操作。
总的来说,MyBatis分页插件通过拦截器机制,在SQL查询过程中动态添加分页的SQL语句,实现了分页查询的功能。