目录
1.什么是Mybatis?
2.Mybatis的优缺点?
3.#{} 和 ${} 的区别是什么?
4.xml 映射文件中有哪些标签?
5.模糊查询 like 语句该怎么写?
6.Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗?
7.MyBatis 是如何进行分页的?分页插件的原理是什么?
8.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
9.如何执行批量插入?能返回主键id吗
10.MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
11.MyBatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。
12.MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?
13.MyBatis 的 xml 映射文件中,不同的 xml 映射文件的id 是否可以重复?
14.MyBatis 都有哪些 Executor 执行器?它们之间的区别是什么?
15.MyBatis 中如何指定使用哪一种 Executor 执行器?
16.MyBatis 是否可以映射 Enum 枚举类?
17.简述 MyBatis 的 xml 映射文件和 MyBatis 内部数据结构之间的映射关系?
18.为什么说 MyBatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
19.Mybatis 的一级、二级缓存?
20.简述 Mybatis 的插件运行原理,以及如何编写一个插件
Mybatis是一种流行的Java对象关系映射(ORM)框架,它将Java对象映射到关系型数据库中的表格。它提供了一种简单的方式来编写SQL语句并将其映射到Java对象,同时避免了很多传统JDBC的冗长代码。在使用Mybatis时,开发人员可以编写简洁的SQL语句,并且可以使用动态SQL和参数映射来进行高效和灵活的数据操作。
除了ORM之外,Mybatis还提供了许多高级功能,如缓存机制、批量操作、分页等等。它可以与多种数据库(如MySQL、Oracle、SQL Server等)和Web框架(如Spring、Spring Boot等)无缝集成,并被广泛应用于各种规模的应用程序中。
Mybatis的优点:
Mybatis的缺点:
#{}使用预编译的方式来处理SQL语句中的参数,将传入的参数值以安全的方式替换掉占位符。在SQL语句执行前,会先将#{}替换为一个问号占位符,然后使用PreparedStatement进行预编译,最后将实际的参数值设置到预编译语句中。使用#{}可以有效地防止SQL注入等安全问题,同时也可以避免一些数据类型转换的问题。
${}则是直接将参数值替换到SQL语句中。在SQL语句执行前,会直接将${}替换为对应的参数值,这种方式的好处是可以直接拼接字符串,但也带来了一些安全问题。使用${}时需要开发人员自行保证参数的合法性,否则可能会出现SQL注入等安全问题。
例如:
SELECT * FROM user WHERE name = '${name}'
如果$name的值是 "admin' OR '1'='1",则该SQL语句会查询出所有用户的数据,而不是仅查询用户名为"admin"的用户数据,造成了安全风险。
下面是两种常见的模糊查询方式:
1.使用占位符(%)进行模糊查询
SQL语句示例:
SELECT * FROM table WHERE column LIKE '%keyword%';
XML映射文件示例:
在XML映射文件和注解中,可以使用CONCAT函数将占位符与参数进行拼接,生成完整的LIKE语句。
2.使用$符号进行模糊查询
SQL语句示例:
SELECT * FROM table WHERE column LIKE '%${keyword}%';
XML映射文件示例:
在XML映射文件和注解中,可以使用${}占位符将参数直接拼接到SQL语句中,生成完整的LIKE语句。需要注意的是,使用${}方式进行模糊查询存在SQL注入的风险,不建议在生产环境中使用。
Mapper接口是Mybatis提供的一种编写SQL映射的方式,可以将SQL语句与Java方法进行关联,使得Java开发人员可以使用面向对象的方式来编写数据访问代码。Mapper接口的工作原理是:
至于Mapper接口中的方法重载问题,答案是:不可以。在Mapper接口中,每个方法名对应着一个唯一的SQL语句ID,方法名相同会造成冲突,因此Mapper接口中的方法不允许重载。如果需要执行不同的SQL语句,可以使用不同的方法名来实现。
1.MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;
2.MyBatis可以使用SQL语句中的LIMIT关键字实现简单的分页,但是对于大数据量的分页查询,需要使用更高效的方法。
3.MyBatis提供了一种基于拦截器的分页插件来优化分页查询。
分页插件的原理是在MyBatis执行SQL语句之前,对其进行拦截并修改SQL语句,从而实现分页查询的功能。具体步骤如下:
目前比较流行的MyBatis分页插件是PageHelper,其基于拦截器的原理,在拦截器中实现了分页查询的逻辑,并提供了丰富的配置选项和API,可以方便地实现各种复杂的分页查询需求。使用PageHelper进行分页查询的示例代码如下:
// 指定分页查询的页码和每页记录数
PageHelper.startPage(pageNum, pageSize);
// 执行查询操作,返回分页查询结果
List userList = userDao.selectUserList();
// 封装分页查询结果
PageInfo pageInfo = new PageInfo<>(userList);
在这段代码中,我们使用了PageHelper的startPage方法来指定分页查询的页码和每页记录数,然后执行普通的查询操作,PageHelper会自动拦截并修改SQL语句,实现分页查询的功能。最后,我们将查询结果封装到PageInfo对象中,以便进行分页信息的显示和处理。
MyBatis将SQL执行结果封装为目标对象并返回,主要通过以下两个步骤来完成:
MyBatis支持多种映射形式,常用的有以下三种:
其中,基于XML的映射是最常用的一种方式。在XML配置文件中,我们可以使用
在这段代码中,我们首先定义了一个名为userResultMap的
除了
要执行批量插入操作,可以使用 MyBatis 提供的 batch 执行器和 foreach 标签。
首先,我们需要将需要插入的数据封装到一个 List 或数组中。然后,通过 foreach 标签将数据逐个插入到数据库中。
示例如下:
INSERT INTO user(name, age) VALUES(#{user.name}, #{user.age})
在这个示例中,我们定义了一个名为 batchInsertUsers 的 SQL 语句,该语句接受一个 List 类型的参数。通过 foreach 标签,将 List 中的每个元素逐个插入到数据库中。
如果需要返回插入数据的主键 ID,可以使用 MyBatis 提供的 useGeneratedKeys 和 keyProperty 属性。
示例如下:
INSERT INTO user(name, age) VALUES(#{user.name}, #{user.age})
在这个示例中,我们在 SQL 语句中添加了 useGeneratedKeys="true" 属性,表示使用数据库自动生成的主键 ID。然后,通过 keyProperty 属性将主键 ID 映射到 Java 对象的属性中。执行完成后,MyBatis 会将插入数据的主键 ID 封装到 Java 对象中返回。注意,在使用自动生成主键 ID 时,需要确保数据库表的主键字段设置为自增长或序列类型。
MyBatis 动态 SQL 是为了解决 SQL 语句灵活性不足的问题而提出的一种技术。动态 SQL 可以根据条件拼接 SQL 语句,从而满足不同的查询需求。MyBatis 提供了以下几种动态 SQL 标签:
动态 SQL 的执行原理是,当 MyBatis 执行动态 SQL 语句时,会将 SQL 语句和参数传递给 SQL 解析器进行解析。SQL 解析器会根据 SQL 语句中的动态标签和参数的值,生成一个完整的 SQL 语句。然后,MyBatis 将生成的 SQL 语句和参数传递给 JDBC 驱动程序进行执行。最终,JDBC 驱动程序将执行结果返回给 MyBatis。
使用动态 SQL 可以使 SQL 语句更灵活、更具可读性和可维护性,也可以提高应用程序的性能和效率。但是,需要注意使用动态 SQL 时要避免 SQL 注入攻击。建议使用 MyBatis 提供的参数绑定功能,将参数值与 SQL 语句分离,从而避免 SQL 注入攻击。
是的,MyBatis 可以执行一对一、一对多的关联查询。
在 MyBatis 中,实现一对一、一对多关联查询的方式有两种:
两种方式的区别在于执行方式和性能。嵌套查询会执行多条 SQL 语句,每个 SQL 语句返回一个结果集,最终将结果集合并为一个对象;而嵌套结果只执行一条 SQL 语句,返回一个结果集,然后将结果集中的数据映射到对象中。
总的来说,如果数据量较小,建议使用嵌套查询;如果数据量较大,建议使用嵌套结果。但需要注意的是,使用嵌套结果时需要在 SQL 语句中使用 JOIN 操作,所以需要对 SQL 语句的性能进行优化,以避免出现慢查询的情况。同时,在定义 ResultMap 和 SQL 语句时需要注意标签的使用和语句的正确性,以确保关联查询的正确性和性能。
对的,MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,其中 association 指的是一对一,collection 指的是一对多查询。
在 MyBatis 配置文件中,可以通过设置 lazyLoadingEnabled 属性来启用或禁用延迟加载。当 lazyLoadingEnabled 设置为 true 时,MyBatis 将会开启延迟加载功能。此时,只有在实际使用对象时才会触发关联对象的查询操作,否则不会查询数据库。
MyBatis 延迟加载的原理是,在执行主查询语句时,仅仅查询主表的数据,而不去查询关联表的数据,将关联表的查询延迟到实际需要使用关联对象时再去查询。MyBatis 通过使用代理模式,在查询主表数据时创建代理对象,当实际需要使用关联对象时,再去查询关联表数据并设置到代理对象中,从而实现了延迟加载。
在具体实现时,MyBatis 通过 CGLIB 或 JDK 动态代理来创建代理对象,当代理对象的方法被调用时,会触发代理拦截器的方法。拦截器会检查关联对象是否已经加载,如果未加载,则发送延迟加载的 SQL 语句查询关联表数据,并将查询结果设置到代理对象中。这样,在实际使用关联对象时,就可以避免不必要的关联表查询,从而提高查询性能。
需要注意的是,MyBatis 的延迟加载功能需要配合 lazyLoadingEnabled 属性一起使用,并且需要遵循一些规范和约束,如不能在延迟加载时关闭 SqlSession,不能使用多线程并发访问等。同时,在使用延迟加载时,也需要注意潜在的 N+1 查询问题,需要合理设计 SQL 查询语句,避免不必要的性能损耗。
不同的 xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置 namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。
原因就是 namespace+id 是作为 Map
MyBatis 有三种 Executor 执行器,分别是:
其中,SimpleExecutor 是每次请求都会创建一个 Statement 对象,非常耗费性能;ReuseExecutor 在多次请求中,如果有相同的 SQL 语句,就会复用 Statement 对象,优化了性能;BatchExecutor 是执行批量操作时使用,可以一次性执行多条 SQL 语句,优化性能。
在 MyBatis 中,默认使用的是 SimpleExecutor 执行器。可以通过在 MyBatis 的配置文件中指定
在 MyBatis 的配置文件中可以通过 defaultExecutorType 属性来指定默认的执行器类型
另外,针对每个 、
是的,MyBatis可以映射Java中的Enum枚举类。MyBatis提供了两种方式来映射Enum枚举类:
1.使用EnumTypeHandler:EnumTypeHandler是MyBatis内置的类型处理器之一,它可以将Java中的Enum枚举类与数据库中的数据进行相互转换。
例如,在Mapper XML文件中,可以这样使用EnumTypeHandler来映射枚举类型:
在这个例子中,我们将数据库中的gender列与Java中的User类的gender属性进行映射,并使用EnumTypeHandler类型处理器进行转换。
2.使用注解@EnumValue:如果在枚举类中使用了@EnumValue注解,则MyBatis会自动将枚举值与数据库中的数据进行相互转换。
例如,在定义枚举类时,可以使用@EnumValue注解来标识该枚举值对应的数据库中的值:
public enum Gender {
MALE("M"),
FEMALE("F");
private String value;
Gender(String value) {
this.value = value;
}
@EnumValue
public String getValue() {
return value;
}
}
在这个例子中,我们将MALE枚举值对应的数据库值设为"M",将FEMALE枚举值对应的数据库值设为"F"。然后,在Mapper XML文件中,就可以直接使用枚举类型进行映射,例如:
在这个例子中,MyBatis会根据@EnumValue注解中定义的值,将数据库中的gender列与Java中的User类的gender属性进行相互转换。
MyBatis 将 XML 映射文件中的配置信息映射成 Configuration 对象,该对象是 MyBatis 中重要的数据结构之一,包含了所有的配置信息,包括数据库连接信息、映射文件信息、缓存配置信息等等。Configuration 对象内部包含许多其他对象,包括:
在解析 XML 映射文件时,MyBatis 会将
MyBatis 是半自动 ORM 映射工具,它需要手动编写 SQL 语句并将 SQL 语句和 Java 对象进行映射。相比之下,Hibernate 是全自动 ORM 映射工具,它使用对象关系映射技术,将 Java 对象和数据库表进行映射,无需手动编写 SQL 语句。因此,在使用上,MyBatis 更加灵活,可以根据具体的需求编写灵活的 SQL 语句,而 Hibernate 更加便捷,可以快速地进行开发,无需手动编写 SQL 语句。
另外,Hibernate 支持级联操作和对象关系维护,可以自动完成多个对象之间的关联操作,而 MyBatis 不支持级联操作,需要手动编写 SQL 语句来完成多个对象之间的关联操作。
一级缓存是指MyBatis在同一个SqlSession中执行相同SQL时,会把查询到的结果缓存到内存中。当下次查询相同SQL时,会直接从缓存中获取数据,避免了重复查询数据库,提高了查询效率。一级缓存是默认开启的,也不需要进行额外配置。
二级缓存是指MyBatis在多个SqlSession之间共享缓存。它可以避免多个SqlSession重复查询同一条数据,提高了应用的性能。但是,使用二级缓存需要进行额外的配置,包括在mapper.xml文件中配置
需要注意的是,虽然缓存可以提高应用的性能,但是缓存也有可能带来一些问题,如数据不一致等。因此,在使用缓存时需要根据具体情况进行合理的配置。
MyBatis 的插件机制可以在某些语句执行前、后拦截并执行自定义的处理逻辑,可以用于日志记录、参数处理、数据加密等功能。其实现原理是基于 Java 动态代理技术实现的。
MyBatis 插件机制涉及以下两个接口:
当创建 SqlSession 对象时,MyBatis 会将插件按顺序进行包装,最终生成一个包含了所有插件功能的代理对象,当执行 SQL 语句时,会先经过代理对象,然后在代理对象中执行插件逻辑。
自定义插件需要实现 Interceptor 接口,并且实现 intercept() 方法来拦截需要增强的方法,然后使用 @Intercepts 和 @Signature 注解对拦截器进行配置。
例如,定义一个打印 SQL 执行时间的插件,实现过程如下:
1.创建自定义插件类实现 Interceptor 接口
@Intercepts(@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}))
public class PrintSqlTimeInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
long sqlTime = endTime - startTime;
if (sqlTime > 1000) {
System.out.println("SQL execution time is too long: " + sqlTime + "ms");
}
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {}
}
2.使用 @Intercepts 和 @Signature 注解对拦截器进行配置。
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MyPlugin implements Interceptor {
//...
}
3.在 MyBatis 配置文件中配置插件
注意,插件的顺序也很重要,MyBatis 将按照插件在配置文件中的顺序依次执行。