1.简介

1.1什么是MyBatis

在这里插入图片描述

  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。

  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。

  • 2013年11月迁移到Github

获取路径

  • maven仓库:

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    
    
  • github:https://github.com/mybatis/mybatis-3/releases

  • 中文文档:https://mybatis.org/mybatis-3/zh/index.html

1.2持久化

数据持久化

  • 持久化就是讲程序在持久状态和瞬时状态转化的过程
  • 方式:数据库(jdbc),io文件持久化

为什么要持久化

  • 内存:断电即失

1.3Mybatis优点

  • 传统的jdbc太复杂了,简化框架、自动化。

  • 简单易学

  • 灵活

  • 解除sql与程序代码的耦合

  • 提供映射标签,支持对象与数据库的orm字段关系映射

  • 提供对象关系映射标签,支持对象关系组建维护

  • 提供xml标签,支持编写动态sql。

2.第一个MyBatis程序

思路:搭建环境—>导入MyBatis–>编写代码—>测试

2.1配置环境

  • 创建数据库

  • 导入父工程依赖

  • <dependencies>
    <!--    mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.17</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    
  • 创建子工程

  • 编写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>
    <!--    几套环境-->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatistest?userSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value=""/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper resource="org/mybatis/example/BlogMapper.xml"/>
        </mappers>
    </configuration>
    
  • 编写mybatis工具类

    package com.kai.util;
    
    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;
    
    /**
     * @createTime 2021/1/29 18:01
     * @projectName mybatis-study
     * @className MybatisUtils.java
     * @description 工具类
     */
    public class MybatisUtils {
    
        private static SqlSessionFactory sqlSessionFactory;
    //    1.获取sqlSessionFactory
        static {
            String resource = "mybatis-config.xml";
            try {
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    //    既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    //    SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
    //    你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    

2.2编写代码

  • 实体类

    package com.kai.pojo;
    
    /**
     * @createTime 2021/1/29 18:10
     * @projectName mybatis-study
     * @className User.java
     * @description TODO
     */
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User() {
        }
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", pwd='" + pwd + '\'' +
                    '}';
        }
    }
    
  • Dao接口

    package com.kai.dao;
    
    import com.kai.pojo.User;
    
    import java.util.List;
    
    /**
     * @createTime 2021/1/29 18:18
     * @projectName mybatis-study
     * @className UserDao.java
     * @description TODO
     */
    public interface UserDao {
        List<User> getUsers();
    }
    
  • 接口实现类 变为配置文件

    <?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.kai.dao.UserDao">
        <select id="getUsers" resultType="com.kai.pojo.User">
        select * from mybatistest.user
      </select>
    </mapper>
    

2.3测试

package com.kai.dao;

import com.kai.pojo.User;
import com.kai.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
 * @createTime 2021/1/29 18:43
 * @projectName mybatis-study
 * @className userDaoTest.java
 * @description TODO
 */
public class userDaoTest {

    @Test
    public void test(){
//        1.获取sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
//        2.执行sql  方式一:getmapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User> users = mapper.getUsers();
//      遍历
        for(User user:users){
            System.out.println(user);
        }
//        关闭
        sqlSession.close();
    }
//    获取sqlsession
}

注意

  1. 资源过滤问题
    在这里插入图片描述

2.配置文件没有注册

3.方法名不对

4.返回类型不对

3.CRUD

3.1通用基本步骤

  1. 编写mapper接口方法
  2. 在配置文件中注册方法写出sql语句
  3. 测试
    • 获取SqlSession对象来执行sql语句
    • 获取mapper的实例化对象
    • 用对象执行具体方法
    • 增删改 需要提交事务!!
    • 关闭sql

3.2 select

  1. 编写mapper接口方法

    package com.kai.dao;
    
    import com.kai.pojo.User;
    
    import java.util.List;
    
    /**
     * @createTime 2021/1/29 18:18
     * @projectName mybatis-study
     * @className UserDao.java
     * @description TODO
     */
    public interface UserMapper {
        List<User> getUsers();
    //  根据id查询
        User getUserById(int id);
    //  添加
        int addUser(User user);
    //  更新
        int updateUser(User user);
    //   删除
        int deleteUserById(int id);
    }
    
  2. 在配置文件中注册方法写出sql语句

    <select id="getUsers" resultType="com.kai.pojo.User">
    select * from mybatistest.user
    </select>
    <select id="getUserById" resultType="com.kai.pojo.User" parameterType="int">
        select * from mybatistest.user where id = #{id}
    </select>
    
  3. 测试

        @Test
        public void testGetUserById() {
    //        获取
            SqlSession sqlSession = MybatisUtils.getSqlSession();
    //        执行sql
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getUserById(1);
    //        输出结果
            System.out.println(user);
    //        关闭
            sqlSession.close();
        }
    

3.3 delete

  1. 编写mapper接口方法

  2. 在配置文件中注册方法写出sql语句

    <delete id="deleteUserById" parameterType="int">
        delete from mybatistest.user where id = #{id}
    </delete>
    
  3. 测试

        public void deleteUserById() {
    //        获取
            SqlSession sqlSession = MybatisUtils.getSqlSession();
    //        执行sql
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int result = mapper.deleteUserById(5);
    //        输出结果
            if(result>0){
                sqlSession.commit();
                System.out.println("删除成功");
            }
    
    //        关闭
            sqlSession.close();
        }
    }
    

