面试题
面试题:#{} 和${}的区别
${}是直接将数据拼接在SQL中而#{}是将数据做为字符串传入SQL中,采用的是预编译实现的。${}几乎没有办法防止SQL注入,而#{}可以很大程度上防止SQL注入。能使用#{}时优先使用#{}。${}一般用来传动态的表名和数据库名(传入前需要做相应的校验)
面试题:#{}防止SQL注入的原理
其原因就是:采用了JDBC的PreparedStatement,就会将sql语句:“select id,no from user where id =?” 预先编译好,也就是SQL引擎会预先进行语法分析,产生语法树,生成执行计划,也就是说,后面你输入的参数,无论你输入的是什么,都不会影响该SQL语句的语法结构了,因为语法分析已经完成了,而语法分析主要是分析sql命令,比如select,from,where,and,or,order by等等。所以即使你后面输入了这些sql命令,也不会被当成sql命令来执行了,因为这些SQL命令的执行,必须先得通过语法分析,生成执行计划,既然语法分析已经完成,已经预编译过了,那么后面输入的参数,是绝对不可能作为SQL命令来执行的,只会被当做字符串字面值参数。所以的sql语句预编译可以防御SQL注入。而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译
面试题:Mybatis的优缺点
- MyBatis简单易学,整体框架比较小巧容易使用
- 半自动ORM提供了更高的自定义化
- sql写在xml中解除了代码和sql的耦合
- 支持动态sql
缺点
- 半自动化带来的缺点,需要大量编写sql
- sql语句依赖数据库,不能轻易改变数据库
- 二级缓存可能会导致脏读
面试题:Mybatis和Hibernate的区别
- Mybatis是半自动的ORM框架,Hibernate是全自动的ORM框架,Mybatis需要编写大量的SQL语句
- Hibernate是完全面向对象的操作,Hibernate移植性更好因为Hibernate中都是面向对象,不用关心SQL
概述
ORM框架
ORM对象关系映射,是指完成数据库中的数据到程序对象的映射。原生的JDBC代码太臃肿,比如每次执行一个SQL语句需要链接和断开链接。查询出来的结果要手动映射到对象上等等,往往要写一大堆无用的代码,而ORM框架就是为了简化这些操作,ORM是对JDBC的封装。
常见的ORM框架
- Mybatis
- Hibernate
- Spring Data JPA等等
Mybatis和Hibernate
Hibernate和Mybatis都是ORM框架,Mybatis是一个半自动的ORM框架,Hibernate是一个全自动的ORM框架。HIbernate中是完全面向对象的,sql语句是根据对象和数据库映射生成的。而Mybatis中sql语句需要自己写,它主要是帮我们完成查询结果集的映射等。
为什么用Mybatis
mybatis整体更小巧拿来就可以用上手难度相对要低并且SQL语句更灵活
Hibernate移植性要比Mybatis更好
因为Hibernate面向的是对象来进行增删改查的,而Mybatis需要手写SQL可能会出现SQL语句中带有方言(只在特定的数据库中使用的语法)。
Mybatis和Hibernate的缓存
两者都是分两级缓存的,但Hibernate对查询语句有良好的管理机制用户无需关心SQL语句,出现脏读时也会即时报错。而Mybatis中很有可能出现脏读(其他Mapper中执行了一些增删改的操作导致Mapper中的数据变脏)
工作原理和流程
- 读取mybatis的config文件,配置mybatis对应的环境信息,比如数据库链接信息、是否开启驼峰命名、日志等等
- 读取各个mapper.xml映射文件,这里会为每个xml或者注解中的sql语句生成一个mappedstatement对象(这里面封装了sql语句、参数映射、结果映射等信息)
- 构建会话工厂sqlsessionfactory,mybatis根据配置信息构建出一个sqlsession的会话工厂
- 创建一个sqlsession会话对象。这个对象执行sql。sqlsession用来和数据进行交互,是一次和数据库的交互会话
- mybatis底层通过一个Executor执行器对mappedstatement进行解析,设置sql参数最后执行sql语句
- StatementHandler
- ParameterHandler
- ResultSetHandler
- 将结果映射为java中的map,list,实体类等存储结构
一级和二级缓存
mybatis的缓存使用顺序是:二级缓存—>一级缓存—>数据库
一级缓存
一级缓存是sqlsession级别的缓存,sqlsession在没有关闭和提交事务时会将查到的数据缓存起来。sqlsession之间的缓存是互不干扰的。
Spring在集成mybatis后通过动态代理对mybatis的sqlsession进行了增强,默认每次操作结束后都会执行commit。所以Spring+mybatis的情况下如果不开启事务,每次mapper的操作sqlsession都会直接提交。一级缓存就没有作用,在Spring中一级缓存只有在开启事务时才有用
二级缓存
二级缓存是Mapper级别的缓存,只要在同一个命名空间,sqlsession之间可以公用二级缓存。二级缓存默认是关闭的。二级缓存是在sqlsession提交后才会将数据同步到二级缓存中,未提交时数据会放入一个暂存区中,当sqlsession提交时才会将暂存区中的数据放入二级缓存中。
一个命名空间中因为使用了暂存区,所以不会出现数据脏读的问题。但由于多个命名空间之间是隔离的,mapper之间是存在脏读问题的
解决办法:在命名空间中将二级缓存指向另一个命名空间的二级缓存
只需要在对应的Mapper文件中,将该Mapper的命名空间引用另外一个Mapper的命名空间就可以使两个Mapper共用一个缓存空间!
<cache-ref namespace="xxx.xxx.xxx.UserMapper2"></cache-ref>
或者避免在不同的命名空间中对同一部分数据进行修改
Mybatis二级缓存脏读解决办法
- mapper中尽量单表操作
延迟加载的实现
延迟加载就是指mybatis会根据我们的需要来决定是否执行查询,比如一个用户持有多个账户,我们在没有使用他账户信息时就没有必要去把用户的账户查出来,当我们真正需要这部分数据时才去执行查询。一般使用在1对多或者多对多的情况下。
注意
在使用resultMap的情况下,并且配置了或者的情况下才能使用延迟加载,因为这两种方式会去调用其他sql语句。开启了延迟加载的话,在没使用到关联对象时不会执行这部分sql语句。
实现原理
mybatis通过动态代理,创建目标对象的代理对象,拦截延迟加载的对象,当对象使用到延迟加载的属性时会先去查数据库然后重新设置属性的值