Mybatis学习记录

Mybatis是一个Java持久层框架,简化了JDBC操作,专注于SQL语句编写。本文涵盖Mybatis的介绍、快速入门、CRUD操作、配置优化、结果映射、日志、多对一和一对多关系处理、延迟加载、动态SQL、SQL片段和缓存等内容,详细解析了Mybatis的各个核心特性及其优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自:https://dingjn-coder.github.io/2020/03/16/%E6%A1%86%E6%9E%B6/Mybatis%E4%BB%8B%E7%BB%8D%E5%8F%8A%E4%BD%BF%E7%94%A8/#toc-heading-41

Mybatis介绍

Mybatis是一个java持久层框架,内部封装了JDBC,并且做了很多的优化,开发者只需要关注如何写sql语句,不需要处理注册驱动、获取数据库连接等繁杂的过程,使用ORM思想实现对结果集的封装

ORM:对象关系映射,将数据库表的字段与Java实体类的属性对应起来,就可以操作实体类来实现数据库表,需要数据库表的字段与Java实体类的属性保持一致

Mybatis做了哪些优化:

1.数据库连接优化:帮我们优化了数据库的连接以及释放
2.SQL存放优化:SQL统一存取,之前的sql语句散落在各个Java类,可读性很差
3.SQL参数优化:之前的占位符需要一一对应,有一定的局限性,对于不确定的参数无法好的处理,Mybatis引入了EL表达式
4.SQL结果映射:JDBC对于结果处理是很麻烦的,需要一个个获取值然后再给实体对象赋值,Mybatis使用ORM思想实现了对结果集的封装,省略了一大堆代码
5.SQL语句重复优化:对于重复性的部分sql可以单独提取出来,然后进行引用
6.提供了xml标签,支持动态sql

二.快速入门

1.环境搭建
    1)创建Maven工程并且导入坐标
    2)创建实体类以及dao接口
    3)创建Mybatis主配置文件
    4)创建dao的映射配置文件
    5)开始调用
2.注意事项⚠️:
    1)Mybatis中接口名称和映射文件也叫做Mapper
    2)映射配置文件mapper的namespace必须是dao接口的全限定类名
    3)映射配置文件的操作配置,id必须是dao接口中的方法名
    做到了以上3点我们以后无需再写接口的实现类

1.创建实体类以及dao接口

//实体类 属性和表字段一一对应
public class Student implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
        //省略getter setter toString...
}
//dao接口
public interface IStudentDao {
    /**
     * 查询所有学生信息
     */
    //使用注解
    @Select("select * from student")
    List<Student> findAll();
}

2.创建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>
    <!-- shuju -->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源 连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql://localhost:3306/localtest"></property>
                <property name="username" value="root"></property>
                <property name="password" value="mysql04141015"></property>
            </dataSource>
        </environment>
    </environments>

    <!--指定映射文件的位置,映射文件指的是每个dao对应的独立的配置文件
        如果是注解开发,用class
    -->
    <mappers>
        <!--<mapper resource="mapper/IStudentMapper.xml"></mapper>-->
        <mapper class="com.djn.demo01.dao.IStudentDao"></mapper>
    </mappers>
</configuration>

3.创建dao的映射配置文件

namespace命名空间:这个是至关重要的,一是利用更长的全限定名将不同的语句隔离开来,而是为了实现接口绑定

<?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 namespace="com.djn.demo01.dao.IStudentDao">
    <!-- SQL语句
        id:dao的对应的方法名称
        resultType:sql语句的返回值类型
    -->
    <select id="findAll" resultType="com.djn.demo01.pojo.Student">
        SELECT * FROM student
    </select>
</mapper>

4.开始调用方法

1.通过SqlSessionFactoryBuilder创建SqlSessionFactory工厂
2.通过SqlSessionFactory工厂创建SqlSession对象,sqlsession对象包含了执行sql命令所需的方法
3.执行sql语句
4.处理结果
5.关闭sqlsession(重要!!!)
//加载配置文件
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("config.xml");
//创建sqlsessionfactory工厂
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = sfb.build(inputStream);
//通过工厂创建sqlsession对象
SqlSession sqlSession = factory.openSession();
//通过sqlsession创建dao接口的代理对象
IStudentDao mapper = sqlSession.getMapper(IStudentDao.class);
//调用方法执行sql并返回结果
List<Student> list = mapper.findAll();

