1、mybatis的特点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的ORM字段关系映射。
- 提供对象关系映射标签,支持对象关系组建维护。
- 提供xml标签,支持编写动态sql。
2、MyBatis 的 #{} 和 ${} 的区别?
- #{} 是预编译处理,${}是字符串替换。
- Mybatis处理#{}时,会将sql中的#{}转换为?号,然后使用PreparedStatement的set方法来赋值;MyBatis在处理${}时,就是把{}替换成变量的值。
- 使用#{}能有效的预防SQL注入,提高系统的安全性。
- ${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc. Driver。
- #{}是 sql 的参数占位符,MyBatis 会将 sql 中的#{}替换为? 号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的? 号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name} 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()。
在XML文件中,#{}和${}是两种不同的参数占位符,常见于MyBatis等框架中的SQL语句的参数绑定。
#{}和${}的区别
- #{}:参数占位符,表示使用预编译语句,不仅可以防止SQL注入问题,还可以将传入参数自动转义,保证SQL语句的安全性。当使用#{}对参数进行占位时,MyBatis会使用预编译语句执行SQL语句。
- ${}:字符串替换,表示直接将参数的值转换成字符串拼接到SQL语句中,不能防止SQL注入问题,存在SQL注入风险。当使用${}对参数进行占位时,MyBatis将直接将参数的值替换占位符,形成的SQL语句是由参数值拼接的,存在SQL注入风险。
MyBatis中#{}和${}的区别
- #{}:MyBatis中的参数占位符,使用预编译语句执行SQL语句。如果是字符串类型或字符数组类型的参数,默认用#{value}来引用其中的值,值传入后会自动进行转义,可以在防止SQL注入攻击方面起到一定的作用。
- ${}:MyBatis中字符串替换的方式,将传入的参数值直接拼接到SQL语句中。如果使用${}在SQL语句中引用参数时,引用的是参数名,而非参数类型等。因此,一般在Map或POJO对象中配置SQL参数,可能会使用${}方式引用参数
总的来说,#{}方式能够防止SQL注入等问题,更安全可靠,而${}方式不会起到防止SQL注入攻击的作用,更易受到攻击。根据具体业务场景及安全要求,使用不同的参数占位符方式。
3、Mybatis中的Mapper接口和XML文件里的SQL是如何建立关系的?
一、解析XML
首先,Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件,这里我们重点关注两部分。
1、创建SqlSource Mybatis会把每个SQL标签封装成SqlSource对象。然后根据SQL语句的不同,又分为动态SQL和静态SQL。其中,静态SQL包含一段String类型的sql语句;而动态SQL则是由一个个SqlNode组成。
2、创建MappedStatement XML文件中的每一个SQL标签就对应一个MappedStatement对象,这里面有两个属性很重要。
- id 全限定类名+方法名组成的ID。
- sqlSource 当前SQL标签对应的SqlSource对象。
创建完MappedStatement对象,将它缓存到Configuration#mappedStatements中。
Configuration对象,我们知道它就是Mybatis中的大管家,基本所有的配置信息都维护在这里。把所有的XML都解析完成之后,Configuration就包含了所有的SQL信息。
到目前为止,XML就解析完成了。看到上面的图示,聪明如你,也许就大概知道了。当我们执行Mybatis方法的时候,就通过全限定类名+方法名找到MappedStatement对象,然后解析里面的SQL内容,执行即可。
二、Dao接口代理
我们的Dao接口并没有实现类,那么,我们在调用它的时候,它是怎样最终执行到我们的SQL语句的呢?
首先,我们在Spring配置文件中,一般会这样配置:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.viewscenes.netsupervisor.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
或者你的项目是基于SpringBoot的,那么肯定也见过这种:MapperScan("com.xxx.dao")
它们的作用是一样的。将包路径下的所有类注册到Spring Bean中,并且将它们的beanClass设置为MapperFactoryBean。
有意思的是,MapperFactoryBean实现了FactoryBean接口,俗称工厂Bean。那么,当我们通过@Autowired注入这个Dao接口的时候,返回的对象就是MapperFactoryBean这个工厂Bean中的getObject()方法对象。
那么,这个方法干了些什么呢?
简单来说,它就是通过JDK动态代理,返回了一个Dao接口的代理对象,这个代理对象的处理器是MapperProxy对象。所以,我们通过@Autowired注入Dao接口的时候,注入的就是这个代理对象,我们调用到Dao接口的方法时,则会调用到MapperProxy对象的invoke方法。
对这块内容不太能理解的朋友,可以先看看Spring中的FactoryBean 和 JDK动态代理相关知识。
曾经有个朋友问过这样一个问题:对于有实现的dao接口,mapper还会用代理么?
答案是肯定的,只要你配置了MapperScan,它就会去扫描,然后生成代理。但是,如果你的dao接口有实现类,并且这个实现类也是一个Spring Bean,那就要看你在Autowired的时候,去注入哪一个了。
具体什么意思呢?我们来到一个例子。如果我们给userDao搞一个实现类,并且把它注册到Spring。
@Component
public class UserDaoImpl implements UserDao{
public List<User> getUserList(Map\<String,Object> map){
return new ArrayList<User>();
}
}
然后我们在Service方法中,注入这个userDao。猜猜会发生什么?
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userDao1;
public List<User> getUserList(Map<String,Object> map) {
return userDao1.getUserList(map);
}
}
也许你已经猜到了,是的,它会启动报错。因为在注入的时候,找到了两个UserMapper的实例对象。日志是这样的:
No qualifying bean of type \[com.viewscenes.netsupervisor.dao.UserDao] is defined: expected single matching bean but found 2: userDaoImpl,userDao
当然了,也许我们的命名不太规范。其实我们通过名字注入就可以了,像这样:@Autowired UserMapper userDao; 或者 @Autowired UserMapper userDaoImpl;再或者给你其中一个Bean加上@Primary注解。
三、执行
如上所述,当我们调用Dao接口方法的时候,实际调用到代理对象的invoke方法。 在这里,实际上调用的就是SqlSession里面的东西了。
public class DefaultSqlSession implements SqlSession {
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms,
wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}
}
}
看到以上代码,说明我们想的不错。它就是通过statement全限定类名+方法名拿到MappedStatement 对象,然后通过执行器Executor去执行具体SQL并返回。
四、总结
到这里,再回到开头我们提到的问题,也许你能更好的回答。同时笔者觉得,这道题目,如果你覆盖到以下几个关键词,面试官可能会觉得很满意。
- SqlSource以及动态标签SqlNode
- MappedStatement对象
- Spring 工厂Bean 以及动态代理
- SqlSession以及执行器
那么,针对第二个问题:如果有两个XML文件和这个Dao建立关系,岂不是冲突了? 答案也是显而易见,不管有几个XML和Dao建立关系,只要保证namespace+id唯一即可。
转载自:Mybatis中的Mapper接口和XML文件里的SQL是如何建立关系的?
4、mybatis用过哪些标签
5、MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
MyBatis 动态 sql 可以让我们在 xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。MyBatis 提供了 9 种动态 sql 标签:
- <if></if>
- <where></where>(trim,set)
- <choose></choose>(when, otherwise)
- <foreach></foreach>
- <bind/>
6、mybatis实现分页的几种方式
关于 MyBatis 动态 SQL 的详细介绍,请看这篇文章:java - Mybatis系列全解(八):Mybatis的9大动态SQL标签你知道几个?
关于这些动态 SQL 的具体使用方法,请看这篇文章:Mybatis【13】-- Mybatis动态sql标签怎么使用?
7、Mybatis执行流程?
考察点:理解了各个组件的关系、Sql的执行过程(参数映射、sql解析、执行和结果处理)
- 读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件
- 构造会话工厂SqlSessionFactory
- 会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法)
- 操作数据库的接口,Executor执行器,同时负责查询缓存的维护
- Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
- 输入参数映射
- 输出结果映射