MyBatis常见面试题汇总(超详细回答)
1.什么是Mybatis?
Mybatis是一种流行的Java对象关系映射(ORM)框架,它将Java对象映射到关系型数据库中的表格。它提供了一种简单的方式来编写SQL语句并将其映射到Java对象,同时避免了很多传统JDBC的冗长代码。在使用Mybatis时,开发人员可以编写简洁的SQL语句,并且可以使用动态SQL和参数映射来进行高效和灵活的数据操作。
除了ORM之外,Mybatis还提供了许多高级功能,如缓存机制、批量操作、分页等等。它可以与多种数据库(如MySQL、Oracle、SQL Server等)和Web框架(如Spring、Spring Boot等)无缝集成,并被广泛应用于各种规模的应用程序中。
2.Mybatis的优缺点?
Mybatis的优点:
灵活性强:Mybatis允许开发人员编写自定义SQL语句,能够根据项目需求进行灵活配置和扩展,同时可以避免框架的约束和限制。
可读性好:Mybatis使用简洁的XML或注解配置方式,可以清晰地表达SQL语句和数据映射关系,提高代码的可读性和可维护性。
性能高:Mybatis提供了高效的缓存机制,能够有效地减少数据库操作的次数,并支持批量操作和分页查询等功能,能够提高应用程序的性能。
易于集成:Mybatis可以与多种数据库和Web框架无缝集成,能够适应不同的应用场景,同时也有完善的文档和社区支持。
Mybatis的缺点:
学习成本较高:相比于其他ORM框架,Mybatis需要开发人员熟悉SQL语句的编写和调优,需要一定的学习成本。
配置较为繁琐:Mybatis的配置文件较多,需要开发人员仔细配置,否则容易出现错误。
SQL语句调试困难:Mybatis将SQL语句和Java代码分离,当SQL语句出现问题时,调试起来相对困难。
不适合小型项目:对于小型项目而言,Mybatis的优势可能不够明显,反而会增加项目的开发成本和复杂度。
3.#{} 和 ${} 的区别是什么?
#{}使用预编译的方式来处理SQL语句中的参数,将传入的参数值以安全的方式替换掉占位符。在SQL语句执行前,会先将#{}替换为一个问号占位符,然后使用PreparedStatement进行预编译,最后将实际的参数值设置到预编译语句中。使用#{}可以有效地防止SQL注入等安全问题,同时也可以避免一些数据类型转换的问题。
${}则是直接将参数值替换到SQL语句中。在SQL语句执行前,会直接将${}替换为对应的参数值,这种方式的好处是可以直接拼接字符串,但也带来了一些安全问题。使用${}时需要开发人员自行保证参数的合法性,否则可能会出现SQL注入等安全问题。
例如:
SELECT * FROM user WHERE name = '${name}'
如果$name的值是 "admin' OR '1'='1",则该SQL语句会查询出所有用户的数据,而不是仅查询用户名为"admin"的用户数据,造成了安全风险。
4.xml 映射文件中有哪些标签?
CRUD操作标签:包括select、insert、update、delete等标签,用于定义对数据库的增、删、改、查操作。这些标签都包括id=repository.java 里面的具体函数同名、入参parameterType=string,list,vo都行、返回结果resultType用于指定SQL语句的ID、入参参数类型和返回结果类型等信息。
结果集映射标签:包括<resultMap>,用于定义Java对象和数据库表之间的映射关系,可以将查询结果集映射为Java对象的属性。resultType=string,int等。
返回结果resultMap=具体的vo但是select的结果必须as和类变量一样属性。
返回结果resultMap=ID,然后在ID里面定义vo,column对应的vo字段。
SQL片段标签:包括<sql>和<include>标签,用于定义可重用的SQL代码片段。<sql>标签用于定义SQL语句的代码片段,可以在其他SQL语句中使用,<include>标签用于将其他XML片段引入到当前XML文件中,可以用于复用其他XML片段中定义的SQL语句。
动态SQL标签:包括<if>、<choose>、<when>、<otherwise>、<where>、<set>、<foreach>和<bind>等标签,用于动态生成SQL语句中的条件判断、循环、赋值等操作。这些标签可以根据参数值动态生成SQL语句,提高SQL语句的复用性和可维护性。
5.模糊查询 like 语句该怎么写?
下面是两种常见的模糊查询方式:
1.使用占位符(%)进行模糊查询
SQL语句示例:
SELECT * FROM table WHERE column LIKE '%keyword%';
XML映射文件示例:
<select id="selectByKeyword" parameterType="java.lang.String" resultType="com.example.model.Table">
SELECT * FROM table WHERE column LIKE CONCAT('%',#{keyword},'%')
</select>
在XML映射文件和注解中,可以使用CONCAT函数将占位符与参数进行拼接,生成完整的LIKE语句。
2.使用$符号进行模糊查询
SQL语句示例:
SELECT * FROM table WHERE column LIKE '%${keyword}%';
XML映射文件示例:
<select id="selectByKeyword" parameterType="java.lang.String" resultType="com.example.model.Table">
SELECT * FROM table WHERE column LIKE '%${keyword}%'
</select>
在XML映射文件和注解中,可以使用${}占位符将参数直接拼接到SQL语句中,生成完整的LIKE语句。需要注意的是,使用${}方式进行模糊查询存在SQL注入的风险,不建议在生产环境中使用。
6.Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗?
Mapper接口是Mybatis提供的一种编写SQL映射的方式,可以将SQL语句与Java方法进行关联,使得Java开发人员可以使用面向对象的方式来编写数据访问代码。Mapper接口的工作原理是:
定义Mapper接口:首先,开发人员需要定义一个repository.java 接口,其中每个方法代表一条SQL语句,方法名与xml的SQL语句的ID一致,方法的参数和返回值类型与SQL语句的参数和返回值类型一致。
映射Mapper接口:然后,将Mapper接口与SQL语句进行映射,可以使用XML文件或注解的方式进行映射。映射的方式包括指定SQL语句的ID、参数类型、返回值类型等信息。
使用Mapper接口:最后,在Java代码中使用Mapper接口来进行数据访问,通过Mybatis框架将Mapper接口中的方法与SQL语句进行绑定,实现对数据库的访问操作。
至于Mapper接口中的方法重载问题,答案是:不可以。在Mapper接口中,每个方法名对应着一个唯一的SQL语句ID,方法名相同会造成冲突,因此Mapper接口中的方法不允许重载。如果需要执行不同的SQL语句,可以使用不同的方法名来实现。
7.MyBatis 是如何进行分页的?分页插件的原理是什么?
1.MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;
2.MyBatis可以使用SQL语句中的LIMIT关键字实现简单的分页(里面小于多少,外面大于多少,记得order by),但是对于大数据量的分页查询,需要使用更高效的方法。
3.MyBatis提供了一种基于拦截器的分页插件来优化分页查询。
分页插件的原理是在MyBatis执行SQL语句之前,对其进行拦截并修改SQL语句,从而实现分页查询的功能。具体步骤如下:
拦截器初始化:插件在MyBatis启动时进行初始化,创建拦截器对象并将其添加到MyBatis的拦截器链中。
拦截器拦截:当进行分页查询时,拦截器会拦截对应的SQL语句,并在其中添加分页的限制条件,例如使用LIMIT关键字进行分页。
执行SQL语句:修改后的SQL语句会被传递给MyBatis的执行器,执行器会将SQL语句发送到数据库进行查询,并返回查询结果。
封装查询结果:查询结果会被封装到MyBatis的结果对象中,并返回给调用方。
目前比较流行的MyBatis分页插件是PageHelper,其基于拦截器的原理,在拦截器中实现了分页查询的逻辑,并提供了丰富的配置选项和API,可以方便地实现各种复杂的分页查询需求。使用PageHelper进行分页查询的示例代码如下:
// 指定分页查询的页码和每页记录数
PageHelper.startPage(pageNum, pageSize);
// 执行查询操作,返回分页查询结果
List<User> userList = userDao.selectUserList();
// 封装分页查询结果
PageInfo<User> pageInfo = new PageInfo<>(userList);
在这段代码中,我们使用了PageHelper的startPage方法来指定分页查询的页码和每页记录数,然后执行普通的查询操作,PageHelper会自动拦截并修改SQL语句,实现分页查询的功能。最后,我们将查询结果封装到PageInfo对象中,以便进行分页信息的显示和处理。
8.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
MyBatis将SQL执行结果封装为目标对象并返回,主要通过以下两个步骤来完成:
映射SQL执行结果到结果集对象:在执行SQL查询之后,MyBatis将结果集中的每一行映射为一个结果集对象,并将所有结果集对象封装到一个List集合中。
映射结果集对象到目标对象:MyBatis根据配置文件中的映射关系,将结果集对象的属性值映射到目标对象的属性中,并返回映射后的目标对象。
MyBatis支持多种映射形式,常用的有以下三种:
基于XML的映射:通过XML配置文件来描述结果集对象和目标对象之间的映射关系。
基于注解的映射:通过Java注解来描述结果集对象和目标对象之间的映射关系。
基于API的映射:通过Java代码来描述结果集对象和目标对象之间的映射关系。
其中,基于XML的映射是最常用的一种方式。在XML配置文件中,我们可以使用<resultMap>标签来定义结果集对象和目标对象之间的映射关系,例如:
<!-- 定义结果集对象 -->
<resultMap id="userResultMap" type="com.example.User">
<result property="id" column="user_id" />
<result property="name" column="user_name" />
<result property="age" column="user_age" />
</resultMap>
<!-- 执行SQL查询 -->
<select id="selectUserList" resultMap="userResultMap">
SELECT user_id, user_name, user_age FROM user
</select>
在这段代码中,我们首先定义了一个名为userResultMap的<resultMap>标签,用于描述结果集对象和目标对象之间的映射关系。其中,type属性指定了目标对象的类型,<result>标签用于描述每个属性之间的映射关系。然后,在执行SQL查询时,我们指定了resultMap属性为userResultMap,表示使用这个映射关系来将结果集映射为目标对象。
除了<resultMap>标签,MyBatis还提供了其他的映射标签,例如<result>、<association>、<collection>等,可以根据实际需要来灵活配置映射关系。
9.如何执行批量插入?能返回主键id吗
要执行批量插入操作,可以使用 MyBatis 提供的 batch 执行器和 foreach 标签。
首先,我们需要将需要插入的数据封装到一个 List 或数组中。然后,通过 foreach 标签将数据逐个插入到数据库中。
示例如下:foreach 相当于for遍历,外面Java控制list大小100个。一般还用来in
<insert id="batchInsertUsers" parameterType="java.util.List">
<foreach collection="list" item="user" index="index" separator=";">
INSERT INTO user(name, age) VALUES(#{user.name}, #{user.age})
</foreach>
</insert>
在这个示例中,我们定义了一个名为 batchInsertUsers 的 SQL 语句,该语句接受一个 List 类型的参数。通过 foreach 标签,将 List 中的每个元素逐个插入到数据库中。
如果需要返回插入数据的主键 ID,可以使用 MyBatis 提供的 useGeneratedKeys 和 keyProperty 属性。
示例如下:
<insert id="batchInsertUsers" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
<foreach collection="list" item="user" index="index" separator=";">
INSERT INTO user(name, age) VALUES(#{user.name}, #{user.age})
</foreach>
</insert>
在这个示例中,我们在 SQL 语句中添加了 useGeneratedKeys="true" 属性,表示使用数据库自动生成的主键 ID。然后,通过 keyProperty 属性将主键 ID 映射到 Java 对象的属性中。执行完成后,MyBatis 会将插入数据的主键 ID 封装到 Java 对象中返回。注意,在使用自动生成主键 ID 时,需要确保数据库表的主键字段设置为自增长或序列类型。
二、mybatis ExecutorType.BATCH
Mybatis内置的ExecutorType有3种,默认的是simple,该模式下它为每个语句的执行创建一个新的预处理语句,单条提交sql;而batch模式重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能将更优; 但batch模式也有自己的问题,比如在Insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某型情形下是不符合业务要求的
三、JDBC插入
当然也可以使用原生的JDBC的方式进行批量插入,使用` statement.addBatch();的方式,也是很快的
Mybatis批量插入的四种方式_mybatis 批量插入-CSDN博客 这个写的简洁
mybatis的批量操作的几种方式_mybatis 批量-CSDN博客 这个补充啰嗦一点
10.MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
MyBatis 动态 SQL 是为了解决 SQL 语句灵活性不足的问题而提出的一种技术。动态 SQL 可以根据条件拼接 SQL 语句,从而满足不同的查询需求。MyBatis 提供了以下几种动态 SQL 标签:
<if>:条件判断标签,当条件成立时才执行其中的 SQL 语句。
<choose>、<when>、<otherwise>:选择判断标签,根据条件选择不同的 SQL 语句执行。
<trim>、<where>、<set>:SQL 语句修饰标签,用于在 SQL 语句的前后加上修饰字符,如 WHERE、AND、OR 等。
<foreach>:遍历标签,用于遍历一个集合并将集合中的元素添加到 SQL 语句中。
动态 SQL 的执行原理是,当 MyBatis 执行动态 SQL 语句时,会将 SQL 语句和参数传递给 SQL 解析器进行解析。SQL 解析器会根据 SQL 语句中的动态标签和参数的值,生成一个完整的 SQL 语句。然后,MyBatis 将生成的 SQL 语句和参数传递给 JDBC 驱动程序进行执行。最终,JDBC 驱动程序将执行结果返回给 MyBatis。
使用动态 SQL 可以使 SQL 语句更灵活、更具可读性和可维护性,也可以提高应用程序的性能和效率。但是,需要注意使用动态 SQL 时要避免 SQL 注入攻击。建议使用 MyBatis 提供的参数绑定功能,将参数值与 SQL 语句分离,从而避免 SQL 注入攻击。
11.MyBatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。
是的,MyBatis 可以执行一对一、一对多的关联查询。
在 MyBatis 中,实现一对一、一对多关联查询的方式有两种:
嵌套查询(Nested Query):通过在 ResultMap 中定义关联对象的 ResultMap,然后在 SQL 语句中使用嵌套查询(通常使用子查询)来完成关联查询。这种方式适用于数据量较小的场景,但如果数据量很大,可能会影响查询性能。
嵌套结果(Nested Results):通过在 ResultMap 中使用 Association 和 Collection 标签,定义关联对象的 ResultMap,然后通过查询结果集的方式来完成关联查询。这种方式适用于数据量较大的场景,因为它可以在一次 SQL 查询中完成关联查询,从而提高查询性能。
两种方式的区别在于执行方式和性能。嵌套查询会执行多条 SQL 语句,每个 SQL 语句返回一个结果集,最终将结果集合并为一个对象;而嵌套结果只执行一条 SQL 语句,返回一个结果集,然后将结果集中的数据映射到对象中。
总的来说,如果数据量较小,建议使用嵌套查询;如果数据量较大,建议使用嵌套结果。但需要注意的是,使用嵌套结果时需要在 SQL 语句中使用 JOIN 操作,所以需要对 SQL 语句的性能进行优化,以避免出现慢查询的情况。同时,在定义 ResultMap 和 SQL 语句时需要注意标签的使用和语句的正确性,以确保关联查询的正确性和性能。
一对一查询,订单明细对应一个订单头 association
一对多,用collection进行存储。用的不多,一个订单头对应多个订单明细
会自动进行封装,一般这个写法多一些。这种查询场景,还有就是批量查询,然后批量子查询最后拼接出来。肯定性能不如这种效果好。
MyBatis 的关联关系配置 一对多,一对一,多对多 关系的映射处理_mybatis left join 一对多 配置-CSDN博客 这个快速入门
mybatis中的一对一和一对多_mybatis如何配置一对一和一对多-CSDN博客 这个写的很细,是对的,看第3个快
【Mybatis 学习】Mybatis 延迟加载策略_mybatis fetchtype.eagle-CSDN博客 这个才是对的,简洁明了。上面第1个对于真正的子查询细节写错了,其实还是类似的,要定义repository.java 的函数,对应它的xml的id.上面那里xml回写这里一样的id,才关联起来的。这个核心细节没写。其实就是多个函数,通过id关联而已。Select用的多。
12.MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?
对的,MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,其中 association 指的是一对一,collection 指的是一对多查询。一对一还是一对多。其实就是对应一个还是多个而已,就是标记不同而已,通常一对多兼容性强一点。
一、何为延迟加载?
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据,延迟加载也称懒加载.目的是为了提升查询的性能
在 MyBatis 配置文件中,可以通过设置 lazyLoadingEnabled 属性来启用或禁用延迟加载。当 lazyLoadingEnabled 设置为 true 时,MyBatis 将会开启延迟加载功能(这个开关本质就是是否打开子查询的意思)。此时,只有在实际使用对象时才会触发关联对象的查询操作??看下面图片,否则不会查询数据库。
MyBatis 延迟加载的原理是,在执行主查询语句时,仅仅查询主表的数据,而不去查询关联表的数据,将关联表的查询延迟到实际需要使用关联对象时再去查询。MyBatis 通过使用代理模式,在查询主表数据时创建代理对象,当实际需要使用关联对象时,再去查询关联表数据并设置到代理对象中,从而实现了延迟加载。
一个订单头vo定义了一个list订单明细vo作为属性,当Java代码用到了才会去加载查询。
Java中mybatis是否支持延迟加载?延迟加载的原理是什么?-CSDN博客 这个具体细节
2.如果延迟加载的对象在外部Java代码中被修改或删除,那么在加载关联对象时可能会出现数据不一致的情况。因此,后面又使用延迟加载时,需要确保关联对象的数据是不变的。
在具体实现时,MyBatis 通过 CGLIB 或 JDK 动态代理来创建代理对象,当代理对象的方法被调用时,会触发代理拦截器的方法。拦截器会检查关联对象是否已经加载,如果未加载,则发送延迟加载的 SQL 语句查询关联表数据,并将查询结果设置到代理对象中。这样,在实际使用关联对象时,就可以避免不必要的关联表查询,从而提高查询性能。
需要注意的是,MyBatis 的延迟加载功能需要配合 lazyLoadingEnabled 属性一起使用,并且需要遵循一些规范和约束,如不能在延迟加载时关闭 SqlSession,不能使用多线程并发访问等。同时,在使用延迟加载时,也需要注意潜在的 N+1 查询问题,需要合理设计 SQL 查询语句,避免不必要的性能损耗。
13.MyBatis 的 xml 映射文件中,不同的 xml 映射文件的id 是否可以重复?
不同的 xml 映射文件,如果配置了 namespace,那么 id 可以重复;
如果没有配置 namespace,那么 id 不能重复;通常理解就是同一个xml里面不能重复。写namespace是必须的,默认的,<mapper>是第一层的标签必须的。
毕竟 namespace 不是必须的,只是最佳实践而已。
原因就是 namespace+id 是作为 Map<String, MappedStatement> 的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。
14.MyBatis 都有哪些 Executor 执行器?它们之间的区别是什么?
MyBatis 有三种 Executor 执行器,分别是:
SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map 内,供下一次使用。简言之,就是重复使用 Statement 对象。
BatchExecutor:执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理的。
其中,SimpleExecutor 是每次请求都会创建一个 Statement 对象,非常耗费性能;ReuseExecutor 在多次请求中,如果有相同的 SQL 语句,就会复用 Statement 对象,优化了性能;BatchExecutor 是执行批量操作时使用,可以一次性执行多条 SQL 语句,优化性能。
在 MyBatis 中,默认使用的是 SimpleExecutor 执行器。可以通过在 MyBatis 的配置文件中指定 <setting name="defaultExecutorType" value="REUSE"/>,来改变默认的执行器。
15.MyBatis 中如何指定使用哪一种 Executor 执行器?
在 MyBatis 的配置文件中可以通过 defaultExecutorType 属性来指定默认的执行器类型
另外,针对每个 <select>、<insert>、<update>、<delete> 标签,也可以通过 executorType 属性来指定该 SQL 语句使用的执行器类型,如:
<select id="selectUserById" resultType="User" executorType="BATCH">
SELECT * FROM user WHERE id = #{id}
</select>
16.MyBatis 是否可以映射 Enum 枚举类?
是的,MyBatis可以映射Java中的Enum枚举类。MyBatis提供了两种方式来映射Enum枚举类:
1.使用EnumTypeHandler:EnumTypeHandler是MyBatis内置的类型处理器之一,它可以将Java中的Enum枚举类与数据库中的数据进行相互转换。
例如,在Mapper XML文件中,可以这样使用EnumTypeHandler来映射枚举类型:
<resultMap id="userResultMap" type="User">
<id property="id" column="id" />
<result property="gender" column="gender" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
</resultMap>
在这个例子中,我们将数据库中的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文件中,就可以直接使用枚举类型进行映射,例如:
<resultMap id="userResultMap" type="User">
<id property="id" column="id" />
<result property="gender" column="gender"/>
</resultMap>
在这个例子中,MyBatis会根据@EnumValue注解中定义的值,将数据库中的gender列与Java中的User类的gender属性进行相互转换。
17.简述 MyBatis 的 xml 映射文件和 MyBatis 内部数据结构之间的映射关系?
MyBatis 将 XML 映射文件中的配置信息映射成 Configuration 对象,该对象是 MyBatis 中重要的数据结构之一,包含了所有的配置信息,包括数据库连接信息、映射文件信息、缓存配置信息等等。Configuration 对象内部包含许多其他对象,包括:
MappedStatement:封装了一个 SQL 语句的信息,如 SQL 语句、输入参数、输出结果等。
SqlSource:封装了一个 SQL 语句的信息,如 SQL 语句、输入参数等,但不包含输出结果。
BoundSql:表示绑定了 SQL 语句和实际参数值的 SQL 语句,包含了 SQL 语句、参数值等信息。
ParameterMap:表示参数映射关系的对象,包含了参数的名称、类型等信息。
ParameterMapping:表示一个参数映射关系,包含了参数名称、类型等信息。
ResultMap:表示结果集映射关系的对象,包含了结果集字段与 Java 对象属性之间的映射关系。
ResultMapping:表示一个结果集映射关系,包含了结果集字段与 Java 对象属性之间的映射关系。
在解析 XML 映射文件时,MyBatis 会将 <select>、<insert>、<update>、<delete> 等标签解析成 MappedStatement 对象,将 <resultMap> 标签解析成 ResultMap 对象,将 <parameterMap> 标签解析成 ParameterMap 对象,将 SQL 语句解析成 SqlSource 对象,将 SQL 语句和实际参数值绑定后的结果解析成 BoundSql 对象,并将这些对象保存在 Configuration 对象中。通过这些对象,MyBatis 可以在执行 SQL 语句时进行参数映射、结果集映射等操作。
18.为什么说 MyBatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
MyBatis 是半自动 ORM 映射工具,它需要手动编写 SQL 语句并将 SQL 语句和 Java 对象进行映射。相比之下,Hibernate 是全自动 ORM 映射工具,它使用对象关系映射技术,将 Java 对象和数据库表进行映射,无需手动编写 SQL 语句。因此,在使用上,MyBatis 更加灵活,可以根据具体的需求编写灵活的 SQL 语句,而 Hibernate 更加便捷,可以快速地进行开发,无需手动编写 SQL 语句。
另外,Hibernate 支持级联操作和对象关系维护,可以自动完成多个对象之间的关联操作,而 MyBatis 不支持级联操作,需要手动编写 SQL 语句来完成多个对象之间的关联操作。
19.Mybatis 的一级、二级缓存?
一级缓存是指MyBatis在同一个SqlSession中执行相同SQL时,会把查询到的结果缓存到内存中。当下次查询相同SQL时,会直接从缓存中获取数据,避免了重复查询数据库,提高了查询效率。一级缓存是默认开启的,也不需要进行额外配置。
二级缓存是指MyBatis在多个SqlSession之间共享缓存。它可以避免多个SqlSession重复查询同一条数据,提高了应用的性能。但是,使用二级缓存需要进行额外的配置,包括在mapper.xml文件中配置<cache>标签以及在MyBatis的配置文件中开启二级缓存。需要注意的是,二级缓存仅仅是对查询结果进行缓存,对于insert、update、delete等操作,并不会清空缓存。
需要注意的是,虽然缓存可以提高应用的性能,但是缓存也有可能带来一些问题,如数据不一致等。因此,在使用缓存时需要根据具体情况进行合理的配置。
20.简述 Mybatis 的插件运行原理,以及如何编写一个插件
MyBatis 的插件机制可以在某些语句执行前、后拦截并执行自定义的处理逻辑,可以用于日志记录、参数处理、数据加密等功能。其实现原理是基于 Java 动态代理技术实现的。
MyBatis 插件机制涉及以下两个接口:
Interceptor:定义了拦截器的基本方法,包括 intercept()、plugin()、setProperties() 等。
Invocation:定义了被拦截方法的基本信息和执行方法的 invoke() 方法。
当创建 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 配置文件中配置插件
<plugins>
<plugin interceptor="com.example.PrintSqlTimeInterceptor"/>
</plugins>
注意,插件的顺序也很重要,MyBatis 将按照插件在配置文件中的顺序依次执行。
———————原文链接:https://blog.csdn.net/qq_33129875/article/details/129304204