3.4 update

  1. 编写mapper接口方法

  2. 在配置文件中注册方法写出sql语句

        <update id="updateUser" parameterType="com.kai.pojo.User">
            update mybatistest.user set name=#{name},pwd=#{pwd}  where id =#{id};
        </update>
    
  3. 测试

    public void updateUser() {
    //        获取
            SqlSession sqlSession = MybatisUtils.getSqlSession();
    //        执行sql
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int result = mapper.updateUser(new User(5,"嘿嘿嘿","123456"));
    //        输出结果
            if(result>0){
                sqlSession.commit();
                System.out.println("修改成功");
            }
    
    //        关闭
            sqlSession.close();
        }
    

3.5 insert

  1. 编写mapper接口方法

  2. 在配置文件中注册方法写出sql语句

    <insert id="addUser" parameterType="com.kai.pojo.User">
        insert into mybatistest.user (id,name,pwd) value (#{id},#{name},#{pwd})
    </insert>
    
  3. 测试

   public void addUser() {
//        获取
        SqlSession sqlSession = MybatisUtils.getSqlSession();
//        执行sql
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int result = mapper.addUser(new User(5,"哈哈哈","123456"));
//        输出结果
        if(result>0){
            sqlSession.commit();
            System.out.println("插入成功");
        }

//        关闭
        sqlSession.close();
    }

3.6万能Map

这是在项目中常常用到的方式

加入一个实体类有很多的属性,只有部分属性需要修改,可以使用万能map的方式。

  1. 编写mapper接口方法

    User getUserByName(Map<String,Object> map);
    
  2. 在配置文件中注册方法写出sql语句

    <select id="getUserByName" resultType="com.kai.pojo.User" parameterType="map">
        select * from mybatistest.user where name = #{userName} and id = #{id}
    </select>
    
  3. 测试

        public void getUserByName(){
            //        获取
            SqlSession sqlSession = MybatisUtils.getSqlSession();
    //        执行sql
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //      输出结果
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("id","1");
            map.put("userName","张三");
    
            User user = mapper.getUserByName(map);
            System.out.println(user);
    //        关闭
            sqlSession.close();
    
        }
    

3.7模糊查询

  • 在sql语句中拼出

  • 在java代码中使用通配符

    select * from mybatistest.user where name like%#{name}“%”
    

4.配置

4.1核心配置文件

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--核心配置文件-->
<configuration>
<!--    几套环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatistest?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
<!--    每个mapper都需要在配置文件中注册-->
    <mappers>
        <mapper resource="com/kai/dao/UserMapper.xml"/>
    </mappers>
</configuration>

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

4.2配置

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置。

尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

每个数据库对应一个 SqlSessionFactory 实例

默认使用的环境 ID(比如:default="development")。
每个 environment 元素定义的环境 ID(比如:id="development")。
事务管理器的配置(比如:type="JDBC")。
数据源的配置(比如:type="POOLED")。

事务管理器

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。

数据源

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

4.3属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【如果有相同属性,优先级:外部配置文件>peoperties子元素】

  1. 新建.properties文件

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatistest?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT
    username=root
    password=
    
  2. 在核心配置文件中引入.properties文件**【注意配置文件的标签是有顺序的】**

    <properties resource="org/mybatis/example/config.properties">
      <property name="username" value="dev_user"/>
      <property name="password" value="F2Fa3!33TYyg"/>
    </properties>
    
  3. 在核心配置文件中 替换需要动态配置的属性值。比如:

    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
    

4.4类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。

  • 意在降低冗余的全限定类名书写。

    两种方式

    ​ 1.为每一个类设置别名

    <typeAliases>
      <typeAlias alias="Author" type="domain.blog.Author"/>
    </typeAliases>
    

    ​ 2.也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean

    【每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值

    <typeAliases>
      <package name="domain.blog"/>
    </typeAliases>
    

常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

4.5Mapper映射

方式一:resource

<!--    每个mapper都需要在配置文件中注册-->
    <mappers>
        <mapper resource="com/kai/dao/UserMapper.xml"/>
    </mappers>

方式二:class

<mappers>
    <mapper class="com.kai.dao.UserMapper"/>
</mappers>

方式三:packege

<mappers>
    <package name="com.kai.dao"/>
</mappers>

【注意方式二三 的应用有条件】:

  • mapper文件和对应的接口同名

  • mapper文件和对应的接口同包

  • 在这里插入图片描述

4.6生命周期和作用域

理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder
  • 一旦创建了 SqlSessionFactory,就不再需要它了。
  • SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory
  • 可以想象程:连接池

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

  • SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession
  • 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。在Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。
  • 每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。
  • 在这里插入图片描述

4.7ResultMap结果集

  • 可以解决 数据库表与实体类类字段名称不一致
<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultType="User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>

​ 如果这个世界总是这么简单就好了。

  • 复杂关系中可以用到

----------------------------分割线--------------------------------------------

由于中间是一些日志、分页类的内容,所以先去除了。

7.注解方式

7.1测试实现

主要原理是反射 ,底层用到了动态代理

public interface UserMapper {

    @Select("select * from user")
    List<User> getUsers();

}

测试使用

public void TestUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    List<User> users = mapper.getUsers();
    for(User user :users){
        System.out.println(user);
    }

    sqlSession.close();
}

