Mybatis笔记
常用快捷键
代码块:Ctrl+Shift+K
公式块:Ctrl+Shift+M
引用:Ctrl+Shift+Q
有序列表:Ctrl+Shift+[
无序列表:Ctrl+Shift+]
1、Mybatis简介
官网:https://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2、第一个Mybatis程序
2.1、基本步骤
1)建立一个maven项目,
** 在pom.xml文件中**加载mysql驱动依赖、mybatis依赖和junit依赖****
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>LogMybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<name>LogMybatis</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<!--mysqL驱动依赖版本不能过高-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--log4j依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
</project>
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 resource="dp.properties"/>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!--别名-->
<typeAliases>
<typeAlias type="com.bjpowernode.pojo.user" alias="User" />
</typeAliases>
<environments default="jdbc">
<environment id="jdbc">
<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>
<!--连接mapper, 注意路径是target下面生成的路径,使用斜杠"/",使用复制路径-->
<mappers>
<mapper resource="com/bjpowernode/dao/UserDao.xml"/>
</mappers>
</configuration>
其中,JDBC注册驱动按下面的properties配置文件进行注册连接
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bjpowernode?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=199478
3)书写相关类
书写pojo.user(数据库表)实例类、utils.util的JDBC工具类和dao.userdao(userMapper)接口,并配置实现方法的xml文件
a. 书写pojo.user数据库实例类(注意和数据库名字保持一致)
package com.bjpowernode.pojo;
public class user {
private int Id;
private String name;
private int age;
public user() {
}
public user(int id, String name, int age) {
Id = id;
this.name = name;
this.age = age;
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "user{" +
"Id=" + Id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
b. 书写dao.userdao接口和实现xml配置文件
//dao.userdao接口
package com.bjpowernode.dao;
import com.bjpowernode.pojo.user;
import java.util.List;
import java.util.Map;
public interface UserDao {
List<user> selectUser();
//插入数据
public int insertUser(user u);
//根据id查找数据
public user getuserById(int id);
//更新
public int updateById(user user);
//删除数据
public int deleteById(int Id);
//通过map方式操作数据库
public int addByMap(Map<String, Object> map);
//根据Id实现分页查询
public List<user> selectByLimit(Map<String, Integer> map1);
}
<?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.bjpowernode.dao.UserDao">
<!--借助与map实现limit分页查询-->
<select id="selectByLimit" parameterType="map" resultType="User">
select * from user limit #{startIndex}, #{pageSize}
</select>
<!--普通查询-->
<select id="selectUser" resultType="com.bjpowernode.pojo.user">
select * from user
</select>
<!--插入数据-->
<insert id="insertUser" parameterType="com.bjpowernode.pojo.user">
insert into user (Id,name,age) values (#{Id},#{name}, #{age})
</insert>
<!--插入map类型数据-->
<insert id="addByMap" parameterType="map">
insert into user (Id,name,age) values (#{mapId},#{mapName},#{mapAge})
</insert>
<!--获取数据-->
<select id="getuserById" resultType="com.bjpowernode.pojo.user">
select * from user where Id=#{Id}
</select>
<!--根据id更新数据-->
<update id="updateById" parameterType="com.bjpowernode.pojo.user">
update user set name = #{name}, age =#{age} where Id=#{Id}
</update>
<!--根据id删除数据-->
<delete id="deleteById" >
delete from user where Id=#{Id}
</delete>
</mapper>
c. 书写util工具类,包装SqlSession实例对象
package com.bjpowernode.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 utils {
static private SqlSessionFactory sqlSessionFactory;
static {
try {
//这三句话是mybatis官网固定写法
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getsqlSession(){
return sqlSessionFactory.openSession();
}
}
4) 编写测试类,实现测试
package com.bjpowernode.dao;
import com.bjpowernode.Utils.utils;
import com.bjpowernode.pojo.user;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Testdao {
static Logger logger = Logger.getLogger(Testdao.class);
//增、删、改需要提交事务
@Test
public void selectByLimit(){
//实现分页查询的功能
SqlSession sqlSession = null;
try{
sqlSession = utils.getsqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String,Integer> map1 = new HashMap<>();
//Map<String,Integer> map2 = new HashMap<>();
map1.put("startIndex", 1);
map1.put("pageSize", 2);
List<user> users = mapper.selectByLimit(map1);
for (user user : users) {
System.out.println(user);
}
}finally {
sqlSession.close();
}
}
//采用map方式插入数据
@Test
public void insertByMap(){
SqlSession sqlSession = null;
try {
sqlSession = utils.getsqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("mapId", 7);
map.put("mapName", "张飞");
map.put("mapAge", 42);
int nums = mapper.addByMap(map);
sqlSession.commit();
if (nums>0)
System.out.println("数据插入成功~");
else
System.out.println("数据插入失败~~");
}finally {
sqlSession.close();
}
}
}
3、CRUD 增删改查
package com.bjpowernode.dao;
import com.bjpowernode.Utils.utils;
import com.bjpowernode.pojo.user;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Testdao {
static Logger logger = Logger.getLogger(Testdao.class);
//增、删、改需要提交事务
//查询
@Test
public void selectByLimit(){
//实现分页查询的功能
SqlSession sqlSession = null;
try{
sqlSession = utils.getsqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String,Integer> map1 = new HashMap<>();
//Map<String,Integer> map2 = new HashMap<>();
map1.put("startIndex", 1);
map1.put("pageSize", 2);
List<user> users = mapper.selectByLimit(map1);
for (user user : users) {
System.out.println(user);
}
}finally {
sqlSession.close();
}
}
@Test
public void tse01(){
SqlSession sqlSession = null;
try{
//获取SqlSession
sqlSession = utils.getsqlSession();
//获取mapper,等价于dao。用于操作数据库的对象实例
UserDao mapper = sqlSession.getMapper(UserDao.class);
//调用方法,进行查询操作
List<user> userList = mapper.selectUser();
for (user user : userList) {
System.out.println(user);
}
}finally {
//关闭资源,放在finally块中,防止忘记关闭
sqlSession.close();
}
}
@Test
public void testLog4j(){
logger.debug("DUBUG:进入了Bug测试");
logger.error("Error:进入了Bug测试");
logger.info("Info:进入了Bug测试");
}
@Test
public void testInsert(){
user us = new user(11,"鲁智深",35);
SqlSession sqlSession = null;
try{
sqlSession = utils.getsqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int nums = mapper.insertUser(us);
//注意提交事务
sqlSession.commit();
if (nums > 0){
System.out.println("插入数据成功~~");
}else {
System.out.println("插入输入失败~~~");
}
}finally {
sqlSession.close();
}
}
//根据id查询
@Test
public void getuserById(){
SqlSession sqlSession = null;
try{
sqlSession = utils.getsqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
user user = mapper.getuserById(4);
System.out.println(user);
}finally {
sqlSession.close();
}
}
//更新数据库
@Test
public void updateUser(){
SqlSession sql = null;
try{
user us = new user(1,"关羽",56);
sql = utils.getsqlSession();
UserDao mapper = sql.getMapper(UserDao.class);
int nums = mapper.updateById(us);
//提交事务
sql.commit();
if(nums > 0){
System.out.println("数据更新成功~~~");
}else {
System.out.println("数据更新失败~~");
}
}finally {
sql.close();
}
}
@Test
public void deleteById(){
SqlSession sqlSession = null;
try{
sqlSession = utils.getsqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int nums = mapper.deleteById(3);
//提交事务
sqlSession.commit();
if(nums > 0)
System.out.println("数据删除成功~~");
else
System.out.println("数据删除失败~~");
}finally {
sqlSession.close();
}
}
//采用map方式插入数据
@Test
public void insertByMap(){
SqlSession sqlSession = null;
try {
sqlSession = utils.getsqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("mapId", 7);
map.put("mapName", "张飞");
map.put("mapAge", 42);
int nums = mapper.addByMap(map);
sqlSession.commit();
if (nums>0)
System.out.println("数据插入成功~");
else
System.out.println("数据插入失败~~");
}finally {
sqlSession.close();
}
}
}
4、配置解析
5、resultMap
解决字段名和属性名字不一致的情况
6、日志
7、分页
1)limit
2)RowBounds(了解)
8、使用注解开发
8.1 面向接口编程
根本原因:解耦和
关于接口的理解
- 接口从更深层次的理解,应该是定义(规范、约束)与实现(名、实分离的原则)的分离
- 接口的本身反映了系统设计人员的抽象理解
- 接口应该有两类:
- 第一类是对一个个体的抽象,即对应一个抽象体
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面
- 一个个体可能有多个抽象面,抽象体与抽象面是由区别的
三个面向区别
- 面向对象是指,我们在考虑问题时,以对象为单位,考虑他的属性及方法
- 面向过程是指,我们在考虑问题时,以一个具体的流程(事务过程)为单位,考虑他的实现
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题。更多的体现是对系统整体的架构。
8.2 面向注解开发流程
1. 注解在接口中的实现
package com.bjpowernode.dao;
import com.bjpowernode.pojo.user;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface UserDao {
//使用注解方式进行查询、删除、增加、更新等操作
@Select("select * from user limit 1,3")
public List<user> selectUser();
}
2. 需要在核心配置mybatis-config.xml文件中绑定接口
<mappers>
<!--mapper class绑定接口-->
<mapper class="com.bjpowernode.dao.UserDao"/>
</mappers>
3. 测试:编写测试类
4. 底层实现原理:动态代理;本质:反射
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ul9IOBS5-1601702455044)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201001180836065.png)]
8.3 注解的CRUD
-
设置自动提交
public static SqlSession getsqlSession(){ //true表示设置自动提交事务 return sqlSessionFactory.openSession(true); }
-
使用注解实现增删改查
package com.bjpowernode.dao; import com.bjpowernode.pojo.user; import org.apache.ibatis.annotations.*; import java.util.List; import java.util.Map; public interface UserDao { @Select("select * from user") List<user> selectUser(); //根据id查询, 注意Param中的参数名字与#{Id}一致 @Select("select * from user where Id = #{userId}") user getUserById(@Param("userId") int id); //添加用户 @Insert("insert into user(Id, name, age) values (#{userId}, #{userName}, #{userAge})") int addUser(@Param("userId") int id, @Param("userName") String name, @Param("userAge") int age); //更新 @Update("update user set name=#{name}, age=#{age} where Id=#{Id}") int updateUser(user user); //删除 @Delete("delete from user where Id=#{userId}") int deleteUser(@Param("userId") int id); }
-
编写测试类执行操作
@Test public void tse01(){ SqlSession sqlSession = null; try{ //获取SqlSession sqlSession = utils.getsqlSession(); //获取mapper,等价于dao。用于操作数据库的对象实例 UserDao mapper = sqlSession.getMapper(UserDao.class); //调用方法,进行查询操作 List<user> userList = mapper.selectUser(); for (user user : userList) { System.out.println(user); } }finally { //关闭资源,放在finally块中,防止忘记关闭 sqlSession.close(); } }
-
注意:在mybatis-config中绑定接口
<mappers> <mapper class="com.bjpowernode.dao.UserDao"/> </mappers>
-
关于@Param()注解
-
基本类型的参数或者String类型,需要加入,引用类型不需要加
//更新 @Update("update user set name=#{name}, age=#{age} where Id=#{Id}") int updateUser(user user);
-
如果只有一个基本类型,可以忽略,但是建议加上
-
在SQL中引用的就是@Param()中设置的属性名字
-
9、Lombok
idea构造方法的插件,一般不用
10、多对一处理
-
测试环境搭建
-
新建实体类Teacher、Student
-
建立Mapper接口
-
建立Mapper.xml文件
-
在核心配置文件中绑定注册我们的Mapper接口或者文件
<mappers> <!--绑定class--> <mapper class="com.bjpowernode.dao.TeacherMapper"/> <mapper class="com.bjpowernode.dao.StudentMapper"/> <!--第二种:绑定mapper配置文件--> </mappers>
-
测试
案例:需求:查找所有学生,即对应的老师
- 方式1:按照查询嵌套处理:对应于子查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjpowernode.dao.StudentMapper">
<!--查询所有学生的信息,以及对应的老师的信息
1. 查询所有的学生
2. 根据查询出来的学生的id,寻找对应的老师
-->
<select id="SelectStudent" resultMap="StudentTeacher">
select * from student
</select>
<!--StudentTeacher表示查询出来的学生对应的老师 ,进行结果集映射-->
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id" />
<result property="name" column="name"/>
<!--复杂属性:需要单独处理 对象:association 集合:collection-->
<association property="teacher" column="tid" javaType="Teacher" select="selectTeacher"/>
</resultMap>
<select id="selectTeacher" resultType="Teacher">
select * from teacher where id=#{id};
</select>
</mapper>
方式2:按照结果映射处理:对应于连表查询
<!--第二种:按照结果映射查询-->
<!--查询语句
select s.id sid, s.name sname, t.name from student s, teacher t where sid = tid
-->
<select id="SelectStudent2" resultMap="StudentTeacher2">
select s.id sid, s.name sname, t.id tid, t.name tname from student s, teacher t where s.tid = t.id
</select>
<!--type指java实体类的类型,这儿查询出来的是一个Student类-->
<resultMap id="StudentTeacher2" type="Student">
<!--property是java实体类的类型的属性名字 column是数据库的名字-->
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
11、一对多处理
- 一个老师对应多个学生,对于老师来说是一对多关系
1. 方式1:
-
按照查询嵌套处理:对应于子查询
<!--方式1:按照查询映射处理,对应于子查询--> <select id="selectTeacher2" resultMap="TeacherStudent2"> select * from teacher </select> <resultMap id="TeacherStudent2" type="Teacher"> <result property="id" column="id"/> <result property="name" column="name"/> <collection property="student" column="id" select="studentselect" javaType="ArrayList" ofType="Student"/> </resultMap> <select id="studentselect" resultType="Student"> select * from student where tid=#{id} </select>
2. 方式2:结果集映射
- 按照结果嵌套处理:对应于子查询
<!--方式2:按照结果嵌套查询(简单),resultMap的使用理解-->
<select id="selectTeacher" resultMap="TeacherStudent">
select t.id tid, t.name tname, s.id sid, s.name sname, s.tid from teacher t, student s where
s.tid=t.id
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--复杂的属性,我们需要单独处理,对象使用association处理
集合使用collection处理
ofType是java指定的属性类型,集合中的泛型使用ofType获取
-->
<collection property="student" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
3. 小结
- 关联 --association【多对一】
- 集合–collection 【一对多】
- javaType 和 ofType
- javaType:指定实体类中属性的类型
- ofType:指定映射到List或者集合中的泛型(pojo) 类型,泛型中的约束类型
注意点:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一中,属性名字和字段的问题
- 如果问题不好排查错误,可以使用日志,建议使用log4j
面试高频(慢sql):
- Mysql引擎
- InnoDB底层原理
- 索引
- 索引优化
12、动态SQL
-
动态SQL:根据不同的条件生成不同的SQL语句
-
动态SQL就是拼接sql语句,首先需要保证sql语句正确
-
所以在写的时候,建议首先写出正确的sql语句,然后再在mybatis中写出对应的操作语句
1. if
<!--动态Sql之if的用法-->
<select id="selectBlogByIf" parameterType="map" resultType="blog">
select * from blog where 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
2. where
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<select id="selectBlogByIfWhere" parameterType="map" resultType="blog">
select * from blog
<where>
<!--智能的自动拼接sql-->
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
3. choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
下面的语句实现选择查询:如果传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
4. sql片段
使用sql标签抽取公共部分,实现代码复用,在使用地方使用include标签引用即可
注意事项:
1. 最好基于单表来定义sql片段;
2. 不要存在where标签
<sql id="sqlpianduan">
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</sql>
<!--动态Sql之if的用法-->
<select id="selectBlogByIf" parameterType="map" resultType="blog">
select * from blog where 1=1
<include refid="sqlpianduan"></include>
</select>
5. trim、where、set
<update id="updateBlogBySet" parameterType="map">
update blog
<set>
<if test="author != null">
author=#{author},
</if>
<if test="title != null">
title=#{title}
</if>
</set>
where id=#{id}
</update>
6. Foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。
<!--参数解释:select * from blog where (id = 1 or id = 2 or id = 3)
collection 传入的进行遍历的list集合
item 集合中的每一个元素
open 拼接的sql以什么开头, 这里以"("开头
close 拼接的sql以什么结尾,这里以")"开头
separator 以什么分割,这里是or
index 起始下标 这里没有(默认从头开始)
-->
<select id="selectBlogByForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" separator=" or " open=" (" close=" )">
id=#{id}
</foreach>
</where>
</select>
//测试foreach的测试函数
@Test
public void selectBlogByForeach(){
SqlSession sqlSession = null;
try {
sqlSession = utils.getsqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
//传入一个map集合,集合中的元素就是需要遍历的list集合
HashMap map = new HashMap();
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
//注意这儿的key需要与xml文件中的foreach语句中名字对应,否则找不到
//比如map中放的key应该和collection="ids"名字一致
map.put("ids",ids);
List<Blog> blogs = mapper.selectBlogByForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}finally {
sqlSession.close();
}
}
13、缓存
1. 缓存简介
-
定义:存在内存中的临时数据叫做缓存
-
目的:提高查询速度
-
能够使用缓存的数据:经常使用且不经常改变的数据
2. mybatis缓存
- mybatis系统默认定义了两级缓存:一级缓存和二级缓存
- 默认情况开启一级缓存
- 二级缓存需要手动开启和配置,基于namespace级别的缓存
- mybatis定义了缓存接口Cache,可以通过实现Cache接口自定义二级缓存
- 基本上就是这样。Mybatis简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
3. 一级缓存
- 一级缓存也叫本地缓存:SqlSession (SqlSession 开启到关闭阶段)
- 一级缓存默认自动开启,只在一次SqlSession 中有用
- 一级缓存相当于一个map
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b97vy4Un-1601702455047)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201002105704407.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9bTYiR9c-1601702455049)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201002110137484.png)]
4. 二级缓存
二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是:会话关闭了,一级缓存中的数据会被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存中
步骤:
-
开启全局缓存
<!--配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
在要使用二级缓存的mapper中开启
<cache/>
5. 缓存原理
查询时,先看二级缓存有没有数据,再看一级缓存有没有,否则走数据库。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRiu7hDU-1601702455051)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201002130721204.png)]
6. 自定义缓存
Ehcacha是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjpowernode.dao.TeacherMapper">
<!--配置Ehcache缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
</mapper>
。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是:会话关闭了,一级缓存中的数据会被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存中
步骤:
-
开启全局缓存
<!--配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
在要使用二级缓存的mapper中开启
<cache/>
5. 缓存原理
查询时,先看二级缓存有没有数据,再看一级缓存有没有,否则走数据库。
[外链图片转存中…(img-nRiu7hDU-1601702455051)]
6. 自定义缓存
Ehcacha是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjpowernode.dao.TeacherMapper">
<!--配置Ehcache缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
</mapper>
Redis数据库来做缓存