#{}
和
${}
的区别是什么?
#{}
是预编译处理,
${}
是字符串替换。
Mybatis
在处理
#{}
时,会将
sql
中的
#{}
替换为
?
号,调用
PreparedStatement
的
set
方法来赋值;
Mybatis
在处理
${}
时,就是把
${}
替换成变量的值。
使用
#{}
可以有效的防止
SQL
注入,提高系统安全性。
![](https://img-blog.csdnimg.cn/c297fbcc7b4f4d3b9fedeb6264f55fae.png)
10
、
Mybatis
是否支持延迟加载?如果支持,它的实现原理是什
么?
Mybatis
仅支持
association
关联对象和
collection
关联集合对象的延迟加载,
association
指的就是一
对一,
collection
指的就是一对多查询。在
Mybatis
配置文件中,可以配置是否启用延迟加载
lazyLoadingEnabled=true|false
。
它的原理是,使用
CGLIB
创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调
用
a.getB().getName()
,拦截器
invoke()
方法发现
a.getB()
是
null
值,那么就会单独发送事先保存好的
查询关联
B
对象的
sql
,把
B
查询上来,然后调用
a.setB(b)
,于是
a
的对象
b
属性就有值了,接着完成
a.getB().getName()
方法的调用。这就是延迟加载的基本原理。
当然了,不光是
Mybatis
,几乎所有的包括
Hibernate
,支持延迟加载的原理都是一样的。
11
、说说
Mybatis
的缓存机制
:
Mybatis
整体:
一级缓存
localCache
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的
SQL
,
MyBatis
提供了一级缓存的方案优化这部分场景,如果是相同的
SQL
语句,会优先命中一级缓存,
避免直接对数据库进行查询,提高性能。
每个
SqlSession
中持有了
Executor
,每个
Executor
中有一个
LocalCache
。当用户发起查询时,
MyBatis
根据当前执行的语句生成
MappedStatement
,在
Local Cache
进行查询,如果缓存命中
的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入
Local Cache
,最后
返回结果给用户。具体实现类的类关系图如下图所示:
![](https://img-blog.csdnimg.cn/f34275b2488d48868b6401b5bf7dafa0.png)
1. MyBatis
一级缓存的生命周期和
SqlSession
一致。
2. MyBatis
一级缓存内部设计简单,只是一个没有容量限定的
HashMap
,在缓存的功能性上有
所欠缺。
3. MyBatis
的一级缓存最大范围是
SqlSession
内部,有多个
SqlSession
或者分布式的环境下,
数据库写操作会引起脏数据,建议设定缓存级别为
Statement
。
二级缓存
在上文中提到的一级缓存中,其最大的共享范围就是一个
SqlSession
内部,如果多个
SqlSession
之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用
CachingExecutor
装饰
Executor
,进入一级缓存的查询流程前,先在
CachingExecutor
进行二级缓存的查询,具体的工作
流程如下所示。
二级缓存开启后,同一个
namespace
下的所有操作语句,都影响着同一个
Cache
,即二级缓存被
多个
SqlSession
共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程为:
二级缓存
->
一级缓存
->
数据库
1. MyBatis
的二级缓存相对于一级缓存来说,实现了
SqlSession
之间缓存数据的共享,同时粒度
更加细,能够到
namespace
级别,通过
Cache
接口实现类不同的组合,对
Cache
的可控性
也更强。
2. MyBatis
在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件
比较苛刻。
3.
在分布式环境下,由于默认的
MyBatis Cache
实现都是基于本地的,分布式环境下必然会出现
读取到脏数据,需要使用集中式缓存将
MyBatis
的
Cache
接口实现,有一定的开发成本,直
接使用
Redis
、
Memcached
等分布式缓存可能成本更低,安全性也更高。
二级缓存开启后,同一个
namespace
下的所有操作语句,都影响着同一个
Cache
,即二级缓存被
多个
SqlSession
共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程为:
二级缓存
->
一级缓存
->
数据库
1. MyBatis
的二级缓存相对于一级缓存来说,实现了
SqlSession
之间缓存数据的共享,同时粒度
更加细,能够到
namespace
级别,通过
Cache
接口实现类不同的组合,对
Cache
的可控性
也更强。
2. MyBatis
在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件
比较苛刻。
3.
在分布式环境下,由于默认的
MyBatis Cache
实现都是基于本地的,分布式环境下必然会出现
读取到脏数据,需要使用集中式缓存将
MyBatis
的
Cache
接口实现,有一定的开发成本,直
接使用
Redis
、
Memcached
等分布式缓存可能成本更低,安全性也更高。
12
、
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
中比如
UserMapper.java
是接口,为什么没有实
现类还能调用?
使用
JDK
动态代理
+MapperProxy
。本质上调用的是
MapperProxy
的
invoke
方法。