7.2CRUD

1.编写接口

根据id查询用户 多个参数时,基本类型(加上string)的每个参数都需要加@param 引用类型不需要

package com.kai.dao;

import com.kai.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;

/**
 * @createTime 2021/1/29 18:18
 * @projectName mybatis-study
 * @className UserDao.java
 * @description TODO
 */
public interface UserMapper {

    @Select("select * from user")
    List<User> getUsers();

//    根据id查询用户 多个参数时,每个参数都需要加@param 引用类型不需要
    @Select("select * from user where id = #{id}")
    List<User>  getUserById(@Param("id") int id);

//    增加
    @Insert("insert into user(id,name,pwd) value (#{id},#{name},#{pwd})")
    int addUser(User user);
//    删除
    @Delete("delete from user where id = #{id}")
    int deleteUser(@Param("id") int id);
//    更改
    @Update("update user set name=#{name},pwd=#{pwd} where id = #{id}")
    int updateUser(User user);
}

8.复杂查询

8.1基础概念

关联:多对一

集合:一对多

环境搭建

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

8.2多对一处理

方式一:按照查询嵌套处理(嵌套查询)

<mapper namespace="com.kai.dao.StudentMapper">

    <!--select s.id,s.name,t.name from student s,teacher t where s.tid = t.id;-->
    <!--    查询所有学生的详细信息 (子查询)-->
    <!-- 1.查询所有学生的信息-->
    <!-- 2.根据学生信息的tid 查到老师-->
    <!--   结果集映射 -->
    <select id="getStudentInfo" resultMap="StudentTeacher">
        select * from student
    </select>
    
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
<!--        复杂的属性需要单独处理 对象:association 集合:collection-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacherById"/>
    </resultMap>
    <select id="getTeacherById" resultType="Teacher">
        select * from teacher where id = #{id}
    </select>
</mapper>

方式二:按照结果嵌套处理(连表查询)

<select id="getStudentInfo2" 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"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>

8.3一对多处理

方式一:按照结果嵌套处理

<mapper namespace="com.kai.dao.TeacherMapper">

    <select id="getTeacherById" resultMap="TeacherStudent">
        select t.id tid,t.name tname,s.id sid,s.name sname
        from teacher t, student s
        where t.id=s.tid and t.id = #{tid}
    </select>
    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--        复杂的属性需要单独处理 对象:association 集合: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>

9.动态SQL

9.1概念

什么是动态SQL:根据不同的条件生成不同的SQL语句

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

9.2搭建环境

  • 数据库表
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. 导包

  2. 编写配置文件

  3. 创建实体类

    public class Blog {
        private String id;
        private String title;
        private String author;
        private int views;
        private Date createTime;//和数据库命名不一致
    }
    
  4. 编写实体类对应mapper和mapper.xml

  5. 测试

9.3自动转换为驼峰命名

mybatis 设置的一项属性 mapUnderscoreToCamelCase = true

9.4 IF标签

<select id="queryBlogs" parameterType="map" resultType="blog">
    select * from blog
    <where>//没有元素符合 会自动删除where,第二个符合 去掉and
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
    </where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

9.5 choose (when, otherwise)

类似于java中的 swich case语句

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG 
  <where>
  	<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>
  </where>
</select>

9.6 foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。

它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

9.7 SQL片段

为了减少代码的重复,增强复用性,将重复的代码提取出来

  • 将公共部分提取出来

    <sql id="if-title-author">
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
    
  • 使用的时候用标签

    <select id="queryBlogs" parameterType="map" resultType="blog">
        select * from blog
        <where>
            <include refid="if-title-author"/>
        </where>
    </select>
    
    

注意:

1.不要包括where

2.最好基于单表

10.缓存

10.1简介

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

10.2一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

一级缓存失效的四种情况

1、sqlSession不同

2、sqlSession相同,查询条件不同

3、sqlSession相同,两次查询之间执行了增删改操作!

4、sqlSession相同,手动清除一级缓存

一级缓存就是一个map

10.3二级缓存

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

使用步骤

1、开启全局缓存 【mybatis-config.xml】

<setting name="cacheEnabled" value="true"/>

2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】

<cache/>

官方示例=====>查看官方文档
<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

3、代码测试

  • 所有的实体类先实现序列化接口
  • 测试代码
@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();

   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 =session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   session.close();

   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session2.close();
}

结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