mybatis04
mybatis 缓存
一、mybatis 缓存概述
1、缓存
缓存 是存在于内存中的临时数据,使用缓存的目的是:减少和数据库的交互次数,提高执行效率。
2、mybatis 缓存
mybatis 与 大多数的持久层框架一样,提供了缓存策略,通过策略减少数据库的查询次数,从而提高性能。
3、mybatis 缓存分类
- 一级缓存
- 二级缓存
二、一级缓存
1、一级缓存介绍
(1)描述
mybatis 一级缓存,是一种 session 级别的,针对同一个会话SqlSession中,执行多次条件完全相同的同一个SQL,那么会共享这一个缓存。
(2)特点
- 自带的,不能卸载
- SQLSession 级别的,使用无需配置
2、一级缓存结构图
3、一级缓存示例代码
(1)项目结构和pom.xml、jdbc.properties、mybatisConfig.xml文件与mybatis03一样
(2)持久层接口 StudentMapper.java
package com.etime.mapper;
import com.etime.pojo.Student;
import java.util.List;
public interface StudentMapper {
List<Student> getAllStudent();
}
(3)持久层接口映射文件 StudentMapper.xml
<?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.etime.mapper.StudentMapper">
<select id="getAllStudent" resultType="Student">
SELECT * FROM student
</select>
</mapper>
(4)在mybatisConfig.xml文件中设置setting
<settings>
<!--日志打印-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 注意setting标签的位置
(5)编写测试方法
@Test
public void t01() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list1 = studentMapper.getAllStudent();
list1.forEach(System.out::println);
System.out.println("----------------------------------");
List<Student> list2 = studentMapper.getAllStudent();
list2.forEach(System.out::println);
sqlSession.close();
}
4、一级缓存分析
从上面的代码可以出,我们写了两次查询操作,但在访问数据时,只有一次。
第一次先从一级缓存中获取,因为session是新创建的,一级缓存中没有数据,于是就查询数据获取数据,然后把查询的数据放到一级缓存中,此时一定要注意的是,一级缓存是一个Map集合,map的key是你的查询条件字符串,值就是查询出来的对象。
第二次查询时,先从一缓存中获取,因为上一次查询后已经放到一级缓存中了,所以从一级缓存中获取到了,就不用访问数据库了,减少和数据次的一次交互,提高了执行效率。
5、一级缓存的清空
- 代码
@Test
public void test01(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = studentMapper.getAllStudent();
// 清理以及缓存
sqlSession.clearCache();
list.forEach(System.out::println);
System.out.println("--------------------------");
List<Student> list2 = studentMapper.getAllStudent();
list2.forEach(System.out::println);
sqlSession.close();
}
- 结果
6、总结一级缓存清空三种方式
- clearCache();
- 执行数据库的操作:delete、insert、update;
- 手动提交事务:commit();
- 对于查询来说,事务可以不提交。故使用该方式需考虑情况
三、二级缓存
1、二级缓存介绍
(1)描述
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个
SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
(2)特点
- 默认不开启
- 使用需配置
2、二级缓存结构图
3、二级缓存的开启与关闭
(1)在mybatisConfig.xml文件中开启二级缓存
<settings>
<!--日志打印-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
- cacheEnabled
- true:默认值,开启
- false:关闭
(2)配置映射文件 StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--
引入dtd约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace 是当前mapper对应的接口-->
<mapper namespace="com.etime.mapper.StudentMapper">
<cache></cache>
<select id="getAllStudent" resultType="Student">
select * from student
</select>
</mapper>
- 注意:cache标签的位置
(3)在映射文件 StudentMapper.xml 配置statement 上面的 userCache属性
<?xml version="1.0" encoding="UTF-8" ?>
<!--
引入dtd约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace 是当前mapper对应的接口-->
<mapper namespace="com.etime.mapper.StudentMapper">
<cache></cache>
<select id="getAllStudent" resultType="Student" useCache="true">
select * from student
</select>
</mapper>
- useCache
- true:开启
- false:关闭
- 注意:针对每次查询都需要最新的数据sql,要禁用二级缓存
(4)二级缓存测试
a.实体类需要序列化
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student implements Serializable {
private int sid;
private String sname;
private int cid;
}
b.测试方法
@Test
public void test01(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = studentMapper.getAllStudent();
sqlSession.clearCache();
list.forEach(System.out::println);
System.out.println("--------------------------");
List<Student> list2 = studentMapper.getAllStudent();
list2.forEach(System.out::println);
sqlSession.close();
}
4、二级缓存分析
从结果可以看出,第一次查询将所有学生信息存入一级缓存,然后存入二级缓存中。
第二次查询,我们现将一级缓存清除,再进行查询发现是从二级缓存取出的学生信息,但它又从数据库中查询数据。
可以得出结论,在没有清除一级缓存时,我们是从二级缓存中取得的数据。
mybatis 分页插件
一、什么是分页
分页是将所有数据分段展示各用户的技术,用户所看到的数据只是一部分。若用户没有想要的内容,可通过指定页面、翻页的方式转换内容。
二、分页的好处
1、提高性能
数据量很大,若一次性查出,浪费内存 ,降低了效率。
2、展现层面的考虑
不好排版,不美观
三、分页插件的使用
1、引入依赖,在pom.xml文件中
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
2、在mybatisConfig.xml配置文件中 配置分页
- 注意:配置分页的位置在environments标签之前
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
3、在接口 StudentMapper.java 中定义方法
package com.etime.mapper;
import com.etime.pojo.Student;
import java.util.List;
public interface StudentMapper {
List<Student> getAllStudent();
}
4、 在映射文件 StudentMapper.xml 配置信息
<?xml version="1.0" encoding="UTF-8" ?>
<!--
引入dtd约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace 是当前mapper对应的接口-->
<mapper namespace="com.etime.mapper.StudentMapper">
<cache></cache>
<select id="getAllStudent" resultType="Student" useCache="true">
select * from student
</select>
</mapper>
5、测试
@Test
public void test02() {
// 设置当前页码及每页显示条数 必须在获得sqlSession对象的前面设置
PageHelper.startPage(1,2);
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = studentMapper.getAllStudent();
// 从查到的数据 取出当前页数据,生成pageInfo对象
PageInfo<Student> pageInfo = new PageInfo<>(list);
// 从pageInfo对象中获取当页数据
List<Student> pageList = pageInfo.getList();
System.out.println("当前页数据");
pageList.forEach(System.out::println);
// 获取总数据条数
long total = pageInfo.getTotal();
System.out.println("总数据条数 = " + total);
// 获取总页数
int pages = pageInfo.getPages();
System.out.println("总页数 = " + pages);
sqlSession.close();
}
mybatis 注解开发
一、mybatis 注解开发概述
注解提供了一种简单的方式来实现 简单映射语句,而不糊引入大量的开销。
能够读懂别人的代码,特别是框架相关的代码。
本来是可能需要很多配置文件,需要很多逻辑才能实现事务使用一个或多个注解来替代,这样就使得编程更加简洁,代码更加清晰。
二、mybatis 注解介绍
注解 | 描述 |
---|---|
@Insert | 新增 |
@Update | 更新 |
@Delete | 删除 |
@Select | 查询 |
@Result | 结果封装集 |
@Results | 与@Result使用,封装多个结果集 |
@ResultMap | 引用@Results定义的封装 |
@One | 一对一结果封装集 |
@Many | 一对多结果哦封装集 |
@SelectProvider | 动态SQL映射 |
@CacheNamespace | 注解二级缓存的使用 |
三、注解实现基本增删改查
1、实体类
- 学生类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int cid;
}
2、在接口 StudentMapper中定义方法并使用注解
- mybatisConfig.xml 中映射文件的注册信息不变
- 将resource资源包中的 映射文件给删除
package com.etime.mapper;
import com.etime.pojo.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface StudentMapper {
@Select("select * from student")
List<Student> getAllStudent();
@Insert("insert into student(sname,cid) values(#{sname},#{cid})")
int addStudent(Student student);
@Update("update student set sname=#{sname},cid=#{cid} where sid=#{sid}")
int updateStudent(Student student);
@Delete("delete from student where sid=#{sid}")
int deleteStudentBySid(int sid);
}
3、测试
package com.etime.test;
import com.etime.mapper.StudentMapper;
import com.etime.pojo.Student;
import com.etime.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class StudentTest {
@Test
public void test01(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.getAllStudent();
list.forEach(System.out::println);
sqlSession.close();
}
@Test
public void test02() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int res = mapper.addStudent(new Student(0, "胡神哎", 1));
System.out.println("res = " + res);
sqlSession.close();
}
@Test
public void test03() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int res = mapper.updateStudent(new Student(8, "胡-寂", 1));
System.out.println("res = " + res);
sqlSession.close();
}
@Test
public void test04() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int res = mapper.deleteStudentBySid(8);
System.out.println("res = " + res);
sqlSession.close();
}
}
四、复杂关系的注解
1、注解介绍
(1)@Results注解
- 代替标签:
- 使用示例:@Results({@Result(),@Result()})或者 @Results(@Result())
- 使用说明:该注解中可以使用单个 @Result,或 @Result集合
(2)@Result注解
- 代替标签:、
- @Result 属性介绍
- id:是否 是主键字段
- properties:需要配置实体类的属性
- column:数据库表的列名
- one:需使用 @One 注解
- 例:@Result(one=@One)
- many:需使用 @Many 注解
- 例:@Result(many=@Many)
(3)@One注解
- 描述:一对一,是多表插叙的关键,用来指定子查询返回单一对象。
- 代替标签:
- @One 属性介绍
- select:指定多表查询的 sqlMaper。使用完全限定名确定方法的位置。
- 示例:@Result(column=" “,property=”“,one=@One(select=”"))
(4)@Many 注解
- 描述:多对一,是多表查询的关键,指定子查询返回对象集合
- 代替标签:
- 注意:聚集元素用来处理“一对多”的关系。需要执行映射的java实体类的属性,属性的javaType。
- 示例:@Result(property=“”,column=“”,many=@Many(select=“”,javaType=“”))
2、一对一关系注解
(1)实体类
-
以妻子表 和 丈夫表为例
-
妻子类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Wife {
private int wid;
private String wname;
}
- 丈夫类(主体)
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Husband {
private int hid;
private String hname;
private int wid;
private Wife wife;
}
(2)在接口 HusbandMapper.java 中使用注解方法
package com.etime.mapper;
import com.etime.pojo.Husband;
import com.etime.pojo.Wife;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface HusbandMapper {
// 查询所有丈夫中,对应一对一的妻子
@Select("select * from husband")
@Results({
@Result(property = "hid",column = "hid"),
@Result(property = "hname",column = "hname"),
@Result(property = "wid",column = "wid"),
/*
javaType:指定类型
column:指定传入那列 为参数
one:一对一,返回一个对象
select:子查询的方法的位置
*/
@Result(property = "wife",javaType = Wife.class,column = "wid",
one = @One(select = "com.etime.mapper.WifeMapper.getWifeByWid"))
})
List<Husband> getAllHusband();
}
(3)在接口 WifeMapper.java 中使用注解方法,定义子查询
package com.etime.mapper;
import com.etime.pojo.Wife;
import org.apache.ibatis.annotations.Select;
public interface WifeMapper {
@Select("select * from wife where wid=#{wid}")
Wife getWifeByWid(int wid);
}
(4)测试
package com.etime.test;
import com.etime.mapper.HusbandMapper;
import com.etime.pojo.Husband;
import com.etime.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class HusbandTest {
@Test
public void test01(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
HusbandMapper mapper = sqlSession.getMapper(HusbandMapper.class);
List<Husband> list = mapper.getAllHusband();
list.forEach(System.out::println);
sqlSession.close();
}
}
3、多对一关系注解
- 以学生和班级为例
多对一
(1) 实体类
- 班级类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Classes {
private int cid;
private String cname;
}
- 学生类(主体)
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int cid;
private Classes classes;
}
(2)在接口StudentMapper.java中使用注解方法
@Select("select * from student")
@Results({
@Result(property = "sid",column = "sid"),
@Result(property = "sname",column = "sname"),
@Result(property="cid",column = "cid"),
@Result(property = "classes",javaType = Classes.class,column = "cid",
one = @One(select = "com.etime.mapper.ClassesMapper.getClassesByCid"))
})
List<Student> getAllStudentAndClasses();
(3)在接口ClassesMapper.java中使用注解方法
@Select("select * from classes where cid=#{cid}")
Classes getClassesByCid(int cid);
(4)测试
@Test
public void test05() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.getAllStudentAndClasses();
list.forEach(System.out::println);
sqlSession.close();
}
一对多
(1) 实体类
- 学生类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int cid;
}
- 班级类(主体)
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Classes {
private int cid;
private String cname;
private List<Student> studentList;
}
(2)在接口ClassesMapper.java中使用注解方法
package com.etime.mapper;
import com.etime.pojo.Classes;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ClassesMapper {
@Select("select * from classes where cid=#{cid}")
Classes getClassesByCid(int cid);
@Select("select * from classes")
@Results({
@Result(property = "cid",column = "cid"),
@Result(property = "cname",column = "cname"),
@Result(property = "studentList",javaType = List.class,column = "cid",
many = @Many(select = "com.etime.mapper.StudentMapper.getStudentByCid"))
})
List<Classes> getAllClassesAndStudent();
}
(3)在接口StudentMapper.java中使用注解方法
@Select("select * from student where cid=#{cid}")
List<Student> getStudentByCid(int cid);
(4)测试
package com.etime.test;
import com.etime.mapper.ClassesMapper;
import com.etime.pojo.Classes;
import com.etime.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class ClassesTest {
@Test
public void test01(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
ClassesMapper mapper = sqlSession.getMapper(ClassesMapper.class);
List<Classes> list = mapper.getAllClassesAndStudent();
list.forEach(System.out::println);
sqlSession.close();
}
}
4、多对多关系注解
(1)实体类
-
以 学生、课程、中间表为例
-
课程类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Course {
private int courseid;
private String coursename;
}
- 中间表类(第二主体)
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class StudentCourse {
private int scid;
private int sid;
private int courseid;
private Course course;
}
- 学生类(第一主体)
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int cid;
private List<StudentCourse> studentCourseList;
}
(2)在接口 StudentMapper.java中 编写使用注解方法
@Select("select * from student")
@Results({
@Result(property = "sid",column = "sid"),
@Result(property = "sname",column = "sname"),
@Result(property = "cid",column = "cid"),
@Result(property = "studentCourseList",javaType = List.class,column = "sid",
many = @Many(select = "com.etime.mapper.StudentCourseMapper.getStudentCourseBySid"))
})
List<Student> getStudentAndCourse();
(3)在接口 StudentCourseMapper.java中 编写使用注解方法
package com.etime.mapper;
import com.etime.pojo.Course;
import com.etime.pojo.StudentCourse;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface StudentCourseMapper {
@Select("select * from studentcourse where sid=#{sid}")
@Results({
@Result(property = "scid",column = "scid"),
@Result(property = "sid",column = "sid"),
@Result(property = "courseid",column = "courseid"),
@Result(property = "course",javaType = Course.class,column = "sid",
one = @One(select = "com.etime.mapper.CourseMapper.getCourseByCid"))
})
List<StudentCourse> getStudentCourseBySid(int sid);
}
(4)在接口 CourseMapper.java中 编写使用注解方法
package com.etime.mapper;
import com.etime.pojo.Course;
import org.apache.ibatis.annotations.Select;
public interface CourseMapper {
@Select("select * from course where cid=#{cid}")
Course getCourseByCid(int cid);
}
(5)测试
@Test
public void test06() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.getAllStudentAndClasses();
list.forEach(System.out::println);
sqlSession.close();
}
(4)在接口 CourseMapper.java中 编写使用注解方法
package com.etime.mapper;
import com.etime.pojo.Course;
import org.apache.ibatis.annotations.Select;
public interface CourseMapper {
@Select("select * from course where cid=#{cid}")
Course getCourseByCid(int cid);
}
(5)测试
@Test
public void test06() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.getAllStudentAndClasses();
list.forEach(System.out::println);
sqlSession.close();
}