//第二种方式:不建议使用,因为看起来不清晰而且还需要担心字符串有没有写错以及类型的转换
//List<Student> list = sqlSession.selectList("com.djn.demo01.dao.IStudentDao.findAll");

for(Student student:list){
    System.out.println(student);
}
//释放资源
sqlSession.close();

三.CRUD

如果底层使用JDBC(在mybatis主配置文件中配置的transactionManager标签的type设为jdbc的话)Mybatis是自动开启事务的,也就是对于DML操作需要手动commit;方式一,sqlSession.commit();方式二:SqlsessionFactory.opensession(true)

Mybatis写sql语句有xml和注解两种方式,注解方式写起来方便,但是对于处理一对多和多对一的关系蛋疼!

属性:

xml中:
  1.parameterType:参数类型
      如果只有一个基本类型参数,可以直接取出,如果有多个参数需要用到实体类或者Map,或者@Param注解
      如果是实体类,直接取出属性即可
      如果是Map类型,直接取出key即可
  2.resultType:返回值类型,增删改不需要
      对应接口的返回值,如果是集合,那么要写集合内容的类型,而不是集合本身的类型
  3.resultMap:结果映射,解决数据库表的字段名与属性名不一致问题
注解中:
    1.@Param:当有多个参数时可以使用该注解,在sql语句中取的是@Param中定义的值

1.使用XML

1.定义接口

/**
 * 查询所有
 */
List<Student> findAll();

/**
 * 根据id查询学生
 */
Student findById(int id);

/**
 * 添加学生
 */
int addStudent(Student student);

/**
 * 更新学生信息
 */
int updateStudent(Student student);

/**
 * 删除学生
 */
int deleteByName(String name);

2.定义Mapper.xml映射文件

<!--
        parameterType:参数类型
        resultType:返回值类型,增删改不需要
-->
<!--根据id查询学生-->
    <select id="findById" parameterType="int" resultType="com.djn.demo02.pojo.Student">
        SELECT * FROM student WHERE id = #{id}
    </select>
