1.当前的主流框架
SSM框架: 现在主流 Spring SpringMVC Mybatis 轻量级框架
SSH框架: SSM框架之前主流的: Spring + Struts2 + Hibernate 轻量级框架 不依赖环境
EJB框架: 重量级框架: SSH框架之前主流(SUN) 依赖开发环境, 开发环境不一样, 代码也不一样
2.使用mybatis的主要流程
1.各个部分的联系
2.Mybatis执行流程图:
3.mybatis-config.xml核心配置文件的基本框架
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--开发环境-->
<environments default="mysql">
<environment id="mysql">
<!--事务管理器: JDBC: 使用jdbc管理事务, 需要手动提交事务-->
<transactionManager type="JDBC"/>
<!--dataSource数据源的类型: POOLED 连接池 UNPOOLED: 不带连接池 -->
<dataSource type="POOLED">
<!--数据库四大参数-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/0311db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8"/>
<property name="username" value="root"/>
<property name="password" value="admin123"/>
</dataSource>
</environment>
</environments>
<!--加载sql映射文件-->
<mappers>
<!--相对路径
判断路径是否写错, 按住ctrl不放,
-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
4.Mybatis细节(#{}
与${}
面试高频 )
1.使用#{} 接收列名时,它只是作为一个常量值插入
2.使用${}
接收列名,作为sql语句进行插入
5.面试细节(selectOne 与selectList 的区别)
都是集合,只不过selectOne是经过判断,如果大于一条记录会抛出一异常
6.InputStream,SqlSessionFactoryBuilder,SqlSessionFactory,SqlSession 的scope作用域
InputStream, SqlSessionFactoryBuilder:
一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域
方法的局部变量
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次
作为静态属性
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
方法的局部变量
7.mybatis的Mapper代理模式(方法和步骤)
1.被代理接口: 包含方法签名
2.编写对应的Sql映射文件: 满足Mapper代理实现的要求
1.sql映射文件的namespace必须是对应Dao接口的全限定名(包.接口名)
2.sql映射文件的Statement的id必须是对应方法的方法名
3.service类得到Mapper接口代理对象
1.service层方法
public class UserServiceImpl implements UserService {
UserMapper userMapper= MybatisUtil.getMapper(UserMapper.class);
@Override
public boolean saveUser(User user) {
//调用UserDao
try {
return userMapper.add(user);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return false;
}
}
2.使用一个自己整合的工具类得到mapper代理接口对象
package com.fs.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* @author suke
* @version 1.0
* @title MybatisUtil
* @description Mybatis工具类
* @create 2024/4/15 17:47
*/
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
//tl 被多个线程共享, SqlSession不共享,每个线程都有自己的SqlSession
private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
static{
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession openSqlSession(){
SqlSession sqlSession = tl.get();
if(sqlSession == null){
sqlSession = sqlSessionFactory.openSession();
tl.set(sqlSession);
}
return sqlSession;
}
public static void close(){
SqlSession sqlSession = tl.get();
if(sqlSession != null){
sqlSession.commit();
sqlSession.close();
tl.remove();//防止内存泄漏
}
}
//定义泛型方法 <T>: 定义泛型变量 T: 使用泛型变量
// MybatisUtil.getMapper(UserMapper.class);
public static <T> T getMapper(Class<T> clazz){
SqlSession sqlSession = tl.get();
if(sqlSession == null){
sqlSession = openSqlSession();
}
return sqlSession.getMapper(clazz);
}
}
8.sql语句需要传入多个参数时(三种方式)
9.动态sql的使用方法
1.提供了动态sql标签:
if: if条件
-
where: where关键字
-
foreach: 循环标签
-
set: set关键字
-
sql: sql片段, 重复sql的重用
1.<if>
<if test="条件(属性名)">
sql片段
</if>
例子
如果传入的是对象类型时
要进行非空判断进行优化
2.<where>
-
where关键字
-
去掉sql语句中第一个and或者or
把条件包裹在where标签内部
<select id="selectByCondition" parameterType="UserCondition" resultType="User">
select * from tb_user
<where>
<!--如果用户传递这个条件, sql语句拼接这个条件
判断userCondition是否为null,如果为null, 没有条件
-->
<if test="userCondition != null">
<if test="userCondition.user != null ">
<!--username: String 非空判断有两种情况: null, ""
只有字符串才判断 "", 其他类型一定不能判断""
-->
<if test="userCondition.user.username != null and userCondition.user.username!='' ">
and username like #{userCondition.user.username}
</if>
<if test="userCondition.user.sex != null and userCondition.user.sex!='' ">
and sex = #{userCondition.user.sex}
</if>
</if>
<if test="userCondition.startDate != null">
and brithday >= #{userCondition.startDate}
</if>
<if test="userCondition.endDate != null">
and brithday <= #{userCondition.endDate}
</if>
</if>
</where>
</select>
3.<set >
-
set关键字
-
去掉最后一个逗号
4.<foreach>
<select id="selectByIds" resultType="User">
select * from tb_user where
<!--循环产生sql片段
collection: 集合/数组, list集合: list 数组: array
item:循环变量
separator: sql片段的连接符:
open: sql片段前部分
close: sql片段后部分
循环生成sql:
" id = 20 or id = 21 or id =22 "
"id in(20,21,22)"
sql片段 = open + (循环片 + separator +循环片 ) + close
-->
<foreach collection="array" item="i" separator="or" open=" " close=" ">
id = #{i}
</foreach>
</select>
5.<sql>
代替重复的sql代码
10.表连接查询(三种表的连接查询)
1.一对一
外键列可以加在任何一方, 但是这个外键列必须添加一个唯一约束
类的设计
2.一对多
类的设计
3.多对多
多对多关联关系: 使用中间表维护表的关系, 中间表由两个外键组成, 这两个外键组合一个主键
11.缓存机制
缓存
缓存作用: 1. 提高查询效率(减少SQL执行)
2.减轻数据库服务器的压力
Mybatis的缓存
Mybatis有两级缓存
-
一级缓存: 默认开启, 基于SqlSession的
同一个SqlSession执行相同的sql语句, 后面的执行都将从SqlSession缓存获取数据, 当SqlSession关闭之后, 该SqlSession中一级缓存数据删除
-
不同的SqlSession,无法共享一级缓存, 同一个SqlSession,才共享一级缓存
-
一旦SqlSession执行增删改操作, 先清空缓存, 为了保存缓存与数据库的数据一致性
-
-
二级缓存
基于Mapper的二级缓存, 不同sqlSession执行相同mapper(namespace)中相同sql语句, 并且传递参数参数, 也就是执行相同的sql语句, 那就会先从二级缓存查询数据,如果二级缓存没有数据,才到数据库查询, 如果二级缓存有数据, 返回缓存中的数据
Mybatis的全局二级缓存开启: 表示Mybatis允许使用二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
cacheEnabled默认值: true
开启Mapper的二级缓存,没有开启
-
在mapper映射文件中使用
<cache/>
表示该Mapper下的所有的查询都进行二级缓存
<cache/>
有type属性, 指定Cache的实现类, 默认:SerializedCache(序列化的缓存实现)面试题: 自定义mybatis二级缓存, 怎么实现:
编写一个类实现Cache接口
在Mapper配置文件中,
<cache type="你的Cache实现类全限定名"/>
-
对Mapper下查询的返回值类型,实现可序列化接口:
SerializedCache: 要求数据进行序列化操作
面试题:
查询执行顺序:
如果二级缓存开启, 首先走的是二级缓存, 如果二级缓存(tcm)有, 直接返回, 如果没有, 走一级缓存(localCache) ,如果一级缓存有, 直接返回, 如果一级缓存没有: 从数据库查询
单独关闭某个Statement的二级缓存:
二级缓存中,如果执行增删改, 清空二级缓存.