Mybatis
Mybatis继承了JDBC,代替传统DAO模式开发
jar包+API+源码----->开源文件
- 环境:
- JDK1.8
- Mysql 5.7
- maven 3.6.1
- IDEA
DAO层的缺点
- 架构太麻烦,逻辑太繁杂
- 使用JDBC连接数据库没有办法实现java代码和SQL代码之间的解耦(耦合度太高,SQL语句卸载class中)
- 使用JDBC连接数据库在接受查询的数据的时候非常费劲,特别影响开发效率
- JDBC连接数据库的效率比较低,我们需要自己使用连接池连接
一、Mybatis简介
Mybatis是一个Apache的一共开源项目iBatis,是internet和abatis的组合,是一个基于Java的持久层框架
是一个半自动化的ORM框架,其本质是对JDBC的封装,封装过后只需要写Mysql代码而不需要写JDBC代码。ORM(Object-Relational Mapping),对象关系映射。
Mybatis较于Hibernate的优势
- Mybatis直接手写SQL语句,可以更加精确定义SQL,更加灵活,也便于性能优化。
- 学习成本比Hibernate低不少
- 相对轻量级,封装少、映射多样化、支持存储过程、可以进行SQL语句优化等特点,符合互联网高并发、大数据、高性能、高相应的要求。
Mybatis的jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCMCtxDp-1637676602882)(C:\Users\86135\AppData\Roaming\Typora\typora-user-images\image-20211016154547648.png)]
- asm和javassist是Java字节码解析助手,主要帮助解析字节码文件
- cglib包的作用是动态代理
- commons-logging是组件和子文件
- log4j和slf4g包都是日志
- ognl包ognl表达式的包
- 最核心的内容在mybatis的jar包中
二、Mybatis的配置
去Mybatis官网搜索,去Mybatis的git社区下载Mybatis文件,将jar包拷贝到项目的lib文件夹下,在src中创建xml文件配置Mybatis。
<?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>
<!--配置mybatis中数据库连接环境-->
<environments default="mysql">
<environment id="mysql">
<!--配置mybatis中事务和JDBC中事务保持一致-->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接数据库的四个元素,底层采用的是数据库连接池方式-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///travel"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
1.环境配置(environments)
MyBatis可以配置成适应多种环境
不过:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境
Mybatis默认的事务管理器就是JDBC,连接池:POOLED(用于不让事务回收)
2.属性(properties)
我们可以通过properties属性来实现引用配置文件
这些属性都是可外部配置且可动态替换的,既可以在典型的Java属性文件中配置,也可以通过properties元素的子元素来传递【db.propertis】
编写一个db.properties配置文件
driver:
url: //省去&
username:
password:
在xml中引入properties文件之后,在environment中的value就可以中properties文件中的值用**${}**来引用
如果引入的文件和xml中的properties中自己输入的配置冲突,优先使用外部文件的配置
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="pwd" value="123456"/>
</properties>
3.类型别名(typeAliases)
<typeAliases>
<typeAlias type="com.tunan.pojo.User" alias="User"/>
<package name="com.tunan.pojo" />
</typeAliases>
<!-- 优化之后xml中就可以用别名代替原来的文本 -->
- 扫描实体类的包,它的默认别名就为这个类的类型,首字母小写
- 在实体类比较少的时候可以直接给类起别名,也就是第一种
- 在实体类比较多的时候,用第二种
- 第一种可以DIY,第二种不行
- 也可以用注解
4.设置(settings)
- 这是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行时行为
- 数量有限,是可以直接罗列完的,在spring中封装后就不用这个了
5.其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins插件
- mybatis-generator-core
- mybatis-plus
- 通用mapper
6.映射器(mappers)
MapperRegistry:注册绑定Mapper文件
方式一:【推荐使用resource】
<!-- 每一个Mapper.XML都需要在Mybatis核心配置文件中注册 -->
<mappers>
<mapper resource="com/tunan/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<!-- 每一个Mapper.XML都需要在Mybatis核心配置文件中注册 -->
<mappers>
<mapper class="com.tunan.dao.UserMapper"/>
</mappers>
注意点:
- 接口和Mapper配置文件必须同名
- 接口和Mapper配置文件必须在同一个包下
方式三:使用扫描包进行注入绑定
<!-- 每一个Mapper.XML都需要在Mybatis核心配置文件中注册 -->
<mappers>
<package name="com.tunan.dao"/>
</mappers>
注意点:
- 接口和Mapper配置文件必须同名
- 接口和Mapper配置文件必须在同一个包下
7.生命周期和作用域
生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactory,就不再需要它了
- 局部变量
SqlSessionFactory:
- 等价于数据库连接池
- 一旦创建就一直存在
- SqlSessionFactory最佳作用域就是应用作用域
- 最简单的就是使用单例模式和多例模式
SqlSession:
- 连接到连接池的一个请求!
- SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域是请求或方法作用域。
- 用完之后就要马上关闭,否则资源被占用。
每一个Mapper就代表一个具体的业务。
三、CRUD
- 在pom.xml中添加maven
- 先写MybatisUtils工具类
- 写mybatis-config.xml配置文件,配置Mybatis,环境准备
- 写实体类User.java
- 写接口UserMapper.java
- 写接口的UserMapper.xml
- 写Test测试
在Java代码中
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession(); //获得实例化sql接口
UserMapper mapper = sqlSession.getMapper(UserMapper.class); //创建mapper对象
mapper.deleteUser(4);
sqlSession.commit(); //提交事务
sqlSession.close(); //关闭sql接口
}
1.namespace
- 在UserMapper.XML中namespace的包名要和Dao/Mapper接口的包名一致
2.select
选择,查询语句
- id:就是对应namespace中的方法名
- resultType:sql语句执行的返回值(Class、int、varchar)
- parameterType:参数类型
<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
select * from mybatis.user where id = #{id}
</select>
3.insert
插入语句
增删改需要提交事务
- id:就是对应namespace中的方法名
- parameterType:参数类型
<insert id="getUserById" parameterType="com.kuang.pojo.User">
insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
//对象中的属性可以直接取出来
需要在Test中提交事务:
sqlSession.commit();
4.update
<update id="updateUser" parameterType="com.kuang.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} = where id = #{id}
</update>
//对象中的属性可以直接取出来
5.delete
<update id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id}
</update>
//对象中的属性可以直接取出来
注意点:增删改需要提交事务
6.Map
假如,实体类或者数据库中的表,字段或者参数过多,应当考虑使用map
//接口函数
int addUser2(Map<String,Object> map);
<!-- XML设置
不需要包含一个对象或者一个表中的所有参数,比较灵活
-->
<insert id="addUser" parameterType="map">
insert into mybatis.user (id,pwd) values (#{userid},#{password})
</insert>
@Test
public void addUser2(){
SqlSession sqlSessin = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("password","2222333");
mapper.addUser2(map);
sqlSession.close();
}
//接口函数
User getUserById2(Map<String,Object> map);
<!-- XML设置
不需要包含一个对象或者一个表中的所有参数,比较灵活
-->
<select id="getUserById2" parameterType="map" resultType="com.kuang.pojo.User">
select * from mybatis.user where id = #{helloId} and name = #{name}
</select>
@Test
public void getUserById2(){
SqlSession sqlSessin = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("helloId",1);
mapper.getUserById2(map);
sqlSession.close();
}
Map传递参数,直接在sql取出key即可 【parameterType=“map”】
对象传递参数,直接在sql中取对象属性即可 【parameterType=“Object”】
只有一个基本类型参数的情况下,可以直接在sql中取到
多个参数用Map,或者注解!
7.模糊查询
//接口类
List<User> getUserLike(String value);
<select id="getUserLike" resultType="com.kuang.pojo.User">
select * from mybatis.user where name like #{value}
</select>
@Test
public void getUserLike(){
SqlSession sqlSessin = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserLike("%李%");
for(User user:userList){
System.out.println(user);
}
sqlSession.close();
}
四、解决属性名和字段名不一致的问题
1.直接在sql语句select中起别名
select id,name,pwd as password from mybatis.user where id = #{id}
2.resultMap
结果集映射
id name pwd
id name password
<mapper namespace="com.tunan.dao.UserMapper">
<resultMap id="UserMap" type="User">
<!-- column数据库中的字段,property实体类中的属性 -->
<!-- <result column="id" property="id"/>
<result column="name" property="name"/> -->
<result column="pwd" property="password"/>
</resultMap>
</mapper>
五、日志
5.1日志工厂
如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手
曾经:sout、debug
现在:日志工厂
- SLF4J
- LOG4J【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握】
- NO_LOGGING
在设置中设置Mybatis具体使用哪一个日志
COMMONS_LOGGING(标准日志输出)
<settings>
<setting name="logImpl" value-"STDOUT_LOGGING"/>
</settings>
log4j.properties
-
导入log4j的包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
配置log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 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=【%c】-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
配置log4j为日志的实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
log4j的使用
-
在要使用Log4j的类中,导入包import org.apache.log4j.Logger;
-
日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserDaoTest.class);
-
日志级别
logger.info("info:进入了testLog4j"); logger.debug("debug:进入了testLog4j"); logger.error("error:进入了testLog4j");
-
public static UserDaoTest{
static Logger logger = Logger.getLogger(UserDaoTest.class);
@Test
public void testLog4j(){
logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");
}
}
//运行完之后会在根目录下生成一个log文件夹,生成log文件
//去网上找文档,可以修改日志的输出格式
六、分页
使用Limit分页
语法:SELECT * from user limit startIndex,pageSize;
SELECT * from user limit 3; #[0,3]
SELECT * from user limit 2,1; #[2,3]
使用Mybatis实现分页,核心Mysql
<select id="getUserByLimit" parameterType="map" resultType="user">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
sqlSession.getMapper(UserMaper.class);
HashMap<String,Integer> map = new HashMap<String,Integer>();
map.put("startIndex",0);
map.put("pageSize",0);
List<User> userList = mapper.getUserByLimit(map);
for(User user : userList){
System.out.println(user);
}
sqlSession.close();
}
七、使用注解开发
7.1 面向接口编程
- 根本原因:解耦,可拓展,提高服用,分层开发中,上层不用管具体实现,大家都遵守相同的标准,使得开发变得容易,规范性好
关于面向接口的理解
- 定义(规范,约束)与实现的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解
- 接口有两种类型
- 第一类是一个个体的抽象,对应抽象类
- 第二类是对一个个体某一方面的抽象,形成一个抽象面
三个面向区别
- 面向对象是指,考虑问题的时候以对象为单位,考虑它的属性及方法。
- 面向过程是指,考虑问题的时候,以具体的流程(事务过程)为单位,考虑它的实现。
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构。
7.2 使用注解开发
- 优势:开发比较简洁。
- 缺点:如果是比较复杂的逻辑,就不好实现了。
-
注解在接口上实现
@Select("select * from user") List<User> getUsers();
-
需要在核心配置文件中绑定接口
<mappers> <mapper class="com.tunan.dao.UserMapper"/> </mappers>
-
测试
本质:反射机制实现。
底层:动态代理。
7.3 CRUD
可以在工具类中自动提交事务。在MybatisUtils里面设置
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
编写接口,增加注解
@Select("Select & from user where id = #{id}")
User getUserByID(@Param("id") int id,@Param("name") String name);
//方法存在多个参数的时候,所有的参数前面必须加上@Param注解
//Param里面类似于取别名,基本类型都要加上
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password}")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
编写测试类
【注意:必须将接口注册绑定到注册文件(mybatis-config.xml)中】
八、Lombok
- 可以通过注解的形式自动生成构造器
- 不支持构造器重载,但是可以手动再加
- 会降低代码阅读量
使用步骤:
-
在IDEA中安装Lombok插件
-
在项目中导入Lombok的jar包
-
@Getter and @Setter @FiledNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor,@RequiredArgsConstructor and @NoArgsConstructor //有参无参构造 @Log,@Log4j,@Log4j2,@slf4j,@xslf4j,@CommonsLog,@JBossLog,@Flogger @Data // 无参构造,get,set,tostring,hashcode,equals @Builder @Singular @Delegate @Value @Accessors @Wither @SneakyThrows
九、多对一处理
1.实体类(pojo目录下):
@Data
public class Student{
private int id;
private String name;
//学生需要一个老师
private Teacher teacher;
}
@Data
public class Teacher(){
private int id;
private String name;
}
2.接口(dao目录下):
public interface StudentMapper{
//查询所有的学生信息,以及对应的老师的信息
public List<Student> getStudent();
}
public interface TeacherMapper{
}
3.在配置目录下
<?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.tunan.dao.StudentMapper">
<!-- 方法一:按照查询嵌套处理 -->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<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="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>
<!-- 方法二:按照结果嵌套处理 -->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname
from student s,teacher t
where s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<reuslt property="name" column="sname"/>
<assciation property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</assciation>
</resultMap>
</mapper>
<?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.tunan.dao.TeacherMapper">
</mapper>
mybatis-config.xml配置
<mappers>
<mapper resource="com.tunan.dao.TeacherMapper"/>
<mapper resource="com.tunan.dao.StudentMapper"/>
</mappers>
十、一对多
比如:一个老师拥有多个学生,对于老师来说就是一对多的关系。
1.实体类(pojo目录下):
@Data
public class Student{
private int id;
private String name;
//学生需要一个老师
private int tid;
}
@Data
public class Teacher(){
private int id;
private String name;
//一个老师可以拥有多个学生
private List<Student> students;
}
2.接口(dao目录下):
public interface StudentMapper{
}
public interface TeacherMapper{
//获取老师
//public List<Teacher> getTeacher();
//获取指定老师下的所有学生及老师的信息
Teacher getTeacher(@Param("tid")int id);
}
3.在配置目录下
<?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.tunan.dao.StudentMapper">
</mapper>
<?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.tunan.dao.TeacherMapper">
<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid = t.id and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- 集合用collection
javaType="" 指定属性的类型,集合中的泛型信息,我们使用ofType获取
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
</mapper>
mybatis-config.xml配置
<mappers>
<mapper resource="com.tunan.dao.TeacherMapper"/>
<mapper resource="com.tunan.dao.StudentMapper"/>
</mappers>
小结:
- 关联-association
- 集合-collection
- javaType & ofType
- javaType 用来指定实体类中属性的类型
- ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
注意点:
- SQL的可读性。
- 注意一对多和多对一,属性名和字段的问题。
- 问题不好排查的时候,可以使用日志
慢SQL 1s 1000s
面试高频
- Mysql引擎
- InnoDB底层原理
- 索引
- 索引优化
十一、动态SQL
动态SQL就是指根据不同的条件生成不同的SQL语句
if
choose(when,otherwise)
trim(where,set)
foreach
搭建环境
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
创建一个基础工程
- 导包(MybatisUtils)
- 编写一个配置文件
- 编写实体类
- 编写实体类对应Mapper接口和Mapper.XML文件
编写实体类
public class Blog{
private int id;
private String title;
private String author;
private Date date;
private int views;
}
编写实体类对应Mapper接口和Mapper.XML文件
public interface BlogMapper{
int addBlog(Blog blog);
}
<mappers>
<mapper class="com.tunan.dao.BlogMapper"/>
</mappers>
<insert id="addBlog" parameterType="blog">
insert into mybatis.blog(id,title,author,create_time,views)
values(#{id},#{title},#{author},#{create_time},,#{views})
</insert>
MyTest.java
public class MyTest{
@Test
public void addInitBlog(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDutils.getId());
blog.setTitle("Title");
blog.setAuthor("TuNan");
blog.serCreateTime(new Date());
blog.setViews(999);
mapper.addBlog(blog);
blog.setId(IDutils.getId());
blog.setTitle("Title2");
blog.setViews(666);
mapper.addBlog(blog);
session.close();
}
}
if
//接口
//查询博客
List<Blog> queryBlogIF(Mapper mapper);e
<select id="queryBlogIF" parameterTye="map">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
choose(when,otherwise)
<select id="queryBlogChoose" parameType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
<!-- 全都不执行的时候才会执行otherwise,如果第一行不成立,第一个and会自动去掉,满足一个条件就结束-->
trim(where,set)
<select id="queryBlogIF" parameterTye="map">
select * from mybatis.blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
<!-- set元素会动态前置SET关键字,同时也会删掉无关的逗号(一般是最后一个) -->
所谓的动态SQL,本质还是SQL语句,只是可以在SQL层面, 取执行一个逻辑代码
Foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 设置开头,结尾,分隔符,类似于v-for变量集合中的item,用分隔符隔开 -->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="(" close=")" seperator="or">
id = #{id}
</foreach>
</where>
</select>
SQL片段
有点时候,可能需要将一些片段抽取出来,方便复用
<sql id="sqlFragment">
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</sql>
<select id="queryBlogIF" parameterTye="map">
select * from mybatis.blog
<where>
<include refid="sqlFragment"/>
</where>
</select>
<!-- 定义一个sql标签,然后用include的refid引入SQL语句 -->
注意事项:
- 最好基于单表来定义SQL片段
- 不要存在where标签
十二、缓存(了解)
12.1 简介
查询 : 连接数据库,高资源
一次查询的结果,给他暂存在一个可以直接取到的地方 ----> 内存:缓存
在查询相同数据的时候,直接走缓存,就不用走数据库了
- 什么是缓存[Cache]?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
12.2 Mybatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大提升查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也成为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高拓展性,MyBatis定义了缓存接口Cache。我们可以通过Cache接口来自定义二级缓存
12.3 一级缓存
- 一级缓存也叫本地缓存
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
- 默认开启,只在一次SqlSession中有效,也就是拿到连接和关闭连接这个区间段
缓存失效的情况:
- 查询不同的东西
- 增删改操作,可能会改变原来的数据
- 查询不同的Mapper.xml
- 手动清理缓存
12.4 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了。会话关闭后,缓存就存在了二级缓存。
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
步骤:
-
开启全局缓存
<!-- 方式一:显式开启全局缓存 --> <setting name="cacheEnabled" value="true"/>
-
在要使用二级缓存的mapper中开启
<cache/> <!-- 可以自定义参数 --> <cache eviction="FIFO" flushInterval="1000" size="0" readOnly="true"/>
-
测试
-
问题:需要将实体类序列化,否则会报错
Caused by:java.io.NotSerializableException:com.tunan.pojo.User
序列化方法:实体类 implements Serializable
-
小结:
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中。
12.5 总结
- 在没有开启二级缓存的时候默认使用一级缓存
- 一级缓存的作用范围为一个SqlSession,在连接数据库的时候开,关闭数据库的时候关闭
- 二级缓存需要手动开启,每一个Mapper对应一个二级缓存
- 二级缓存的作用范围为一个namespace
- 每个一级缓存关闭或者会话提交后,会将数据提交到二级缓存中
- 同一个namespace共用一个二级缓存
12.6 自定义缓存(ehcache)
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
创建一个eache的xml文件
<?xml version=1.0 encoding=UTF-8 ?>
<ehcache xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:noNamespaceSchemaLocation=http://ehcache.org/ehcache.xsd
updateCheck=false>
<diskStore path=./tmpdir/Tmp_EhCache/>
<defaultCache
eternal=false
maxElementsInMemory=10000
overflowToDisk=false
diskPersistent=false
timeToIdleSeconds=1800
timeToLiveSeconds=259200
memoryStoreEvictionPolicy=LRU/>
<cache
name=cloud_user
eternal=false
maxElementsInMemory=5000
overflowToDisk=false
diskPersistent=false
timeToIdleSeconds=1800
timeToLiveSeconds=1800
memoryStoreEvictionPolicy=LRU/>
</ehcache>
用Redies做缓存