一、什么是MyBatis
(
1
)
Mybatis
是一个半
ORM
(对象关系映射)框架,它内部封装了
JDBC
,开发时只需要关注
SQL
语句本身,不需要花费精力去处理加载驱动、创建连接、创建
statement
等繁杂的过程。程序员直
接编写原生态
sql
,可以严格控制
sql
执行性能,灵活度高。
(
2
)
MyBatis
可以使用
XML
或注解来配置和映射原生信息,将
POJO
映射成数据库中的记录,避 免了几乎所有的 JDBC
代码和手动设置参数以及获取结果集。
(3
)通过
xml
文件或注解的方式将要执行的各种
statement
配置起来,并通过
java
对象和 statement中
sql
的动态参数进行映射生成最终执行的
sql
语句,最后由
mybatis
框架执行
sql
并将结果映射为java
对象并返回。(从执行
sql
到返回
result
的过程)。
二、Mybatis中 #和$区别是什么?
#{}
是预编译处理,
${}
是字符串替换 。
Mybatis
在处理
#{}
时,会将
sql
中的
#{}
替换为
?
号,调用PreparedStatement的
set
方法来赋值;
Mybatis
在处理
${}
时,就是把
${}
替换成变量的值。所以有
SQL
注入漏洞问题。
结论:
使用
#{}
可以有效的防止
SQL
注入,提高系统安全性。但是
$
在保证安全的情况下,也有一些使用场景,比如:$
符号的动态传参 ,动态传递表名、动态设置排序字段等。
三、Mybatis的优缺点
优点:
(
1
)基于
SQL
语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,
SQL 写 在 XML
里,解除
sql
与程序代码的耦合,便于统一管理;提供
XML
标签,支持编写动态
SQL
语句,并可重用。
(2
)与
JDBC
相比,减少了
50%
以上的代码量,消除了
JDBC
大量冗余的代码,不需要手动开关连接;
(3
)很好的与各种数据库兼容(因为
MyBatis
使用
JDBC
来连接数据库,所以只要
JDBC
支持的数据库 MyBatis
都支持)。
(
4
)能够与
Spring
很好的集成;
(
5
)提供映射标签,支持对象与数据库的 ORM
字段关系映射;提供对象关系映射标签,支持对象 关系组件维护。
缺点:
(
1
)
SQL
语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写
SQL
语句的功底有一定要求。
(2
)
SQL
语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
四、当实体类中的属性名和表中的字段名不一样处理方式
第1
种: 通过在查询的
sql
语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
第2
种: 通过来映射字段名和实体类属性名的一一对应的关系。
五、Mybatis是如何进行分页的?分页插件的原理是什么?
使用
mybatis
整合
pageHelper
或者
mybatisPlus
的分页功能,他是基于
Mybatis
里面的
Interceptor
拦截器 ,在
select
语句执行之前动态拼接分页关键字。查询执行日志会发现,在
mysql
中,他会按照查询条件先执行一条 count
语句,查询总条数,然后根据用户输入的
pageNum
和
pageSize
,给查询语句拼接一个 limit ?,?
,计算出两个占位符的值。
分页插件的基本原理:使用
Mybatis
提供的插件接口,实现自定义插件,在插件的拦截方法内拦截
待执行的
sql
,然后重写
sql
,根据
dialect
方言,添加对应的物理分页语句和物理分页参数。
六、Mybatis如何将sql执行结果封装为目标对象并返回?有哪些映射形式?
第一种:使用标签,逐一定义数据库列名和对象属性名之间的映射关系。
第二种:使用
sql
列的别名功能,将列的别名书写为对象属性名。
有列名与属性名的映射关系后,Mybatis
通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
七、常见的动态sql有哪些?
<select>、<insert> 、<update>、 <delete>、<where>、<if> 、<foreach>、<trim> 、<choose><when>、<otherwise>
sqlsession
八、MyBatis实现一对一有几种方式?具体怎么操作的?
两种,联合查询和嵌套查询。
联合查询:几个表联合查询
,
只查询一次
,
通过在
resultMap
里面配置 association节点配置一对一的类就可以完成;
嵌套查询:先查一个表,根据这个表里面的结果的 外键
id
,去再另外一个表里面查询数据
,
也是通过 association配置,但另外一个表的查询通过
select
属性配置。
九、Mybatis延迟加载,及实现原理
介绍:
MyBatis
中的延迟加载(
Lazy Loading
)是一种优化数据库查询性能的技术。它允许在需要的时候,才去真正加 载关联对象的数据,而不是在初始化查询时就将所有相关数据一次性加载到内存中。延迟加载通常与一对一、多对一、多对多等关联关系结合使用。
比如查看一个商品,需要的时候再展示商品对应的购买评论,大多数情况下用户可能只是看一眼商品信息,只有感兴趣的时候,才会看评论,这个评论就可以使用延迟加载。
实现原理:
在
MyBatis
中,延迟加载主要是基于代理(
Proxy-based
)的延迟加载,使用 CGLIB
创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName()
,拦截器
invoke()
方法发现a.getB()是
null
值,那么就会单独发送事先保存好的查询关联
B
对象的
sql
,把
B
查询上来,然后调用
a.setB(b)
,于是 a
的对象
b
属性就有值了,接着完成
a.getB().getName()
方法的调用。
十、Mybatis的缓存机制
Mybatis
有一级缓存和二级缓存
一级缓存:默认开启,使用范围是同一个
sqlSession,多次获取的是同一个对象,
二级缓存:手工开启
EnableCache
,查询条件里也要再开启一次。
可以被多个 sqlSession 共享,他的粒度更细,能到 namespace 级别。 必须要求缓存的对象实现了序列化,所以获取到的对象是反序列化回来的,不是同一个对象。
注意:由于默认的
MyBatis Cache
实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将 MyBatis
的
Cache
接口实现,有一定的开发成本,直接使用
Redis
、
Memcached
等分布式缓存可能成本更低,安全性也更高。
十一、JDBC 编程步骤
1.
装载相应的数据库的
JDBC
驱动并进行初始化:
Class
.
forName
(
"com.mysql.jdbc.Driver"
);
2.
建立
JDBC
和数据库之间的
Connection
连接:
Connection c
=
DriverManager
.
getConnection
(
"jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8"
,
"root"
,
"123456"
);
3.
创建
Statement
或者
PreparedStatement
接口,执行
SQL
语句。
4.
处理和显示结果。
5.
释放资源。
十二、MyBatis 常见的设计模式
MyBatis
是一个开源的持久层框架,它在设计中使用了多种设计模式来实现各种功能和提供灵活性。以下是MyBatis 中使用到的一些设计模式:
1.
工厂模式
(
Factory Pattern
)
:
MyBatis
使用工厂模式来创建和管理
SqlSessionFactory
实例。
SqlSessionFactoryBuilder
作为构建工厂,负责创建 SqlSessionFactory
实例。
2.
建造者模式(
Builder Pattern
)
:
SqlSessionFactoryBuilder
使用了建造者模式来构建复杂的
SqlSessionFactory
对象。通过链式调用和设置不同的属性来创建配置灵活的 SqlSessionFactory
实例。
3.
单例模式
(
Singleton Pattern
)
:
SqlSessionFactory
往往被设计为单例模式,因为它的创建和初始化过程是相对昂贵的。通过单例模式确保在应用程序的生命周期内只有一个 SqlSessionFactory
实例,提高了性能和资源利用率。
4.
代理模式
(
Proxy Pattern
)
:
MyBatis
中的
Mapper
接口在底层都会被动态代理生成实现类。这些实现类在调用时会执行对应的
SQL
操作。这种代理模式隐藏了底层的实现细节,使得调用方可以像调用本地方法一样调用 Mapper 接口。
5.
模板模式
(
Template Pattern
)
:
MyBatis
中的
SqlSession
类提供了一组用于执行
SQL
操作的模板方法,如
select()
、
insert()
、
update()
和
delete()
等。这些方法定义了算法的骨架,具体的
SQL
执行细节由不同的子类实现。
6.
观察者模式(
Observer Pattern
)
:
MyBatis
中的
Interceptor
接口和
InterceptorChain
类实现了观察者模式。
Interceptor
接口定义了拦截器的行为,而 InterceptorChain
则负责管理和调度拦截器,当执行
SQL
操作时,拦截器链会被触发,执行一系列的拦截逻辑。
7.
适配器模式(
Adapter Pattern
)
:
MyBatis
中的
ResultSetHandler
接口和各种实现类就是适配器模式的应用。
ResultSetHandler
定义了对结果集的处理方法,不同的实现类可以适配不同的处理逻辑,比如DefaultResultSetHandler
、FastResultSetHandler 等。
这些设计模式使得 MyBatis
在实现上具有了高度的灵活性、可扩展性和可维护性,同时也降低了框架的复杂度。
十三、mybatis只写一个接口,没写实现类,为什么还能调用
使用
JDK
动态代理
+MapperProxy
。本质上调用的是
MapperProxy
的
invoke
方法。
十四、springboot是如何集成mybatis的(必会)
1、maven
配置文件中导入
mybatis
的
starter
2、yml
文件中配置
myatis
相关信息 别名
mapper.xml
文件的路径 开启驼峰命名 日志等
3、在
mapper.java
文件上加上
@Mapper
注解,或在全局配置上加上
@MapperScan
十五、MyBatis中的常见注解及作用
@MapperScan、@Mapper、@Select、@Insert、@Update、@Delete、@Param、@Results 和 @Result
@MapperScan: 用于扫描指定包下的 Mapper 接口,并注册到 Spring 容器中。通常配合在 Spring Boot 项目中使用。
@Mapper: 用于标记 Mapper 接口,告诉 MyBatis 这是一个 Mapper 接口,可以被扫描并动态生成实现类。
@Select: 用于配置查询语句,将方法映射到 SQL 查询语句,用于查询操作。
@Insert: 用于配置插入语句,将方法映射到 SQL 插入语句,用于插入操作。
@Update: 用于配置更新语句,将方法映射到 SQL 更新语句,用于更新操作。
@Delete: 用于配置删除语句,将方法映射到 SQL 删除语句,用于删除操作。
@Param: 用于给方法参数取别名,解决方法参数为多个时,SQL 中的参数名称不明确的问题。@Results 和 @Result: 用于配置查询结果映射,将数据库字段映射到 Java 对象的属性。
十六、ResultMap和ResultType区别
在
MyBatis
中,
ResultMap
和
ResultType
都用于映射查询结果到
Java
对象
ResultType
:
作用:
ResultType
用于指定查询结果的类型,即映射的目标
Java
对象的类型。
使用方式:
在
SQL
映射文件中或者注解方式中,通过
resultType
属性指定返回结果的类型。
eg:
<select id="getUser" resultType="User">
SELECT * FROM users WHERE id = #{userId}
</select>
ResultMap
:
作用:
ResultMap
用于定义复杂的映射规则,将查询结果的列与
Java
对象的属性进行映射。
使用方式:
在
SQL
映射文件中,通过
<resultMap>
元素定义一个结果映射,并在
<select>
中使用
resultMap 属性引用这个映射。
eg:
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name" />
<result property="email" column="user_email" />
</resultMap>
<select id="getUser" resultMap="userResultMap">
SELECT user_id, user_name, user_email FROM users WHERE id = #{userId}
</select>
优势:
ResultMap
的主要优势在于它提供了更灵活的映射规则,可以处理数据库表与
Java
对象之间的复杂映射关系,包括关联关系、嵌套查询等。另外 resultMap
还支持
collection
和
associatio
总结:
ResultType
用于简单的映射情况,直接指定返回结果的类型;而
ResultMap
则用于处理更复杂的映射,提供了更灵活的配置选项,可以定义详细的映射规则。在实际应用中,根据业务需求和查询复杂度的不同,选择使用 ResultType 或
ResultMap
。
十七、collection和association怎么做一对多和多对一
使用
<collection>
实现一对多:
在父对象(一的一方)的
<resultMap>
中使用
<collection>
元素定义集合属性。
使用
select
属性指定子查询的
SQL
语句。
使用
column
属性指定父对象关联键与子对象关联键的对应关系。
eg:
<resultMap id="parentResultMap" type="Parent">
<id property="parentId" column="parent_id" />
<result property="parentName" column="parent_name" />
<!--
一对多关系
-->
<collection property="children" ofType="Child" select="getChildByParentId">
<id property="childId" column="child_id" />
<result property="childName" column="child_name" />
</collection>
</resultMap>
<select id="getParents" resultType="Parent">
SELECT * FROM parent;
</select>
<select id="getChildByParentId" parameterType="Long" resultType="Child">
SELECT * FROM child WHERE parent_id = #{parentId}
</select>
使用
<association>
实现多对一:
在子对象(多的一方)的
<resultMap>
中使用
<association>
元素定义关联属性。
使用
select
属性指定父对象查询的
SQL
语句。
使用
column
属性指定父对象关联键与子对象关联键的对应关系。
eg:
<resultMap id="childResultMap" type="Child">
<id property="childId" column="child_id" />
<result property="childName" column="child_name" />
<!--
多对一关系
-->
<association property="parent" javaType="Parent" select="getParentByChildId">
<id property="parentId" column="parent_id" />
<result property="parentName" column="parent_name" />
</association>
</resultMap>
<select id="getChildren" resultType="Child">
SELECT * FROM child;
</select>
<select id="getParentByChildId" parameterType="Long" resultType="Parent">
SELECT * FROM parent WHERE parent_id = #{parentId}
</select>