<!--添加学生-->
<insert id="addStudent" parameterType="com.djn.demo02.pojo.Student">
    INSERT INTO student VALUES(NULL,#{username},#{birthday},#{sex},#{address})
</insert>

<!--更改学生消息-->
<update id="updateStudent" parameterType="com.djn.demo02.pojo.Student">
    UPDATE student SET username = #{username} , address =#{address} WHERE id = #{id}
</update>

<delete id="deleteByName" parameterType="java.lang.String">
    DELETE FROM student WHERE username = #{username}
</delete>

3.在主配置文件中注册Mapper映射文件

<mappers>
     <mapper resource="mapper/IStudentMapper.xml"></mapper>
 </mappers>

2.使用注解

1.定义接口

/**
 * 查询所有
 */
@Select("SELECT * FROM student")
List<Student> findAll();

/**
 * 根据id查询学生
 */
@Select("SELECT * FROM student WHERE id = #{id}")
Student findById(@Param("id") int id);

/**
 * 根据名字模糊查询
 */
List<Student> findByLikeName(String name);

/**
 * 分页查询
 */
@Select("SELECT * FROM student LIMIT #{startIndex},#{pageSize}")
List<Student> findByPaging(Map<String, Integer> map);

/**
 * 添加学生
 */
@Insert("INSERT INTO student VALUES (NULL,#{username},#{birthday},#{sex},#{address})")
int addStudent(Student student);

2.在主配置文件中注册Mapper映射文件

<mappers>
     <mapper class="com.djn.mapper.IStudentMapper"></mapper>
 </mappers>

Map参数类型

如果数据库表中和实体类中有很多字段,sql参数应该使用Map代替对象,因为如果对象的字段很多,我们只用某几个字段,也需要将这个对象传递过去,使用Map传递,在sql中取出map的key即可。

/**
 * 使用Map更新学生信息
 */
int updateStudent(Map<String,Object> map);
<!--使用更改学生消息-->
<update id="updateStudent" parameterType="map">
      <!--取出map的key-->
    UPDATE student SET username = #{username} WHERE id = #{id}
</update>
 Map<String,Object> map =  new HashMap<>();
 map.put("id",3);
 map.put("username","王老七");
 int res = mapper.updateStudent2(map);

四.配置优化

1.环境配置environments

可以配置多个环境,方便切换

<!-- 配置环境  default:使用哪种环境就定义它的id-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务的类型
                    JDBC:使用了JDBC的提交和回滚
                    MANAGED:这个配置几乎没做什么
            -->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源 连接池
                   type:
                        POOLED:避免了创建新的连接时初始化和运行的时间,而是放在应用启动初始化
                        UNPOOLED:每次请求都会打开和关闭连接,对于对数据库可用性不高的应用可以使用这个
                        JNDI:为了能够在EJB容器中使用,了解即可,因为EJB容器已经不用了
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"></property>
                <property name="url" value="${url}"></property>
                <property name="username" value="${username}"></property>
                <property name="password" value="${password}"></property>
            </dataSource>
        </environment>

        <!--配置oracle的环境-->
        <environment id="oracle">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED"></dataSource>
        </environment>
    </environments>

2.属性配置properties

可以在配置文件中引入外部的配置或者在properties标签中定义配置

eg:
<!--db.propertie-->
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/localtest
username=root
password=mysql04141015

<!--mybatis-config.xml-->
<properties resource="db.properties"></properties>
<dataSource type="POOLED">
    <property name="driver" value="${driver}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${username}"></property>
    <property name="password" value="${password}"></property>
</dataSource>

3.别名配置typeAlias

给实体类起别名,在mapper中parameterType和resultType可以直接写别名,不用写全类名了

<!--
     起别名:
        typeAlias:给单个实体类起别名
            type:实体类全类名
            alias:别名
        package:给整个包下的实体类起别名,默认别名为实体类的小写
            @Alias:可以给该包下的实体类声明注解修改别名
-->
<typeAliases>
    <!--<typeAlias type="com.djn.demo02.pojo.Student" alias="Student"></typeAlias>-->
    <package name="com.djn.demo02.pojo"></package>
</typeAliases>

4.映射器mappers

前面的配置好了以后,需要定义映射SQL语句了,但是要告诉Mybatis去哪里找,也就是指定映射文件

方式一 类路径资源引用(常用):

<mappers>
    <mapper resource="mapper/IStudentMapper.xml"></mapper>
</mappers>

方式二 接口的全限定类名:

<!--
        注意:
            使用这种方式Mapper.xml和接口必须同名!!并且必须在同一个包下!!
-->
<mappers>
    <mapper class="com.djn.dao.IStudentMapper"></mapper>
</mappers>

方式三 扫描包:

<!--
        注意:
            使用这种方式Mapper.xml和接口必须同名!!并且必须在同一个包下!!
-->
<mappers>
    <package name="com.djn.dao"></package>
</mappers>

5.设置settings

Mybatis极为重要的调整配置,会改变Mybatis运行时的行为

<settings>
  <!--是否开启缓存-->
  <setting name="cacheEnabled" value="true"/>
  <!--是否开启延迟加载-->
  <setting name="lazyLoadingEnabled" value="true"/>
  <!--指定实现日志 STDOUT_LOGGING/LOG4J-->
  <setting name="lazyLoadingEnabled" value="LOG4J"/>
</settings>

五.结果映射

如果数据库表的字段名与实体类的属性名不一致,那么就会映射不到,查询到的属性也为null

假设实体类的属性名为username,而数据库表的字段名为user_name,如何映射呢

方式一 给字段起别名:

<!--给user_name起别名为username-->
<select id="findAll" resultType="Student" resultMap="map">
    SELECT id,user_name as username,birthday,sex,address FROM student
</select>

方式二 resultMap:

resultMap是Mybatis最关键的元素,对于简单的语句(字段和属性都对应)无需处理,对于复杂的语句只需要描述它的关系就好了,resultMap做到的就是对于查询出来的结果进行映射

<!--进行映射-->
<resultMap id="map" type="Student">
       <!--
                 column:数据库表的字段名
                 property:实体类的属性名
         -->
     <result column="user_name" property="username"></result>
</resultMap>
<!--resultType和resultMap只能同时存在一个-->
<select id="findAll" resultMap="map">
    SELECT id,user_name,birthday,sex,address FROM student
</select>

六.日志

Log4j:apache的开源项目,可以将sql的执行日志输出到控制台、文件等,并且可以设置日志格式以及级别

1.导入jar包

<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>

2.配置文件log4j.properties

#输出到哪里
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/logger.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 HH:mm:ss}][%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.setting设置日志实现    一般在Mybatis的配置文件中设置!!

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

4.程序中使用

Logger logger = Logger.getLogger(*.class);
logger.info("...");
logger.debug("...");
logger.error("...");

![image-20200317174136282](/Users/dingjn/Library/Application Support/typora-user-images/image-20200317174136282.png)

七.多对一

多个学生有一个老师,那之间的关系就叫做多对一,Mybatis中多对一和一对一是一样的用法

有两种实现方式,reultType和resultMap

1.创建实体类

@Data
public class Student {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
        //老师的信息
    private Teacher teacher;
}
@Data
public class Teacher {
    private Integer id;
    private String name;
}

使用resultType

创建一个实体类,包含所有查询的结果的信息,然后返回
//包含了学生表和老师表的信息
@Data
public class StudentVO {
    private Integer id;
    private String name;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}
<!--查询学生信息以及老师的信息-->
<select id="findAllStudent" resultMap="StudentVO">
    SELECT s.*,t.name as tname
    FROM student s
    INNER JOIN teacher t
    ON s.tid = t.id;
</select>

使用resultMap

使用resultMap也有两种方式,联表查询和子查询

association:当查询结果包含实体类时,用association映射

联表查询

<!--Student结果映射-->
<resultMap id="map" type="Student">
    <result property="id" column="id"></result>
    <result property="username" column="username"></result>
    <result property="birthday" column="birthday"></result>
    <result property="sex" column="sex"></result>
    <result property="address" column="address"></result>
      <!--当映射实体类时需要用association javaType:表示实体全类名-->
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"></result>
    </association>
</resultMap>

<!--查询学生信息以及老师的信息-->
<select id="findAllStudent" resultMap="map">
    SELECT s.*,t.name as tname
    FROM student s
    INNER JOIN teacher t
    ON s.tid = t.id;
</select>

子查询

子查询也就是两个表分开单表查询,先查询学生表拿到老师的id,再查询老师表

<resultMap id="map2" type="Student">
        <result property="id" column="id"></result>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
              <!--
                        association:表示映射结果为实体类
                        column:查询语句根据哪个列查询
                        javaType:实体类的对象
                        select:子查询语句
                -->
        <association property="teacher" column="tid" javaType="Teacher" select="com.djn.demo07.mapper.IStudentMapper.findTeacherById"></association>
</resultMap>

<!--查询学生信息以及老师的信息-->
<select id="findAllStudent" resultMap="map2">
    SELECT * FROM student s
</select>
<!--根据学生信息中的tid查询老师的信息-->
<select id="findTeacherById" parameterType="int" resultType="Teacher">
    SELECT * FROM teacher WHERE id = #{tid}
</select>

八.一对多

一个老师也有多个学生,那之间的关系叫一对多,分为联表查询和子查询,子查询可以效率更高一点并且可以实现延迟加载

关键属性:

collection:当查询的结果是集合时用collection映射

1.创建实体类

@Data
public class Teacher {
    private Integer id;
    private String name;
    private List<Student> studentList;
}
@Data
public class Student {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

联表查询

 <!--Student结果映射-->
    <resultMap id="map" type="Teacher">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <!--当映射集合时需要用association ofType:表示实体全类名-->
        <collection property="studentList" ofType="Student">
            <result property="id" column="id"></result>
            <result property="username" column="username"></result>
            <result property="birthday" column="birthday"></result>
            <result property="sex" column="sex"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

    <!--查询学生信息以及老师的信息-->
    <select id="findAllTeacher" resultMap="map">
        SELECT t.*,s.username,s.birthday,s.sex,s.address
        FROM student s
        INNER JOIN teacher t
        ON s.tid = t.id;
    </select>

子查询

 <!--Student结果映射-->
    <resultMap id="map" type="Teacher">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <!--当映射集合时需要用association ofType:表示实体全类名-->
        <collection property="studentList" ofType="Student">
            <result property="id" column="id"></result>
            <result property="username" column="username"></result>
            <result property="birthday" column="birthday"></result>
            <result property="sex" column="sex"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

    <!--查询学生信息以及老师的信息-->
    <select id="findAllTeacher" resultMap="map">
        SELECT t.*,s.username,s.birthday,s.sex,s.address
        FROM student s
        INNER JOIN teacher t
        ON s.tid = t.id;
    </select>

九.延迟加载

延迟加载:在真正使用数据的时候才发起查询,不用的时候就不查询,也就做按需加载,可以提高性能

立即加载:不管用不用,都会发起查询

用法:其实就是在子查询的基础上开启延迟加载

问题:在一对多种,比如我们有1一个用户,它有100个账户
            在查询用户信息时,需要把账户的信息查询出来吗

            在查询账户信息时,需要将用户的信息查询出来吗
什么时候使用延迟加载?
            当一对多,多对多时,通常使用延迟加载,先查询出主要的信息,对应的其他信息可以在用的时候在查询
什么时候使用立即加载?
            当多对一,一对一时,通常使用立即加载,因为需要知道这个信息是属于谁的

多对一、一对一

首先多对一、一对一时是不需要使用延迟加载的,只能使用子查询的方式,因为子查询是分为两个单表查询,如果使用联合查询无法实现,因为一次都查询出来了

开启延迟加载:

<settings>
          <!--开启延迟加载-->
      <setting name="lazyLoadingEnabled" value="true"></setting>
          <!--为false属性就会按需加载-->
      <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

创建实体类:

@Data
public class Student {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
      //老师的信息
    private Teacher teacher;
}

创建映射文件

<resultMap id="map2" type="Student">
        <result property="id" column="id"></result>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
              <!--
                        association:表示映射结果为实体类
                        column:查询语句根据哪个列查询
                        javaType:实体类的对象
                        select:子查询语句
                -->
        <association property="teacher" column="tid" javaType="Teacher" select="com.djn.demo07.mapper.IStudentMapper.findTeacherById"></association>
</resultMap>

<!--查询学生信息以及老师的信息-->
<select id="findAllStudent" resultMap="map2">
    SELECT * FROM student s
</select>
<!--根据学生信息中的tid查询老师的信息-->
<select id="findTeacherById" parameterType="int" resultType="Teacher">
    SELECT * FROM teacher WHERE id = #{tid}
</select>

测试:

//因为只有当需要的时候才会加载,而我们去遍历集合就是用到了就会加载,注释掉就可以测试
@Test
public void findAllStudent() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    IStudentMapper mapper = sqlSession.getMapper(IStudentMapper.class);
    List<Student> allStudent = mapper.findAllStudent();
    for (Student student : allStudent) {
        System.out.println(student);
    }
}

一对多、多对多

使用子查询的方式,一个老师对应多个学生

开启延迟加载:

<settings>
          <!--开启延迟加载-->
      <setting name="lazyLoadingEnabled" value="true"></setting>
          <!--为false属性就会按需加载-->
      <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

创建实体类:

@Data
public class Teacher {
    private Integer id;
    private String name;
    private List<Student> studentList;
}

创建映射文件


<!--Student结果映射-->
<resultMap id="map" type="Teacher">
    <result property="id" column="id"></result>
    <result property="name" column="name"></result>
    <collection property="studentList" ofType="Student" select="com.djn.demo07.mapper.ITeacherMapper.findById" column="id" ></collection>
</resultMap>

<!--查询学生信息以及老师的信息-->
<select id="findAllTeacher" resultMap="map">
    SELECT * FROM teacher
</select>

<select id="findById" parameterType="int" resultType="Student">
    SELECT username FROM student WHERE tid = #{id}
</select>

测试:

  @Test
    public void findAllTeacher() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        ITeacherMapper mapper = sqlSession.getMapper(ITeacherMapper.class);
        List<Teacher> teacherList = mapper.findAllTeacher();
//        for (Teacher teacher : teacherList) {
//            System.out.println(teacher);
//        }
    }

