Mybatis学习笔记

Mybatis

Mybatis继承了JDBC,代替传统DAO模式开发

jar包+API+源码----->开源文件

  • 环境:
    1. JDK1.8
    2. Mysql 5.7
    3. maven 3.6.1
    4. IDEA

DAO层的缺点

  1. 架构太麻烦,逻辑太繁杂
  2. 使用JDBC连接数据库没有办法实现java代码和SQL代码之间的解耦(耦合度太高,SQL语句卸载class中)
  3. 使用JDBC连接数据库在接受查询的数据的时候非常费劲,特别影响开发效率
  4. JDBC连接数据库的效率比较低,我们需要自己使用连接池连接

一、Mybatis简介

​ Mybatis是一个Apache的一共开源项目iBatis,是internet和abatis的组合,是一个基于Java的持久层框架

是一个半自动化的ORM框架,其本质是对JDBC的封装,封装过后只需要写Mysql代码而不需要写JDBC代码。ORM(Object-Relational Mapping),对象关系映射。

Mybatis较于Hibernate的优势
  1. Mybatis直接手写SQL语句,可以更加精确定义SQL,更加灵活,也便于性能优化。
  2. 学习成本比Hibernate低不少
  3. 相对轻量级,封装少、映射多样化、支持存储过程、可以进行SQL语句优化等特点,符合互联网高并发、大数据、高性能、高相应的要求。
Mybatis的jar包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCMCtxDp-1637676602882)(C:\Users\86135\AppData\Roaming\Typora\typora-user-images\image-20211016154547648.png)]

  1. asm和javassist是Java字节码解析助手,主要帮助解析字节码文件
  2. cglib包的作用是动态代理
  3. commons-logging是组件和子文件
  4. log4j和slf4g包都是日志
  5. ognl包ognl表达式的包
  6. 最核心的内容在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:		//省去&amp;
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

  1. 在pom.xml中添加maven
  2. 先写MybatisUtils工具类
  3. 写mybatis-config.xml配置文件,配置Mybatis,环境准备
  4. 写实体类User.java
  5. 写接口UserMapper.java
  6. 写接口的UserMapper.xml
  7. 写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

  1. 导入log4j的包

    <dependency>
    	<groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 配置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
    
  3. 配置log4j为日志的实现

    <settings>
    	<setting name="logImpl" value="LOG4J"/>
    </settings>
    
  4. log4j的使用

    1. 在要使用Log4j的类中,导入包import org.apache.log4j.Logger;

    2. 日志对象,参数为当前类的class

      static Logger logger = Logger.getLogger(UserDaoTest.class);
      
    3. 日志级别

      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 使用注解开发
  • 优势:开发比较简洁。
  • 缺点:如果是比较复杂的逻辑,就不好实现了。
  1. 注解在接口上实现

    @Select("select * from user")
    List<User> getUsers();
    
  2. 需要在核心配置文件中绑定接口

    <mappers>
    	<mapper class="com.tunan.dao.UserMapper"/>
    </mappers>
    
  3. 测试

本质:反射机制实现。

底层:动态代理。

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

  • 可以通过注解的形式自动生成构造器
  • 不支持构造器重载,但是可以手动再加
  • 会降低代码阅读量

使用步骤:

  1. 在IDEA中安装Lombok插件

  2. 在项目中导入Lombok的jar包

  3. @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>
小结:
  1. 关联-association
  2. 集合-collection
  3. javaType & ofType
    1. javaType 用来指定实体类中属性的类型
    2. 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

创建一个基础工程

  1. 导包(MybatisUtils)
  2. 编写一个配置文件
  3. 编写实体类
  4. 编写实体类对应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 简介
查询 : 连接数据库,高资源
	一次查询的结果,给他暂存在一个可以直接取到的地方  ----> 内存:缓存
	
在查询相同数据的时候,直接走缓存,就不用走数据库了
  1. 什么是缓存[Cache]?
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据
12.2 Mybatis缓存
  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也成为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
    • 为了提高拓展性,MyBatis定义了缓存接口Cache。我们可以通过Cache接口来自定义二级缓存
12.3 一级缓存
  • 一级缓存也叫本地缓存
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
    • 默认开启,只在一次SqlSession中有效,也就是拿到连接和关闭连接这个区间段

缓存失效的情况:

  1. 查询不同的东西
  2. 增删改操作,可能会改变原来的数据
  3. 查询不同的Mapper.xml
  4. 手动清理缓存
12.4 二级缓存
  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了。会话关闭后,缓存就存在了二级缓存。
    • 新的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中

步骤:

  1. 开启全局缓存

    <!-- 方式一:显式开启全局缓存 -->
    <setting name="cacheEnabled" value="true"/>
    
  2. 在要使用二级缓存的mapper中开启

    <cache/>
    
    <!--  可以自定义参数  -->
    <cache eviction="FIFO" flushInterval="1000" size="0" readOnly="true"/>
    
  3. 测试

    1. 问题:需要将实体类序列化,否则会报错

      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做缓存

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图南zzz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值