文章目录
- 项目源码地址
- Mybatis概述
- 什么是Mybatis?
- Mybatis导入
- 知识补充
- 数据持久化
- 持久层
- 第一个Mybatis程序:数据的增删改查查
- 项目名称
- 创建环境
- 编写代码
- 1、目录结构
- 2、核心配置文件:resources/mybatis-config.xml
- 3、mybatis工具类:Utils/MybatisUtils
- 4、Pojo:User
- 5、Dao接口:UserDao
- 6、Mapper配置文件:UserMapper.xml
- 测试
- 常见问题汇总
- 第一个Mybatis程序:完善
- 项目名称
- 优化后的整体目录结构
- 1、通过db.properties文件配置数据库连接的基本设置
- 2、配置多套环境
- 3、起别名
- 4、映射器 mappers
- 第一个Mybatis程序:进阶
- resultMap
- 问题描述
- 解决1:改变Sql语句,在sql中起别名
- 解决2:使用resultMap
- 注意事项!!!
- 模糊查询
- 日志
- 日志工厂
- STDOUT_LOGGING使用
- LOG4J使用
- 分页
- Limit分页
- RowBounds分页
- 分页插件:PageHelper
- 第一个Mybatis程序:注解开发实现
- 项目名称
- 实现
- 关于 @Param(“”) 注解
- #{} 和 ${} 的区别
- Lombok
- 第二个Mybatis程序:基础,多对一处理
- 项目名称
- 案例背景
- 编写代码
- 1、Pojo
- 2、Utils
- 3、Dao:按照查询嵌套处理
- 4、Dao:按照结果嵌套处理
- 5、结果
- 总结:association
- 第二个Mybatis程序:进阶,一对多处理
- 项目名称
- 案例背景
- 编写代码
- 1、Pojo
- 2、Utils
- 3、Dao:按照查询嵌套处理
- 4、Dao:按照结果嵌套处理
- 5、结果
- 总结:collection
- 动态SQL
- 项目名称
- 什么是动态SQL?
- 环境搭建
- 动态SQL-IF
- 动态SQL-choose、when、otherwise
- 动态SQL-Set
- 动态SQL-trim、sql片段
- 动态SQL-foreach
- 缓存
- 简介
- Mybatis缓存
- 一级缓存
- 二级缓存
- Mybatis调用顺序
- 自定义缓存-ehcache
- 介绍
项目源码地址
GitHub | https://github.com/Web-Learn-GSF/Java_Learn_Examples |
---|---|
父工程 | Java_Framework_Mybatis |
Mybatis概述
什么是Mybatis?
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射
- MyBatis 免除了几乎所有的 JDBC代码以及设置参数和获取结果集
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录
Mybatis导入
-
Maven仓库:https://mvnrepository.com/artifact/org.mybatis/mybatis
-
官方文档(中文):https://mybatis.org/mybatis-3/zh/index.html
知识补充
数据持久化
- 持久化就是将程序的数据从瞬时状态转化为持久状态的过程
- 生活结合:冰箱冷藏
- 内存中的数据:断电即失
- 可以持久化存储数据的媒介:数据库(jdbc)、io文件
持久层
- 完成持久化工作的代码块
- Dao层,Service层,Controller层…
第一个Mybatis程序:数据的增删改查查
项目名称
Java_Framework_Mybatis | First_Mybatis_Demo |
---|
创建环境
1、Maven项目创建
2、新建一个模块
3、导入相关依赖
4、建立数据库环境
-- 建表
CREATE TABLE `user`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 插入语句
INSERT INTO `user`(`id`, `name`, `pwd`) VALUES
(1, "狂神", "123456"),
(2, "张三", "1234567"),
(3, "李四", "1234568")
编写代码
1、目录结构
后续按代码编写顺序,贴出相关代码
2、核心配置文件:resources/mybatis-config.xml
主要完成对数据库的配置连接和Dao层xml文件的注册
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/learn_mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="GSF/Example/Dao/UserMapper.xml"/>
</mappers>
</configuration>
- mappers标签下的内容是在写了Dao层代码后,写入的
3、mybatis工具类:Utils/MybatisUtils
每次连接数据库之前,都需要通过sqlSessionFactory来获取sqlSession对象,从而进行后续的数据库连接、sql执行、关闭操作
将该获取过程提取抽象,写为工具类,方便每次调用使用
package GSF.Example.Utils;
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;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
String resources = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resources);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession getSqlsession(){
return sqlSessionFactory.openSession();
}
}
4、Pojo:User
和数据库中数据表相对应的实体类对象
- 字段和数据表字段基本一一对应
- 如果不对应,也可以通过一个resultMap映射,完成不对应字段之间的关联
set、get方法很关键,是执行sql语句完毕后,将查询结果写入实体类对象的关键
package GSF.Example.Pojo;
public class User {
private int id;
private String name;
private String pwd;
public User(){
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
5、Dao接口:UserDao
定义获取数据库对象的接口方法
package GSF.Example.Dao;
import GSF.Example.Pojo.User;
import java.util.List;
import java.util.Map;
public interface UserDao {
List<User> getUserList();
// 根据id查询用户
User getUserById(int id);
//插入一个用户
int addUser(User user);
// 用map的方式插入用户
int addUserByMap(Map<String, Object> map);
//修改用户
int updateUser(User user);
//删除一个用户
int deleteUser(int id);
}
6、Mapper配置文件:UserMapper.xml
写入接口方法的具体实现,需要写入相应的sql语句
- 代替的是原来UserDaoImpl中的相关实现
- 写完此配置文件,需要向 “2” 的核心配置文件中注册该mapper
<?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">
<!-- namespace=绑定一个Dao/Mapper接口-->
<mapper namespace="GSF.Example.Dao.UserDao">
<!-- select查询语句,ID对应方法名-->
<select id="getUserList" resultType="GSF.Example.Pojo.User">
select * from learn_mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="GSF.Example.Pojo.User">
select * from learn_mybatis.user where id=#{id}
</select>
<insert id="addUser" parameterType="GSF.Example.Pojo.User">
insert into learn_mybatis.user(id, name, pwd) values (#{id}, #{name}, #{pwd});
</insert>
<insert id="addUserByMap" parameterType="map">
insert into learn_mybatis.user(id, name, pwd) values (#{user_id}, #{user_name}, #{user_pwd});
</insert>
<update id="updateUser" parameterType="GSF.Example.Pojo.User">
update learn_mybatis.user set name=#{name}, pwd=#{pwd} where id=#{id};
</update>
<delete id="deleteUser" parameterType="int">
delete from learn_mybatis.user where id=#{id};
</delete>
</mapper>
测试
1、测试:UserTest
package GSF.Example.Dao;
import GSF.Example.Pojo.User;
import GSF.Example.Utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
public class UserTest {
@Test
public void GetUserList(){
// 官方建议用try...catch包裹,可以不用
SqlSession sqlsession = MybatisUtils.getSqlsession();
try {
UserDao mapper = sqlsession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlsession.close();
}
}
@Test
public void GetUserById(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
User user = mapper.getUserById(2);
System.out.println(user);
sqlsession.close();
}
@Test
public void AddUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
// 插入成功,返回值为1
int flag = mapper.addUser(new User(10, "GSF", "qwerty"));
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
@Test
public void AddUserByMap(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
// 插入成功,返回值为1
HashMap<String, Object> map_obj = new HashMap<>();
map_obj.put("user_id", 12);
map_obj.put("user_name", "121212");
map_obj.put("user_pwd", "zxcxvz");
int flag = mapper.addUserByMap(map_obj);
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
@Test
public void UpdateUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
int flag = mapper.updateUser(new User(1, "123", "12345678"));
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
@Test
public void DeleteUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
int flag = mapper.deleteUser(1);
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
}
常见问题汇总
第一次写程序,可能遇到各种各样错误,基本都涵盖如下:
- Dao层Mapper.xml配置文件没有在mybatis-config.xml中注册
- maven导出资源问题【资源不放在resources文件夹下,正常情况下没法输出到target文件夹中】
- 通过配置资源导出的目录解决该问题
<!-- 在项目的pom.xml文件中加入此配置 -->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
- src/main/resources和src/main/java目录下的所有.xml文件和.properties文件,都会被识别为资源,从而在构建的时候输出到target目录
第一个Mybatis程序:完善
项目名称
Java_Framework_Mybatis | First_Mybatis_Demo_Optimization |
---|
优化后的整体目录结构
1、通过db.properties文件配置数据库连接的基本设置
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/learn_mybatis?useUnicode=true&characterEncoding=utf-8
username=root
password=123456
- url中不需要转义符了
- 原url内容:
jdbc:mysql://localhost:3306/learn_mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
- 原url内容:
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<!-- properties标签的位置有严格的限制,不能随意位置更改-->
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
- 删除其他配置,方便理解当前改动
2、配置多套环境
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 核心配置文件 -->
<configuration>
<!-- properties标签的位置有严格的限制,不能随意位置更改 -->
<properties resource="db.properties"/>
<!-- 通过更改default的值,来实现不同环境的切换 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/learn_mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
3、起别名
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<!--properties标签的位置有严格的限制,不能随意位置更改-->
<properties resource="db.properties"/>
<typeAliases>
<!-- 给特定类名起别名,给出类的全路径标识,给出别名-->
<typeAlias type="GSF.Example.Pojo.User" alias="User"/>
<!--导入包名,在类名上没有注解的情况下,包内所有类的别名默认为类名的首字母小写-->
<package name="GSF.Example.Pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
4、映射器 mappers
方式1:将xml文件注册进核心配置文件(mybatis-config.xml)【强烈推荐】
- 不需要注意xml配置文件和对应的Dao层java代码的名字是否一致
<mappers>
<mapper resource="GSF/Example/Dao/UserDao.xml"/>
</mappers>
方式2:将接口类的完全限定名注册进核心配置文件(mybatis-config.xml)
<mappers>
<mapper class="GSF.Example.Dao.UserDao"/>
</mappers>
- 需要保证:接口和其mapper配置文件同名
- 需要保证:接口和其mapper配置文件在同一个文件夹下
方式3:将包注册进核心配置文件(mybatis-config.xml)
<mappers>
<package name="GSF.Example.Dao"/>
</mappers>
- 需要保证的内容跟方法2一样
第一个Mybatis程序:进阶
resultMap
解决pojo实体类中对应属性名和数据库中对应字段名不一致的问题
问题描述
当数据库的字段和实体类的字段不一致的时候,进行访问,会出现不一致的字段为空
数据库字段
实体类字段
获取id=1的数据库数据
解决1:改变Sql语句,在sql中起别名
<select id="getUserById" parameterType="int" resultType="GSF.Example.Pojo.User">
select id,name,pwd as password from mybatis.user where id =#{id}
</select>
解决2:使用resultMap
<?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">
<!-- namespace=绑定一个Dao/Mapper接口-->
<mapper namespace="GSF.Example.Dao.UserDao">
<resultMap id="UserMap" type="GSF.Example.Pojo.User">
<!-- column:数据库中的列名;property:实体类中的属性名,一样的就无需再配置进来(配置了也不影响)-->
<result column="pwd" property="password"/>
</resultMap>
<!--select查询语句,ID对应方法名-->
<select id="getUserList" resultMap="UserMap">
select id, name, pwd from learn_mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="user">
select id, name, pwd as password from learn_mybatis.user where id=#{id}
</select>
</mapper>
- resultMap标签下的result标签定义不一致字段之间的对应关系
- 对应的select标签:需要将resultType改为resultMap
注意事项!!!
- 当实体类没有空参构造,即使有字段不一致,Mybatis会强制将实体类中的字段和数据库中的字段进行属性匹配
- 可能出现字段不一致,但是并没有 字段=null 的情况出现
模糊查询
等到了项目,理解的才能更深刻
现在暂时参考如下文章:https://blog.csdn.net/Adobe_java/article/details/118460709
日志
日志工厂
有代码出问题了,日志就是最好的排错手段
mybatis官方支持的7种实现日志的方式:
- 掌握:LOG4J(第三方jar包)、STDOUT_LOGGING(标准日志输出)
STDOUT_LOGGING使用
mybatis-config.xml配置即可:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<!-- properties标签的位置有严格的限制,不能随意位置更改 -->
<properties resource="db.properties"/>
<!-- settings标签依旧有严格的位置限制 -->
<settings>
<!-- name和value的一个字都不能和官方规定的不一致:不要字符不一致,不要有多余的空格 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="development">
......
</environments>
</configuration>
设置了日志后的控制台输出:
LOG4J使用
什么是log4j
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 通过一个配置文件来灵活地进行配置,而不需要修改应用代码
log4j使用
1、导包
Maven:https://mvnrepository.com/artifact/log4j/log4j
2、log4j.properties配置(固定名字)【放在resources文件夹下】
### 配置根 ###
log4j.rootLogger = debug,console,file
### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = debug
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
### 配置输出到文件 ###
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.File = ./log/GSF.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = debug
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 配置输出到文件,并且每天都创建一个文件 ###
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.File = logs/log.log
log4j.appender.dailyRollingFile.Append = true
log4j.appender.dailyRollingFile.Threshold = debug
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.mybatis=debug
log4j.logger.java.sql=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug
3、mybatis-config.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<!-- properties标签的位置有严格的限制,不能随意位置更改-->
<properties resource="db.properties"/>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="development">
......
</environments>
</configuration>
前三个步骤完成后,访问数据库相关操作,就可以记录到日志中
4、代码中手动设置日志输出
package GSF.Example.Dao;
import GSF.Example.Pojo.User;
import GSF.Example.Utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
public class UserTest {
static Logger logger = Logger.getLogger(UserTest.class);
@Test
public void GetUserList(){
logger.info("info级别的日志,基本信息,该种日志打印出来,表示系统的正常运行痕迹");
logger.debug("debug级别的日志,用于排查错误内容,该种日志打印出来,用于排除系统的错误");
logger.error("error级别的日志,用于输出错误信息,该种日志打印出来,往往是异常捕捉后的输出");
SqlSession sqlsession = MybatisUtils.getSqlsession();
try {
UserDao mapper = sqlsession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlsession.close();
}
}
}
- 类中导入包
- 定义对象
- 因为日志中可以记录当前日志在什么文件中写入,所以logger的定义需要绑定当前类的class文件
- 每个使用log4j的方法,都需要在其类中定义logger对象(自认为,待确认?)
分页
Limit分页
SELECT * FROM user limit startIndex,pageSize;
SELECT * FROM user limit 0,2;
SELECT * FROM user limit 3; #[0,3]
1、接口定义
//分页
List<User> getUserByLimit(Map<String,Integer> map);
2、mapper.xml配置
<!-- 分页-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
3、测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex",1);
map.put("pageSize",2);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
RowBounds分页
不建议使用
1、接口定义
//分页2
List<User> getUserByRowBounds();
2、mapper.xml配置
<!--分页2-->
<select id="getUserByRowBounds" resultMap="UserMap">
select * from mybatis.user
</select>
3、测试
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(1, 2);
//通过java代码层面实现分页
List<User> userList = sqlSession.selectList("com.qjd.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
分页插件:PageHelper
官方地址:https://pagehelper.github.io/docs/howtouse/
第一个Mybatis程序:注解开发实现
上述三个章节:讲述的都是基于xml配置实现,该章节讲述基于注解开发实现
本质:基于反射和动态代理实现
项目名称
Java_Framework_Mybatis | First_Mybatis_Demo_Annotation |
---|
实现
1、接口上写入注解
package GSF.Example.Dao;
import GSF.Example.Pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface UserMapper {
@Select("select * from learn_mybatis.user")
List<User> getUserList();
//方法存在多个参数,所有的参数前面前面必须加上 @Param("") 注解
@Select("select * from learn_mybatis.user where id=#{id}")
User getUserByID(@Param("id") int id);
@Insert("insert into learn_mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd})")
int addUser(User user);
@Insert("insert into learn_mybatis.user(id,name,pwd) values(#{user_id},#{user_name},#{user_pwd})")
int addUserByMap(Map<String, Object> map);
@Update("update learn_mybatis.user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from learn_mybatis.user where id=#{uid}")
int deleteUser(@Param("uid") int id);
}
2、mybatis-config.xml核心配置文件的配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
......
<!-- 注册-->
<mappers>
<mapper class="GSF.Example.Dao.UserMapper"/>
</mappers>
</configuration>
3、测试
package GSF.Example.Dao;
import GSF.Example.Pojo.User;
import GSF.Example.Utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
public class UserTest {
@Test
public void GetUserList() {
// 官方建议用try...catch包裹,可以不用
SqlSession sqlsession = MybatisUtils.getSqlsession();
try {
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlsession.close();
}
}
@Test
public void GetUserById() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
User user = mapper.getUserByID(2);
System.out.println(user);
sqlsession.close();
}
@Test
public void AddUser() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
// 插入成功,返回值为1
int flag = mapper.addUser(new User(20, "GSF", "qwerty"));
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
@Test
public void AddUserByMap() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
// 插入成功,返回值为1
HashMap<String, Object> map_obj = new HashMap<>();
map_obj.put("user_id", 21);
map_obj.put("user_name", "121212");
map_obj.put("user_pwd", "zxcxvz");
int flag = mapper.addUserByMap(map_obj);
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
@Test
public void UpdateUser() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
int flag = mapper.updateUser(new User(1, "123", "12345678"));
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
@Test
public void DeleteUser() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
int flag = mapper.deleteUser(1);
System.out.println(flag);
sqlsession.commit();
sqlsession.close();
}
}
关于 @Param(“”) 注解
- 基本类型的参数或者String需要加上
- 如果只有一个基本类型的话,可以忽略,但是建议加上
- 引用类型的参数不需要加该注解,比如:User、Map…
- 在sql中引用的就是@Param(“”)注解中设定的属性名
#{} 和 ${} 的区别
#{} | ${} |
---|---|
预编译处理 | 是字符串替换 |
处理#{} 时,会将sql中的#{} 整体替换为占位符(即:?),调用PreparedStatement的set方法来赋值 | 在处理 $ { } 时,就是把 ${ } 替换成变量的值 |
可有效防止SQL注入 | 无法防止SQL注入 |
预编译的机制:
- 预编译是提前对SQL语句进行预编译,而后再调用SQL,注入的参数就不会再进行SQL编译
- 而SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译为SQL时轻而易举的通过,从而导致数据泄露
- 预编译机制则可以很好的防止SQL注入
Lombok
Lombok项目是一个Java库,他是一个插件,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符@Data就可以替换数百行代码从而产生干净,简洁且易于维护的Java类
使用步骤
- 在IDEA中安装Lombok插件
- 在项目中导入Lombok的jar包
- 在实体类上加入注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
}
注解 | 含义 |
---|---|
@Data | 提供属性的set、get方法、无参构造 |
@AllArgsConstructor | 提供类的全参构造 |
@NoArgsConstructor | 提供类的无参构造 |
第二个Mybatis程序:基础,多对一处理
项目名称
Java_Framework_Mybatis | Second_Mybatis_Demo_ManyToOne |
---|
案例背景
描述
- 多个学生对应一个老师
- 对于学生而言:多个学生关联一个老师【多对一】
- 对于老师而言:一个老师集合对应多个学生【一对多】
要求
- 查询所有的学生,及其关联的老师信息
数据库创建
-- 新建老师表
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
-- 插入老师数据
INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
-- 新建学生表
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
-- 插入学生数据
INSERT INTO `student` (`id`, `name`, `tid`) VALUES
('1', '小明', '1'),
('2', '小红', '1'),
('3', '小张', '1'),
('4', '小李', '1'),
('5', '小王', '1')
编写代码
1、Pojo
- 老师类
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
- 学生类
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
//学生需要关联一个老师
private Teacher teacher;
}
2、Utils
- 写法参照《第一个Mybatis程序:数据的增删改查查》
3、Dao:按照查询嵌套处理
思路:子查询
- 查询所有的学生信息
- 根据查询出来的学生的tid,寻找对应的老师
Mapper.xml
<select id="getTeacher" resultType="GSF.Example.Pojo.Teacher">
select * from learn_mybatis.teacher where id=#{id}
</select>
<!--根据查询嵌套处理-->
<select id="getStudent" resultMap="getStudent">
select id, name, tid from learn_mybatis.student;
</select>
<resultMap id="getStudent" type="GSF.Example.Pojo.Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="GSF.Example.Pojo.Teacher" select="getTeacher"/>
</resultMap>
test.java
@Test
public void GetStudent() {
// 官方建议用try...catch包裹,可以不用
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
List<Student> students = mapper.getStudent();
for (Student student : students) {
System.out.println(student);
}
sqlsession.close();
}
4、Dao:按照结果嵌套处理
思路:子查询
- 查询所有的学生信息、老师信息
- 将结果嵌套处理
Mapper.xml
<select id="getStudent2" resultMap="getStudent2">
select s.id s_id ,s.name s_name, t.id t_id, t.name t_name
from learn_mybatis.student s, learn_mybatis.teacher t
where s.tid=t.id
</select>
<resultMap id="getStudent2" type="GSF.Example.Pojo.Student">
<result property="id" column="s_id"/>
<result property="name" column="s_name"/>
<association property="teacher" javaType="GSF.Example.Pojo.Teacher">
<result property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
test.java
@Test
public void GetStudent() {
// 官方建议用try...catch包裹,可以不用
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
List<Student> students = mapper.getStudent();
for (Student student : students) {
System.out.println(student);
}
sqlsession.close();
}
5、结果
Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=1, name=秦老师))
Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=1, name=秦老师))
Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))
总结:association
- association标签,处理关联关系,即多对一
标签属性 含义 javaType 集合对象的类型
第二个Mybatis程序:进阶,一对多处理
项目名称
Java_Framework_Mybatis | Second_Mybatis_Demo_OneToMany |
---|
案例背景
描述
- 多个学生对应一个老师
- 对于学生而言:多个学生关联一个老师【多对一】
- 对于老师而言:一个老师集合对应多个学生【一对多】
要求
- 查询对应id的老师信息,及其包含的所有学生
数据库创建
- 同上
编写代码
1、Pojo
- 老师类
package GSF.Example.Pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
- 学生类
package GSF.Example.Pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int id;
private String name;
private int tid;
}
2、Utils
- 写法参照《第一个Mybatis程序:数据的增删改查查》
3、Dao:按照查询嵌套处理
思路:子查询
- 查询对应id的老师信息
- 根据查询出来的老师的tid,寻找对应的学生
Mapper.xml
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from learn_mybatis.teacher where id=#{id}
</select>
<resultMap id="TeacherStudent2" type="GSF.Example.Pojo.Teacher">
<!--两个地方用到id,这个result就得写出来,不然不展示老师的id-->
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" column="id" javaType="ArrayList" ofType="GSF.Example.Pojo.Student" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="GSF.Example.Pojo.Student">
select * from learn_mybatis.student where tid =#{id}
</select>
test.java
@Test
public void GetTeacherById2() {
// 官方建议用try...catch包裹,可以不用
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher);
sqlsession.close();
}
4、Dao:按照结果嵌套处理
思路:子查询
- 查询对应id的所有老师、学生信息
- 将结果嵌套处理
Mapper.xml
<select id="getTeacher" resultMap="getTeacher">
select s.id s_id, s.name s_name, t.name t_name, t.id t_id
from learn_mybatis.student s, learn_mybatis.teacher t
where s.tid = t.id and t.id=#{id}
</select>
<resultMap id="getTeacher" type="GSF.Example.Pojo.Teacher">
<result property="id" column="t_id"/>
<result property="name" column="t_name"/>
<collection property="students" javaType="ArrayList" ofType="GSF.Example.Pojo.Student">
<result property="id" column="s_id"/>
<result property="name" column="s_name"/>
<result property="tid" column="t_id"/>
</collection>
</resultMap>
test.java
@Test
public void GetTeacherById() {
// 官方建议用try...catch包裹,可以不用
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
sqlsession.close();
}
5、结果
Teacher(id=1, name=秦老师, students=[Student(id=1, name=小明, tid=1), Student(id=2, name=小红, tid=1), Student(id=3, name=小张, tid=1), Student(id=4, name=小李, tid=1), Student(id=5, name=小王, tid=1)])
总结:collection
-
collection标签,处理集合关系,即一对多
标签属性 含义 javaType 集合对象的类型 ofType 集合对象的类型
动态SQL
项目名称
Java_Framework_Mybatis | Third_Mybatis_Demo_DynamicSql |
---|
什么是动态SQL?
简单来说,就是根据不同的判断条件和需要,来动态拼接SQL语句。
在Mybatis之前,动态SQL的描述需要耗费很大精力。借助强大的OGNL表达式,Mybatis3替换了之前的大部分元素,使得动态SQL的描述变得简单很多。
环境搭建
- 数据库环境
--
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `blog`(`id`,`title`,`author`,`create_time`,`views`) VALUES
(1, "如何学Python", "青", "2022-10-11", 300),
(2, "如何学Java", "刘", "2022-10-12", 400),
(3, "如何学Django", "郭", "2022-10-13", 700)
-
java项目环境及相关配置:参考前文
-
Dao:实体类
@Data
public class Blog {
private int id;
private String title;
private String author;
private Date createTime;
private int views;
}
动态SQL-IF
public interface UserDao {
List<Blog> getBlogIf(Map<String, Object> map);
}
<select id="getBlogIf" parameterType="map" resultType="GSF.Example.Pojo.Blog">
select * from learn_mybatis.blog where 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
-- 若传入空的map,真实的sql语句:
select * from learn_mybatis.blog where 1=1
-- 若传入的map带有title键,真实的sql语句:
select * from learn_mybatis.blog where 1=1 and title=?
-- 若传入的map带有title键和author键,真实的sql语句:
select * from learn_mybatis.blog where 1=1 and title=? and author=?
- IF标签不具备自动添加and的功能,每个拼接的子SQL语句,需要自行添加and
@Test
public void GetBlogIf() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
Map<String, Object> objectMap = new HashMap<>();
//注释掉,就返回默认检索的所有情况
objectMap.put("title", "如何学Django");
objectMap.put("author", "刘");
List<Blog> blogIf = mapper.getBlogIf(objectMap);
for (Blog blog : blogIf) {
System.out.println(blog);
}
sqlsession.close();
}
动态SQL-choose、when、otherwise
public interface UserDao {
List<Blog> getBlogChoose_When_Otherwise(Map<String, Object> map);
}
<select id="getBlogChoose_When_Otherwise" parameterType="map" resultType="GSF.Example.Pojo.Blog">
select * from learn_mybatis.blog
<!-- 这里用到了下一个查询才讲述的where标签 -->
<where>
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="author !=null">
and author = #{author}
</when>
<otherwise>
and id = 1
</otherwise>
</choose>
</where>
</select>
-- 若传入空的map,真实的sql语句:
select * from learn_mybatis.blog where id=1
-- 若传入的map带有title键,真实的sql语句:
select * from learn_mybatis.blog where title=?
-- 若传入的map带有title键和author键,真实的sql语句:
select * from learn_mybatis.blog where title=?
- choose、when、otherwisel类似java的switch用法
- 从上到下的判断语句,遇到满足的就用,即使后续有条件也满足,也不会调用
- where标签具备自动添加where字符和删除首个子SQL语句and字符的功能
@Test
public void GetBlogChoose_When_Otherwise() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
Map<String, Object> objectMap = new HashMap<>();
//注释掉,就返回默认检索的所有情况
//objectMap.put("title", "如何学Django");
//objectMap.put("author", "刘");
List<Blog> blogIf = mapper.getBlogChoose_When_Otherwise(objectMap);
for (Blog blog : blogIf) {
System.out.println(blog);
}
sqlsession.close();
}
动态SQL-Set
public interface UserDao {
int updateBlogSet(Map<String, Object> map);
}
<update id="updateBlogSet" parameterType="map">
update learn_mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
</set>
where id = #{id}
</update>
-- 若传入空的map或仅仅有id的map,真实的sql语句:
报错
-- 若传入的map带有title键,真实的sql语句:
update learn_mybatis.blog SET title = ? where id = ?
-- 若传入的map带有title键和author键,真实的sql语句:
update learn_mybatis.blog SET title = ?, author = ? where id = ?
- set标签具备补充set字符和删除sql语句末尾“,”字符的功能
- if标签中sql子句末尾的“,”需要写入,不然sql语句报错
@Test
public void UpdateBlogSet() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
Map<String, Object> objectMap = new HashMap<>();
objectMap.put("id", 1);
objectMap.put("title", "如何学Python-修改");
objectMap.put("author", "刘");
int blogIf = mapper.updateBlogSet(objectMap);
System.out.println(blogIf);
sqlsession.commit();
sqlsession.close();
}
动态SQL-trim、sql片段
public interface UserDao {
int updateBlogTrim(Map<String, Object> map);
}
<sql id="if-title-author">
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
</sql>
<update id="updateBlogTrim" parameterType="map">
update learn_mybatis.blog
<trim prefix="SET" suffixOverrides=",">
<include refid="if-title-author">
</include>
</trim>
where id = #{id}
</update>
-- 若传入空的map或仅仅有id的map,真实的sql语句:
报错
-- 若传入的map带有title键,真实的sql语句:
update learn_mybatis.blog SET title = ? where id = ?
-- 若传入的map带有title键和author键,真实的sql语句:
update learn_mybatis.blog SET title = ?, author = ? where id = ?
- trim标签可以自定义待拼接sql语句的相关前缀、后缀的补充操作及去除操作
- 上述用trim标签,实现set标签的相关功能
trim标签属性 | 描述 |
---|---|
prefix | 给sql语句拼接的前缀 |
suffix | 给sql语句拼接的后缀 |
prefixOverrides | 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND" |
suffixOverrides | 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定,假设该属性指定为",“,当sql语句的结尾为”,“,trim标签将会去除该”," |
// 使用trim标签和SQL片段
@Test
public void UpdateBlogTrim_SQLFragment() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
Map<String, Object> objectMap = new HashMap<>();
objectMap.put("id", 1);
objectMap.put("title", "如何学Python-修改");
objectMap.put("author", "青-修改-修改");
int blogIf = mapper.updateBlogTrim(objectMap);
System.out.println(blogIf);
sqlsession.commit();
sqlsession.close();
}
动态SQL-foreach
public interface UserDao {
List<Blog> getBlogForeach(Map<String, Object> map);
}
<select id="getBlogForeach" parameterType="map" resultType="GSF.Example.Pojo.Blog">
select * from learn_mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
-- 若传入空的map,真实的sql语句:
select * from learn_mybatis.blog
-- 若传入的map中的list带有值1,真实的sql语句:
select * from learn_mybatis.blog WHERE ( id=? )
-- 若传入的map中的list带有值1、2、3,真实的sql语句:
select * from learn_mybatis.blog WHERE ( id=? or id=? or id=? )
- 提供遍历操作
- 还是传入的map,只是map的键对应的值是一个list
@Test
public void GetBlogForeach() {
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
ids.add(4);
HashMap<String, Object> map = new HashMap<>();
map.put("ids", ids);
List<Blog> blogIf = mapper.getBlogForeach(map);
for (Blog blog : blogIf) {
System.out.println(blog);
}
sqlsession.close();
}
缓存
简介
什么是缓存?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用了从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
为什么使用缓存?
- 减少和数据库的交互次数,较少系统开销,提高系统效率
什么样的数据能使用缓存?
- 经常查询而且不经常改变的数据
Mybatis缓存
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启 (SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
也称为本地缓存
与数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库
测试验证一级缓存
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
System.out.println("=================================================================");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.close();
}
- 返回结果为true,且只执行了一次sql语句
一级缓存失效条件
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的Mapper.xml
- 手动清理缓存!
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
// 更新数据,导致缓存时效
//mapper.updateUser(new User(2,"niahoooo","309487"));
// 手动清理缓存,导致缓存时效
//sqlSession.clearCache();
System.out.println("=================================================================");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.close();
}
一级缓存生命周期
生命周期为一个特定mapper.xml的一次sqlsession会话
二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中:
- 如果当前会话关闭了,这个会话对应的一级缓存就没了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper.xml查出的数据会放在自己对应的缓存(map)中
测试验证二级缓存
- 在全局开启二级缓存:mybatis-config.xml
<setting name="cacheEnable" value="true"/>
- 在要开启缓存的mapper.xml中开启
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
- 测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.close();
sqlSession2.close();
}
注意
- 我们需要将实体类序列化(实现Serializable接口),否则就会报错
- sqlsession关闭的时候一定要在最后关闭,不能先关闭sqlsession再关闭sqlsession2,这样会导致Cause: org.apache.ibatis.executor.ExecutorException: Executor was closed
二级缓存的生命周期
在同一个Mapper.xml下的多次Sqlsession
只有当sqlsession关闭的时候,数据才会从一级缓存扔到二级缓存
Mybatis调用顺序
- 先看二级缓存中有没有
- 再看一级缓存中有没有
- 查询数据库
- 一二级缓存都没有,查询数据库,查询后将数据放入一级缓存
自定义缓存-ehcache
介绍
- EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider
- Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
具体使用,用到再说,开发中常用Redis数据库来做缓存