十.动态SQL

Mybatis提供了xml标签支持动态sql,解决根据不同条件拼接语句的痛苦,本质还是sql语句,只是可以在sql层面,去执行一个逻辑代码

CREATE table blog(
        id INT PRIMARY KEY AUTO_INCREMENT,
        title VARCHAR(32) NOT NULL COMMENT '博客标题',
        author VARCHAR(32) NOT NULL COMMENT '作者',
        content VARCHAR(1000) NOT NULL COMMENT '内容',
        views int COMMENT '浏览量',
        create_time TIMESTAMP COMMENT '创建时间'
);
@Data
public class Blog {
    private Integer id;
    private String title;
    private String author;
    private String content;
    private Integer views;
    private Date createTime;
}
/**
  * 测试if标签
  */
List<Blog> findByIf(Map<String,Object> map)

1.if

if判断条件是否成立,成立则执行语句

<select id="findByIf" parameterType="map" resultType="Blog">
    SELECT *
    FROM blog
    <where>
              <!--如果titile不为null,才进行筛选-->
      <if test="title != null">
          AND  title = #{title}
      </if>
      <!--如果author不为null,才进行筛选-->
      <if test="author != null">
          AND  author = #{author}
      </if>
    </where>
</select>

2.choose (when, otherwise)

choose也是判断条件是否成立,成立则执行语句,与if不同的是它只要满足了第一个条件就会退出,即使后续的条件也满足也不会执行了

