Mybatis高频面试题
上篇文章讲述了Spring如何配置Mybatis环境。
本篇文章主要介绍Mybatis的一些高频面试知识点。持续更新。希望点个收藏
#{}、${}区别
网上标准答案:#{}是预编译处理,${ } 是字符串替换。
Mybatis在处理#{}时,会将sql语句中的#{}替换成?号。调用PreparedStatement的set方法来赋值Mybatis在处理${}时,就是把${}替换成变量的值。使用#{}可以有效防止SQL注入,提高系统安全性
答案肯定是对的,但是大部分人简单看完,对防止SQL注入的原理还是一知半解。下面举个实例加深理解
-- 针对此条Sql语句. 网站登入验证SQL语句
-- name = "1' OR '1'='1"
-- passWord = "1' OR '1'='1"
select * from user where name = #{name} and passwrod = #{password};
-- #{} 分两步
-- 第一步 替换成一下sql,进行预编译
select * from user where name = ? and password = ?;
PreparedStatement pstatement = conn.prepareStatement(sql);
-- 第二步 PreparedStatement的set方法替换,执行sql
pstmt.setString(1, name);
pstmt.setString(2, password);
ResultSet rs=pstmt.executeUpdate();
-- ${} 直接替换
select * from user where name = ${name} and password = ${password};
-- 替换后
select * from user where name ='1' or '1' = '1' and password = '1' or '1' = '1';
-- 等同于
select * from user;
-- 在后台帐号验证的时候巧妙地绕过了检验,达到无账号密码,亦可登录网站。所以SQL注入攻击被俗称为黑客的填空游戏。
延迟加载
官方答案:Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。
看完,大多数人只有一个概念,延迟加载是什么估计都没有明白
我先用sql知识解释一下延迟加载,然后,在mybatis环境下实验
原理解析
关联查询:是一个多表连接查询,设计到join操作,不如单表查询快速
-- Student表属性列:Sno, Sname, Sage;
-- SC表属性列: Sno, Cno;
-- 查询选了课的学生名字,关联查询
select Sno, Cno, Sname from sc, student where sc.Sno = student.Sno;
-- 其实可以拆分成两个单表查询
select Sno, Cno from SC;
select Sname from student where Sno = #{第一个查询结果Sno}
延迟加载原理就是将关联查询分解成两个单表查询,首先执行第一个单表查询,当有需要时,再进行第二个单表查询。
实例实验
前期准备:
-
在MySQL数据库中创建两个表,一个学生表,一个选课表,并插入数据
-
创建两个bean类,对应表的属性列
package com.test.demo;
public class Student {
public int sNo;
public String sName;
public int sAge;
public Student(int sNo, String sName, int sAge) {
this.sNo = sNo;
this.sName = sName;
this.sAge = sAge;
}
//getter、setter方法此处省略
}
package com.test.demo;
public class SC {
Student student;
int cNo;
//getter、setter方法省略
}
创建接口和xml文件:
package com.test.demo.mapper;
import java.util.List;
import com.test.demo.Student;
public interface StudentMapper {
void insert(Student student);
void delete(String sNo);
Student selectByNumber(Integer sNo);
void updateName(Student student);
List<Student> findByCondition(String sName, Integer sAge);
List<Student> selectByNumberList(List<Integer> list);
}
package com.test.demo.mapper;
import com.test.demo.SC;
import java.util.List;
public interface SCMapper {
List<SC> selectByNumber(Integer cNo);
}
XML文件重点
studentMapper文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "com.test.demo.mapper.StudentMapper">
<select id="selectByNumber" parameterType="java.lang.Integer" resultType="com.test.demo.Student">
SELECT * FROM student where Sno = #{sNo}
</select>
</mapper>
scMapper文件
<mapper namespace = "com.test.demo.mapper.SCMapper">
<select id="selectByNumber" parameterType="java.lang.Integer" resultMap="lazyMap">
SELECT * FROM sc where Cno = #{cNo}
</select>
<!--property:需要延迟加载的属性-->
<!--javaType:属性类型-->
<!--select:延迟加载需要执行的sql语句-->
<!--column:关联查询的属性列-->
<resultMap id="lazyMap" type="com.test.demo.SC">
<association property="student" javaType="com.test.demo.Student" select="com.test.demo.mapper.StudentMapper.selectByNumber" column="Sno">
</association>
</resultMap>
</mapper>
main方法
List<SC> scList = scMapper.selectByNumber(4);
for(SC sc : scList) {
//延迟加载
Student student = sc.getStudent();
System.out.println(student.getSName());
}
二级缓存
-
一级缓存
默认开启一级缓存,一级缓存的作用范围是
SqlSession
。如果执行的sql语句,SqlSession以前执行过,直接会复用缓存的结果,而不会去数据库查询。调用SqlSession的close
、clearCache
方法或者执行SQL语句是update、delete、insert
,都会更新一级缓存 -
二级缓存
二级缓存向胆固全局缓存,默认不开启,可以再配置文件中开启,并且要求POJO实现Serializable接口,即必须可序列化。
- select语句的返回结果均会被缓存
- insert、update、delete语句会刷新缓存
- 使用LRU算法进行缓存替换