<select id="findBychoose" parameterType="map" resultType="Blog">
    SELECT *
    FROM blog
    <where>
        <choose>
              <!--如果title不为null,执行完后就会退出,即使author也不为null,也不会执行-->
            <when test="title !=null ">
                title = #{title}
            </when>
            <when test="author !=null">
                AND autor = #{autor}
            </when>
              <!--如果前面的条件都不满足,就执行该语句-->
            <otherwise>
                views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

3.trim (where, set)

where会动态前置where关键字,并且只会在至少有一个条件满足的情况插入where语句,如果条件的开头为AND、OR,where会将其自动清除

<select id="findByIf" parameterType="map" resultType="Blog">
    SELECT *
    FROM blog
    <where>
              <!--如果titile不为null,才进行筛选-->
      <if test="title != null">
          AND  title = #{title}
      </if>
      <!--如果author不为null,才进行筛选-->
      <if test="author != null">
          AND  author = #{author}
      </if>
    </where>
</select>

set会动态前置set关键字,并且会清除无用的逗号,不然逗号会报错的

<update id="updateByset" parameterType="map">
      UPDATE blog
      <set>
          <!--如果author为null,那么逗号会自动清除-->
          <if test="title !=null">
              title = #{title},
          </if>
          <if test="author !=null">
              author = #{author}
          </if>
      </set>
      where id =#{id}
</update>

4.foreach

foreach对一个集合进行遍历

<!--
            collection:集合的名字
            item:集合item名字
            open:以什么开始
            separator:item之间用什么隔开
            close:以什么关闭
            SELECT *
      FROM blog
            WHERE (id = ? or id = ? or id = ?)
-->
<select id="findByforeach" parameterType="map" resultType="Blog">
        SELECT *
        FROM blog
        <where>
            <foreach collection="ids" item="id" open="and (" separator="or" close=")">
                id = #{id}
            </foreach>
        </where>
</select>

十一.sql片段

对于重复的sql语句,可以用sql片段单独定义出来,然后inclue引用

<!--定义复用sql片段-->
<sql id="if-title-author">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            AND author = #{author}
        </if>
</sql>

<select id="findByIf" parameterType="map" resultType="Blog">
    SELECT *
    FROM blog
    <where>
          <!--引用sql片段-->
        <include refid="if-title-author"></include>
    </where>
</select>

十二.缓存

  • 缓存是指将数据存储在内存中,下次请求直接从内存中拿去数据,减少与数据库的交互,提高效率

  • 对于经常使用且不经常改变的就可以使用缓存

  • Mybatis中缓存分为一级缓存和二级缓存

1.一级缓存

一级缓存指的是对sqlsession对象的缓存,当我们执行查询以后,会将结果存入到sqlsession的区域中,该区域是一个map结构,下次查询同样的数据会先去sqlsession中查看是否有,有的话就直接获取

一级缓存清空的情况:

1.sqlsession.close()
2.sqlsession.clearCache();
3.对数据进行增删改操作也会将缓存清空,因为要保持缓存与数据库一致

2.二级缓存

因为一级缓存的作用域太低,所有有二级缓存

  • 一个namespace对应一个缓存,不同的namespace缓存无法共享
  • 当sqlsession关闭后,会将缓存内容移交给二级缓存

开启缓存:默认是开启的,但是还是要显示的配置一下

<!--是否开启缓存-->
<setting name="cacheEnabled" value="true"/>

使用缓存:在mapper.xml中加上这个,就是这么简单。。

<!--
    该标签表示:
        映射语句文件中的所有 select 语句的结果将会被缓存。
    映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
    缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
    缓存不会定时进行刷新(也就是说,没有刷新间隔)。
    缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
-->
<cache/>    

<!--对于有些经常改变的不想使用缓存,可以给语句设置属性。useCahce="false"-->
<select id = "findAll" useCahce="false"></select>

<!--对于增删改语句会清空缓存,可以禁止它清空,可以给语句设置属性。 flushCache="false"-->
<update id= "update" flushCache="false"></update>

也可以自己配置

<cache
  eviction="FIFO"
  flushInterval="60000" // 每个60秒刷新一次
  size="512"                      // 最多可以存储结果对象或列表的 512 个引用
  readOnly="true"/>     // 只能读

注意:

被缓存的对象需要序列化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值