MyBatis使用详解

框架

框架

  • Framework:框架

    • 可重用的设计:表现为一组抽象构件及构件实例间交互的方法

      • 可被开发者定制的应用骨架、模板
    • 就是一个半成品软件、一组组件

      • 自己完善为成品
    • 安全的、可复用的、不断升级的软件

  • 框架就是一个模板

    • 规定内容格式,有基础的功能可用

    • 添加自己的功能实现

      • 可以通过框架中的基础功能实现
特点
  • 一般不是全能的,不能做所有事
    • 针对某个方面擅长
  • 例如:MyBatis 对数据库操作
    • 但不能进行其他操作

三层架构

结构
  1. 界面层:User Interface Layer

    • 表示层、视图层;主要接受用户数据,显示请求的处理结果
    • 使用 web 界面和用户交互
      • 类似手机 app 就是表现层,用户在 app 中操作
      • 调用数据访问层获取数据
  2. 业务逻辑层:Business Logic Layer

    • 服务层;接收表示层传递过来的数据、检查数据、计算业务逻辑
    • 调用数据访问层获取数据
  3. 数据访问层:Date Access Layer

    • 持久层,与数据库打交道

    • 主要实现对数据的增、删、改、查

    • 将存储在数据库的数据提交给业务层,同时将业务层处理的数据保存到数据库

交互
  • 用户 → 界面层 → 业务逻辑层 → 数据库访问层 → DB 数据库
对应结构包
  • 界面层: controller 包, servlet
  • 业务逻辑层: service 包, XxxService
  • 数据库访问层: dao 包, XxxDao
对应框架
  • SSMSpringMVCSpringMyBatis

    • 界面层 – servlet 类 → SpringMVC 框架

    • 业务逻辑层 – service 类 → Spring 框架

    • 数据访问层 – dao 类 → MyBatis 框架

MyBatis

  • 相当于增强版 JDBC,便捷连接、操作数据库

介绍

  • 最初 apache 的开源项目 iBatis

    • 2010 年项目由 apache software foundation 迁移到 google code 并改名为 MyBatis
    • 2013 年 11 月迁移到 GitHub
  • 基于 Java 的持久层框架:MyBatis SQL Mapper Framework for Java

    • 基于 Java 的 SQL 映射框架
    • 包括 SQL Mapper:SQL 映射
      • 表中一行记录映射为一个 Java 对象
    • Data Access Objects(DAOs):数据访问,操作数据库数据

作用

  1. 减轻使用 JDBC 的复杂性
    1. 不用编写重复的 ConnectionStatement
    2. 不用编写关闭资源代码
  2. 直接使用 Java 对象表示结果数据
    1. 让开发者专注 SQL 的处理
    2. 其他工作由 MyBatis 完成
  3. MyBatis 可完成
    1. 注册数据库驱动

    2. 创建 JDBC 中必须使用的对象

      • ConnectionStatementRsultSet
    3. xml 文件中获取 sql,并执行 sql 语句

      • ResultSet 结果转换为 Java 对象
      • 将对象封装到到 List 集合中
    4. 关闭资源连接

基础实现

pom.xml
  • Maven 的 pom.xml 文件配置

    1. 导入 mybatismysql-connector-java 相关依赖

    2. 配置 resources 资源

    3. 其他配置文件基于 Maven 版本、JDK 版本 和 IDEA 环境配置

    • 不同版本的 JDK 和 Maven 以及高版本编译工具可能不兼容
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>li_maven</groupId>
        <artifactId>demo10</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
        <dependencies>
    
            <!-- mysql-connector-java 数据库连接 Java 驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.25</version>
            </dependency>
            <!-- mybatis 框架依赖 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.9</version>
            </dependency>
            <!-- junit 单元测试依赖 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <!-- 配置此项目的编码格式 和 编译、执行 JDK 版本 -->
        <properties>
            <project.build.sourceEncoding>utf8</project.build.sourceEncoding>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
        </properties>
    
        <build>
            <!-- 此插件更新测试插件版本 -->
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>3.0.0-M3</version>
                    <configuration>
                        <forkCount>0</forkCount>
                        <testFailureIgnore>true</testFailureIgnore>
                    </configuration>
                </plugin>
            </plugins>
            <!-- 配置以下两项以保证 Maven 将 src\mian\ 目录下的 .xml、.properties 配置文件也进行打包 -->
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
    </project>
    
mybatis-config.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">
    <!-- mybatis-3-config.dtd:约束文件名,固定值 -->
    
    <!--Mybatis 核心配置文件:配置了数据源、连接池、映射mapper文件 -->
    <configuration>
    	<settings>
            <!-- 设置 MyBatis 输出日志,将日志打印到控制台输出 -->
            <setting name="logImpl" value="STDOUT_LOGGING" />
        </settings>
        
        <!--
            环境配置:数据库的连接信息
            default 值必须和某个环境id相同,是当前默认使用的数据库
         -->
        <environments default="MySQL">
            
            <!-- environment:一个数据库信息的配置;	id:唯一值,自定义,表示此环境的名称 -->
            <environment id="MySQL">
                
                <!--
                    transactionManager:MyBatis 的事务类型
                    type=JDBC:表示使用 JDBC 中的 Connection 对象的 commit、rollback进行事务处理
                -->
                <transactionManager type="JDBC"/>
                
                <!--
                    dataSource:数据源,连接数据库的配置;k-v 格式配置
                    type:表示使用连接池
                    name 值固定不能更改,value 是当前对应的数据库连接参数
                -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
                
            </environment>
        </environments>
    
        <!-- 注册mapper文件到mybatis-config.xml中-->
        <mappers>
            <!-- 一个 Mapper 对应一个映射文件,resource 值为编译后 Mapper.xml 文件的全路径 -->
            <mapper resource="mapper/DemoMapper.xml" />
        </mappers>
    
    </configuration>
    
Mapper
Demo 类
  • 对应数据库表数据的实体类
  • 一个对象对应表中的一条记录
package empty;
import java.util.Date;
public class Demo {
    // 属性名与数据表的字段名对应,根据属性名匹配对应的 set 方法
    private int id;
    private String name;
    private int age;
    private Date date;

    // 必须保留无参构造可以让 MyBatis 框架将数据自动封装到实例对象中
    public Demo(){}
    public Demo(int id, String name, int age, Date date){
        this.id = id;
        this.name = name;
        this.age = age;
        this.date = date;
    }
    //get、set、toString 方法 略
}
DemoDao 接口
  • 定义要执行的方法
  • 之后使用动态代理无需自己创建实现类
package mapper;
import empty.Demo;
import java.util.List;
public interface DemoDao {
    public List<Demo> getAll();
    public int add();
}
DemoMapper.xml
  • sql 映射文件
  • 写实现方法的 sql 语句
<?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">
        <!--
            上面代码指定约束文件
            mybatis-3-mapper.dtd:约束文件名,拓展名 dtd
            约束文件作用:限制、检查当前文件中出现的标签、属性名必须符合 MyBatis 要求
        -->

<!-- namespace:命名空间,自定义唯一值;常规使用 dao 接口全限定名 -->
<mapper namespace="mapper.DemoDao">
   
    <!--
        select、insert、update、delete 标签对应相应的数据库操作
        id:执行的 sql 语法的唯一标识,MyBatis 根据 id 值找到要执行的 sql 语句
            使用接口中方法名;可自定义,但不能重复;默认通过方法名自动匹配对应 id
    -->
    
    <!-- select 语句,id 是接口方法名 getAll -->
    <!-- resultType:查询结果返回类型,将查询到的记录自动封装到该类的实例对象,并放到集合中 -->
    <select id="getAll" resultType="empty.Demo">
        select * from demo;
    </select>

    <!-- insert 语句,id 是接口方法名 add -->
    <!-- 返回受影响行数,无需指定返回类型 -->
    <!-- #{} 表示引用 Demo 的属性值进行赋值,执行 sql 语句时传入 Demo 类对象  -->
    <insert id="add">
        insert into demo values(#{id}, #{name}, #{age}, #{date});
    </insert>

</mapper>
测试类
package testMapper;

import empty.Demo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class TestDemoDao {
    private static InputStream in;

    static {
        //访问 mybatis 配置文件读取数据,传入配置文件全路径
        try {
            in = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testGetAll() {
        //得到 sql 语句执行对象,链式编程
        SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
        //指定执行的 sql 语句的标识,包括 Mapper 文件的命名空间 和 sql 标签 id
        String sqlId = "mapper.DemoDao.getAll";
        //执行 sql 语句,根据 sqlId 找到对应的 sql 语句
        List<Demo> Demo = session.selectList(sqlId);
        //遍历输出查询结果
        for (empty.Demo demo : Demo) {
            System.out.println(demo);
        }
        //关闭资源
        session.close();
    }

    @Test
    public void testAddOne() {
        //得到 sql 语句执行对象,链式编程
        SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
        String sqlId = "mapper.DemoDao.addOne";
        //自动查找并执行 insert 语句,返回受影响行数
        int rows = session.insert(sqlId,new Demo(100, "张三", 18, new Date()));
        if (rows > 0) {
            System.out.println("执行成功,受影响行数: " + rows);
        }
        //MyBatis 默认开启事务,需要手动提交后才会添加到数据库
        session.commit();
        //关闭资源
        session.close();
    }
}

主要类

Resources
  • 通过 getResourceAsAtream() 方法读取资源文件

    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

  • MyBatis 中的类

SqlSessionFactoryBuilder
  • 通过 build() 方法得到工厂类对象

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().bulid(in)

    • 传入读取的资源文件
  • 创建 SqlSessionFactory 之后就不再需要

SqlSessionFactory
  • 重量级对象

    • 创建对象耗时较长,消耗资源较多
    • 整个项目中一个即可
  • 是一个接口

    • 实现类:DefaultSqlSessionFactory
      • 默认使用此实现类方法
  • 作用:获取 SqlSession 对象

    • SqlSession Session = factory.openSession();
  • openSession() 方法说明

    1. 无参数,获取非自动提交事务的 SqlSession 对象
    2. openSession(boolean):默认 false
    • 参数为 true:获取自动提交事务的 SqlSession 对象
    • 参数为 false:获取非自动提交事务的 SqlSession 对象
SqlSession
  • 是一个接口

    • 定义了操作数据的方法
      • 例如:selectOne()selectList()insert()update()delete()commit()rollback()
    • 创建接口代理对象:使用 dao 接口的 Class 对象
      • getMapper(Dao.class)
  • 接口实现类:DefaultSqlSession

    • 实现了接口中的操作方法
    • 默认使用此实现类方法
  • 使用要求

    • SqlSession 对象不是线程安全的,需要在方法内部使用
      • 执行 sql 语句之前使用 openSession() 方法获取对象
      • 执行 sql 语句之后进行关闭:sqlSession.close()
        • 保证是线程安全使用

动态代理

条件
  • 接口和 Mapper.xml 文件在同一个目录下
  • 接口和 Mapper 文件名统一
  • Mapper.xml 文件命名空间使用 Dao 接口全限定名
  • Mapper.xml 文件中 sql 语句 id 使用接口方法名
    • 接口中不要使用重载方法
  • 反射机制通过类全限定名方法名找到对应实体类和方法
使用
  • <T> T getMapper(Class<T> var1)

    • 传入 Dao 接口的 Class 对象
    • 框架底层自动完成所有操作得到 接口对象
    • 使用接口对象直接执行方法
  • 例如

    int i = sqlSession.getMapper(DemoDao.class).add();
    /*
    getMapper() 方法得到接口实例的代理对象,通过对象调用方法执行
    框架自动创建接口实现子类并实现抽象方法
    */
    

参数传递

参数
  • 从 Java 代码中将数据传入到 mapper.xml 文件的 sql 语句中

  • parameterTypemapper.xml 文件的一个属性

    • 通过反射可以得到参数类型

      • 非强制使用,一般不使用
    • 表示 dao 接口中方法的参数数据类型

    • 值为 Java 数据类型全限定名或 MyBatis 定义的别名

      • 例如:java.lang.Integer 别名 int
    <select id="getById" parameterType = "java.lang.Integer"  resultType="empty.Demo">
    	select * from demo where id = #{id};
    </select>
    
传参
简单类型
  • 一个参数

    • sql 语句中参数位置使用 #{任意字符}
  • 执行方法时传入参数

  • 原理

    1. 框架底层使用 PreparedStatement 对象预编译 sql
    2. 使用 #{}MyBatis 执行 sql 使用 JDBC 中的 PrepareStatement 对象
    3. 传参时对占位符进行替换

    类似于:

    // 示例执行查询语句,使用 PreparedStatement对象
    PreparedStatement pre = con.prepareStatement(sql);
    pre.setInt(1, 参数);
    // 执行 sql 后将查询结果封装到 resultType 结果类型对象中    
    
命名参数
  • 多参数

    • 多个参数使用 @Param 命名参数
    • 多个参数必须添加 @Param;否则无法检测参数
    //多参数查询方法,使用 @Param 标明参数名
    Demo getOne(@Param("id") int id, @Param("age") int age)
    
    <select id="getOne" resultType="empty.Demo">
    	select * from demo where id = #{id} or age #{name}
    </select>
    <!-- sql 中 #{} 内容必须和 @Param 值对应 -->
    
对象传参
  • 多参数

    • 使用 Java 对象传递参数
  • 对象的属性值就是 sql 语句的参数值

    • 每一个属性对应一个字段
  • 完整语法格式

    • #{property, javaType=Java中数据类型, jdbcType数据库中数据类型}

      • javaTypejdbcType 类型 MyBatis 可以自动检测,一般不需要设置
    • 常用格式:#{property}

// 方法定义,参数类型为对象
List<Demo> get(Demo demo);

// 测试方法
@Test
public void testGet(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    Demo demo = new Demo(100, "二狗", 23, new Date())
    //自动找到 demo 对象中的属性,匹配 sql 语句中的 id、name 传入参数
    List<Demo> demos = sqlSession.getMapper(DemoDao.class).getOne(demo);
    for (Demo demo : demos) {
        System.out.println(demo);
    }
    sqlSession.close();
}
<!-- mapper文件编写 -->
<select id="getAll" resultType="empty.Demo">
    <!-- 占位符中值为对应表中字段的对象属性名,必须一致,否则找不到属性 -->
	select * from demo where id = #{id} or name = #{name}
</select>
索引
  • 参数位置从 0 开始

  • 语法:#{arg0}

    • mybatis 3.3 及之前版本使用 #{0}、#{1}
    • mybatis 3.4 开始使用 #{arg0}、#{arg1} 方式
    • 要定位对应参数坐标
    Demo getByIdName(int id, String name);			// 测试方法
    
    <!-- mapper文件编写,arg0、agr1 对应参数列表中索引位置参数 -->
    <select id="getAll" resultType="empty.Demo">
    	select * from demo where id = #{arg0} or name = #{arg1}
    </select>
    
Map传参
  • 使用 Map 存放参数

    • 占位符使用 key 值
    • 自动找到 Map 中对应的 value 值
    //方法定义
    List<Demo> get(Map<String, Integer> map);
    
    //测试类
    @Test
    public void testGetMap() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //创建 Map 对象 添加数据
        Map<String, Object> map = new HashMap<>();
        map.put("id", 99);
        map.put("name", "张三");
        //传入 Map 对象查询数据
        List<Demo> demos = sqlSession.getMapper(DemoDao.class).getByMap(map);
        for (Demo demo : demos) {
            System.out.println(demo);
        }
        sqlSession.close();
    }    
    
    <!-- mapper文件编写:key 作为占位符 -->
    <select id="getAll" resultType="empty.Demo">
        select * from demo where id = #{id} or name = #{name}
    </select>
    

# 和 $

作用
  • #:占位符

    • 对 MyBatis 表明该位置使用实际参数值代替

      • #{} 等价于 sql 语句的 ?
    • 使用 PreparedStatement 对象执行 sql 语句

      • 更加安全和迅捷,首选做法
      <select id="get" resultType="empty.Demo">
      	select * from demo where id = #{id}
      </select>
      
      1. sql 语句等价于 select * from demo where id = ?
      2. 使用 PreparedStatement 对象编译 sql 语句并传递参数
        • pre.setInt(1, 参数)
        • 参数会找到并替换掉 #{id}
      3. 执行 sql 语句
  • $:字符替换

    • 直接用参数替换 ${} 位置内容

    • 使用 Statement 将 sql 语句和 ${} 的内存连接

      • 有 sql 注入风险
      • 不会自动匹配数据格式
        • 字符串文本需要手动添加 ""
    • 使用 # 占位符会导致部分关键字自动添加 "" 变成字符格式,造成语法错误

      • 主要用在替换表名、列名、不同列排序等操作的关键字拼接
      • 例:desc
      <select id="get" resultType="empty.Demo">
          select * from demo where name = '?{name}'
      </select>
      <!-- 
      等价于 select * from demo where id = '参数';
      直接进行参数替换,使用 Statement 对象拼接字符串
      -->
      
替换列名
List<Demo> orderBy(String colName);				// 定义方法
<select id="orderBy" resultType="empty.Demo">
    select * from demo order by ${colName}			<!-- 必须使用 ?,否则排序关键字会自动拼接引号,造成语法错误 -->
</select>
区别
  • # 使用 ? 在代码中做占位符
    • 使用 PreparedStatement 对象执行 sql,效率高
    • 能避免 sql 注入,更安全
  • $ 不使用占位符,字符串拼接形式传入参数
    • 使用 Statement 对象执行 sql,效率较低
    • 存在 sql 注入的风险
      • 但是对于关键字输入适用
      • PrepareStatement 传入参数自动匹配格式
        • 会将字符自动添加引号,无法正常传入关键字

resultType

定义
  • 执行完后返回的结果类型

    • DQL 语句执行必须添加 resultType
  • DQL 语句执行完成后查询到的数据转为的 Java 对象

    • 可以是任意的 Java 类型,自定义
  • 处理方式

    1. MyBatis 执行 sql 语句

      • 然后 MyBatis 调用类的无参构造创建对象
    2. 执行 sql 语句得到查询结果集

    3. ResultSet 中的字段值封装到对象的同名属性中

      • 找不到同名属性的字段自动忽略

      • 找不到同名字段的属性默认 null 值

        • 即 实体类属性名和表中字段一致
    4. 返回执行结果为封装后的对象

      • 多条结果则将对象封装到 List 集合中
别名
  • resultType 值:最好使用全限定名

    • 绝对路径查找更准确
  • 对于 Java 系统类可使用全限定名或使用 MyBatis 定义的别名

    • 例如:java.lang.Integer 别名 int
  • 对于自定义 Java 类一般使用全限定名

    • 也可以自定义别名使用
  • 自定义别名,在 MyBatis 配置文件中定义

    • 使用 <typeAlias> 标签定义
      • 自定义单个类别名
    • 使用 <package name=""/> 定义
      • name 赋值要取别名的包全限定名称
        • 此包中所有类名就是别名,类名不区分大小写
      • 不建议使用,多个包中有同名类时会产生冲突,导致异常
    <!-- 此标签下定义所有类型别名 -->
    <typeAliases>
        <!-- 一个标签定义一个别名 -->
        <typeAlias type="empty.Demo" alias= "Demo"/>
        <!-- empty 包中所有类类名就是别名 -->
        <package name = "empty">
    </typeAliases>
    
返回 Map
  • 查询结果以 Map 返回

    • Mapkey 为字段名,value 是属性值
    • 只能查询单行记录
      • 查询多行记录可将 Map 封装到 List
    // 方法定义
    Map<Object, Object> getById(int id);
    
    // 测试方法
    @Test
    public void testGetByID() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        Map<Object, Object> demo = sqlSession.getMapper(DemoDao.class).getByID(123);
        for (Object o : demo.keySet()) {
            System.out.println(o  + " = " + demo.get(o));
        }
    }
    
    <!-- mapper.xml 文件 -->
    <select id="getByID" resultType="map">
        select * from demo where id = #{id};
    </select>
    

ResultMap

  • 结果映射

  • 指定列名和 Java 对象的属性间的对应关系

    1. 自定义字段和属性名的映射关系
      • 只需要映射命名不相同的字段
      • 数据库字段和Java实体类中属性命名相同时自动映射
    2. 字段名和属性名不相同时
      • 使用 ResultMap 指定映射关系
      • 指定字段别名和属性名相同
  • ResultMap 中未指定的字段、属性映射会自动映射

    • 优先使用已经指定的映射
    • 未指定的映射会自动查找装配
  • resultType 不要同时使用

    <!-- 数据库字段和Java对象属性间的映射关系 -->
    <resultMap id="Demo" type="empty.Demo">
        <!-- 主键字段,使用 id column -->
        <id column="id" property="id"/>
        <!-- 普通字段映射 -->
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="date" property="date"/>
    </resultMap>
    
    <!-- 使用映射关系,不再写 resultType -->
    <select id="orderBy" resultMap="Demo">
        select * from demo where name = ${name}
    </select>
    

模糊查询

  • 两种方式

    1. 方法参数中写模糊条件
      • 模糊条件比较灵活
    2. sql 语句中拼接模糊条件
    • List<Demo> getByName1(String name);			// 方法定义,在方法参数中拼接模糊条件
      List<Demo> getByName2(String name);			// 方法定义,在sql语句中拼接模糊条件
      
    • <!-- mapper.xml文件中 sql 语句 -->
      <select id="getByName1" resultType = "empty.Demo">
          <!-- 在参数中写模糊条件 -->
        select * from demo where name like #{name}
      </select>
      
      <select id="getByName2" resultType = "empty.Demo">
         <!-- 直接拼接 -->
         select * from demo where name like '%'#{name}'%'
      </select>
      
    • // 测试方法
      @Test
      public void testGetByName() {
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          Demo demo = sqlSession.getMapper(DemoDao.class)
          // 参数中写模糊条件:拼接模糊查询符号 %
      	List<Demo> demos1 = demo.getByName1("%三%");
          // 参数传入到模糊条件
          List<Demo> demos2 = demo..getByName("三");
          for (Demo demo : demos) {
              System.out.println(demo);
          }
          sqlSession.close();
      }
      

注解开发

  • 在 dao 接口方法上添加注解执行对应语句

    • @Select@Insert@Delete@Update
  • 使用注解也可以同时使用 mapper.xml 文件

    • 同一个方法只能选择 注解 或 xml 使用
      • 注解默认以方法名为 id,会造成重名冲突

动态 SQL

  • 动态SQL:sql 内容是变化的

    • 根据不同条件获取到不同的 sql 语句
      • 主要是 where 字句的变化
  • 动态SQL实现使用 MyBatis 提供的标签

    • <if>、<where>、<foreach>
<if>
  • <if>:判断条件
    • 在方法定义中使用对象传参
  • 注意
    1. 单独使用 <if> 需要在 where 后先添加条件

      • 条件随意,不影响查询结果即可
        • 避免 if 条件不成立时可能引起的语法错误
    2. where 标签搭配使用自动进行关键字替换

 <select id="getAllByIf" resultMap="Demo">
     select * from demo
     <!-- where 子句中追加一个条件避免 if 条件不成立造成语法错误,此条件不对查询结果造成影响即可 -->
     where name is not null 
     <!-- test 进行属性条件判断,满足条件才执行包含的语句 -->
     <if test="name != null and name != ''">
         and name like '%' #{name} '%'
     </if>
     <if test="id != 0">
         and id > #{id}
     </if>
     <if test="age != 0">
         or age > #{age}
     </if>
</select>
<!--
实际执行:select * from demo where name is not null and name like '%' ? '%' and id > ? or age > ?
传递参数:二(String), 2(Integer), 15(Integer)
-->
<where>
  • 用来包含 <if> 标签
    • 对于 <if> 使用进行完善
      • 方法中同样需要对象传参
    • 按照语法格式将满足条件的 <if> 内容追加到主 sql 语句
  • 当所有 <if> 中有条件成立
    1. <where> 标签自动添加 where 关键字
      • sql 语句中不再声明 where
    2. 替换 <if> 中多余的关键字 andor 等,避免语法错误
      • 检测到满足条件的首个 if 标签
        • 此段 sql 子句句首存在关键字则替换为 where
        • 没有关键字直接添加 where 关键字
  • 所有 if 条件都不满足则进行全表查询
    • 等价于 sql 语句中不含 where 子句
<select id="getAllByIf" resultMap="Demo">
    select * from demo
    <!-- sql 语句中不再声明 where -->
    <!-- where 标签中所有 if 标签都不满足条件进行全表查询-->
    <where>
        <if test="id != 0">
            <!-- 满足条件时自动添加 where -->
            id > #{id}
        </if>
        <if test="age != 0">
            <!-- 上一个 if 不满足条件但此条满足时 or 替换为 where -->
            or age > #{age}
        </if>
    </where>
</select>
<!--
实际执行:select * from demo WHERE name like '%' ? '%' and id > ? or age > ?
传入参数:二(String), 2(Integer), 15(Integer)
-->
<foreach>
  • 循环标签

    • 实现对数组与集合的遍历
    • 主要用在 in 子句中
      • inallany 等关键字语句
  • <foreach collection = "list" item = "i" open = "(" close = ")"  separator = ",">
        #{i}
    </foreach>
    <!-- 等价于 (#{i}), (#{i}) ... ; 遍历 list 集合中所有元素-->
    
    • collection :遍历的集合类型
      • 数组:array
      • List 集合:list
    • open:开始的字符
    • cloae:结束的字符
    • item:集合中的成员
      • 自定义表示数组或集合成员的变量
    • separator:集合成员间的分隔符
    • 所有字符拼接使用属性都可以手动在 sql 语句中进行拼接
遍历 List<>
  • 存放普通数据
    • 使用 in 判断在满足条件的数据
  • 集合中封装对象
    • 通过封装的对象属性进行数据判断
    • 使用 insert into demo values(),()...; 方式可同时进行多条记录插入
// 方法定义,传入 List 集合
int addList(List<Demo> list);
// 方法测试
@Test
public void testAddList() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    ArrayList<Demo> list = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        list.add(new Demo(i + 101, "废物", 22, new Date()));							// 准备数据
    }
    int rows = sqlSession.getMapper(DemoDao.class).addList(list);					 // 执行 sql,参数为 list 集合
    sqlSession.commit();
    if (rows == 1){
        System.out.println("执行成功,受影响行数 " + rows);
    }
    sqlSession.close();
}
<insert id="addList">
    insert into demo values
    <!-- 遍历传入的 List 集合 -->
    <foreach collection="list" item="demo" separator=",">
        <!-- demo 属于 List 集合中成员,是一个对象,调用对象的属性 -->
        (#{demo.id}, #{demo.name}, #{demo.age}, #{demo.date})
    </foreach>
</insert>
<!--
执行:insert into demo values (?, ?, ?, ?) , (?, ?, ?, ?) 
传参:108, 废物, 22, 2022-02-11, 109, 废物, 22, 2022-02-11
-->
&lt;
  • xml 文件中 < 会被编译器认为是标签体,造成编译错误
    • 使用 <![CDATA[ ]]> 使编译器忽略 [] 里内容
  • 或使用 &lt; 表示 <
    • &gt; 表示 >
代码片段
  • 复用语句

    • 自定义一段 sql 语句进行重复使用
  • 使用步骤

    1. 定义

      • 片段中可以是 sql 语句任何部分
      <sql id="自定义唯一名">sql语句</sql>
      
    2. 引用

      <include refid="sql id值"/>
      
    <!-- 定义 sql 片段 -->
    <sql id="select*">
        select * from demo
    </sql>
    <!-- 引用代码片段 -->
    <select id="getAll" resultMap="Demo">
        <include refid="select*"/>
    </select>
    

配置文件

主配置文件
  • 标签的顺序都有要求,不能随意改变顺序
  • 所有标签都在 configuration 标签内,按顺序排列
    • properties:属性配置
    • settings:全局设置
    • typeAliases:类型别名
    • typeHandlers:类型处理器:数据库类型与 Java 类型转换
    • objectFactory:对象工厂
    • objectWrapperFactory:对象加工工厂
    • reflectorFactory:反射工厂
    • plugins:插件
    • environments:环境配置
    • databaseIdProvider:数据源信息
    • mappers:mapper 文件映射
<?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">
<!-- mybatis-3-config.dtd:约束文件名,固定值 -->

<!-- Mybatis 核心配置文件:配置数据源、连接池、映射mapper文件等 -->
<configuration>
    <!-- 全局设置 -->
    <settings>
        <!-- 设置 MyBatis 输出日志,将日志打印到控制台输出 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>

    <!-- 此标签下定义所有类型别名 -->
    <typeAliases>
        <!-- 自定义类别名,一个标签定义一个别名 -->
        <typeAlias type="empty.Demo" alias= "Demo"/>
        <!-- empty 包中所有类类名就是别名 -->
        <package name = "empty"/>
    </typeAliases>

    <!--
        环境配置:数据库的连接信息
        default 值必须和某个环境值相同,是当前默认使用的数据库
     -->
    <environments default="MySQL">

        <!--
            environment:一个数据库信息的配置
            id:唯一值,自定义,表示此环境的名称
         -->
        <environment id="MySQL">

            <!--
                transactionManager:MyBatis 的事务类型
                type=JDBC:表示使用 JDBC 中的 Connection 对象的 commit、rollback进行事务处理
            -->
            <transactionManager type="JDBC"/>

            <!--
                dataSource:数据源,连接数据库的配置;type:表示使用连接池类型
                name 值固定不能更改,value 是对应的数据库连接参数
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>

        </environment>
    </environments>

    <!-- 注册 mapper 文件到 mybatis-config.xml 中-->
    <mappers>
        <!-- 一个 Mapper 对应一个映射文件,resource 值为编译后 Mapper.xml 文件全路径 -->
        <mapper resource="mapper/DemoMapper.xml" />
    </mappers>

</configuration>
<settings>
  • <setting>:MyBatis 中全局的调整设置
    • 会改变 MyBatis 运行时行为,谨慎设置
  • 设置在 MyBatis 主配置文件 <configuration> 标签下
    • 一般使用默认值即可,无需修改
输出日志
  • 日志类别
    • SLF4J:使用 ALF4J日志框架
    • LOG4J:使用 LOG4J框架实现,1.x 版本
    • LOG4J2:使用 LOG4J 框架,2.x 版本
    • JDK_LOGGING:使用 java.util.logging
    • COMMONS_LOGGING:使用 Apache Commons Logging 实现
    • STDOUT_LOGGING:使用 System 类实现,打印到控制台输出
    • NO_LOGGING:不打印日志
<settings>
    <!-- 设置 MyBatis,将日志打印到控制台输出 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
全局缓存
<settings>
    <!-- 影响所有映射器的缓存的全局开关,默认值 true -->
    <setting name="cacheEnabled" value="true" />
</settings>
本地缓存
  • 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询
    • SESSION:缓存一个会话中执行的所有查询
    • STATEMENT:本地会话仅用在语句执行上
      • 对相同 SqlSession 的不同调用将不会共享数据
<settings>
    <!-- 默认值 SESSION,缓存一个会话中执行的所有查询 -->
    <setting name="localCacheScope" value="SESSION"/>
</settings>
延迟加载
  • 延迟加载的全局开关
    • 特定关联关系可通过设置 fetchType 属性覆盖该项全局开关
<settings> 	
	<!-- 开启时所有关联对象都延迟加载,默认 false -->
    <setting name ="lazyLoadingEnabled" value = "fasle"/>
</setting>
多结果集
<settings> 	
	<!-- 允许单一语句返回多结果集,需要兼容驱动,默认值 true -->
    <setting name ="multipleResultSetsEnabled" value = "true"/>
</setting>
列名代替表名
<settings> 	
	<!-- 不同驱动有不同表现,具体参考相关驱动文档或测试观察结果,默认 true -->
    <setting name ="useColumnLable" value = "true"/>
</setting>
自动生成主键
  • 允许 JDBC 支持自动生成主键,需要驱动兼容,部分驱动不兼容但仍可工作,比如 Derby
<settings> 	
	<!-- 
	  设为 true 强制使用自动生成主键,默认值 false -->
    <setting name ="useGeneratedKeys" value = "false"/>
</setting>
指定映射方式
  • 指定 MyBatis 应如何自动映射列到字段或属性
    • NONE:取消自动映射
    • PARTIAL:只会自动映射没有定义嵌套的结果集
    • FULL:自动映射任意复杂结果的结果集
      • 无论是否嵌套
<settings> 	
	<!-- 默认值 PARTIAL-->
    <setting name ="autoMappingBehavior" value = "PARTIAL"/>
</setting>
执行器
  • SIMPLE:普通执行器
  • REUSE:执行器会重用预处理语句
    • PreparedStatement
  • BATCH:重用语句并执行批量更新
<settings> 	
	<!-- 配置默认的执行器,默认 SIMPLE -->
    <setting name ="defaultExecutorType" value = "SIMPLE"/>
</setting>
超时时间
<settings> 	
	<!-- 设置超时时间,决定驱动等待数据库相应的秒数 -->
    <setting name ="defaultStatementTimeout" value = "25"/>
</setting>
允许分页
<settings> 	
	<!-- 允许在嵌套语句中使用分页(RowBounds),默认值 false -->
    <setting name ="safeRowBoundsEnabled" value = "false"/>
</setting>
驼峰命名
  • 从经典数据库列名 A_COLUMN 到 经典 Java 属性名 aColumn 的类似映射
<settings> 	
	<!-- 开启驼峰命名规则,(camel case),默认值 false -->
    <setting name ="mapUnderscoreToCamelCase" value = "true"/>
</setting>
配置别名
  1. 为单独的类配置别名
  2. 直接将一个包配置别名
    • 不同包中有同名类时会发生冲突
    • 通过注解 @Alias 指定类别名解决同名冲突
      • 注解在实体类上,属性值为自定义类别名
<!-- 此标签下定义所有类型别名 -->
<typeAliases>
    <!-- 一个标签定义一个别名 -->
    <typeAlias type="empty.Demo" alias= "Demo"/>
    <!-- empty 包中所有类类名就是别名 -->
    <package name = "empty"/>
	<!-- 通过 @Alias 注解在类上面自定义别名解决重名冲突 -->
</typeAliases>
plugins
  • 插件配置
    • mybatis-generator-core
      • 逆向工程生成实体类
    • mybatis-plus
      • mybatis 功能增强
    • 通用 mapper
PageHelper
  • MyBatis 通用分页插件,支持多种数据库

    • Oracle、MySQL、DB2、SqlServer(2225、2008、2012)
    • MariaDB、SQOLite、Hsqdb、PostgreSQL
    • Informix、H2、Derby、Phoenix
  • 使用流程

    1. 添加 maven 依赖引入 jar 包

      • 或下载导入 jar 包

      • <!-- 导入 Maven 依赖 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.0</version>
        </dependency>
        
    2. MyBatis 主配置文件加入 plugin 配置

      • 加在 <environments> 之前,类似于过滤器
      <plugins>
          <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
      </plugins>
      
    3. 查询之前调用 PageHelper.startPage 静态方法

      • 类似用法的 PageHelper.offsetPage 方法
      • 在此方法后的第一个 MyBatis 查询方法会被分页
      // 测试分页插件
      @Test
      public void testGetAll() {
      	// 在执行sql前调用插件方法进行分页
      	PageHelper.startPage(2, 10);
      	SqlSession sqlSession = MybatisUtils.getSqlSession();
      	Demo demo = sqlSession.getMapper(DemoMapper.class)
           // 执行方法进行查询
      	List<Demo> demos = demo.getAll();
          for (Demo demo : demos) {
              System.out.println(demo);
          }
          sqlSession.close();
      }
      /*
      预查询得到总记录数: SELECT count(0) FROM demo
      执行的 sql:select * from demo LIMIT ?, ?
      传入的参数:10(Long), 10(Integer)
      */
      
<environments>
<environments default="MySQL">      <!-- 指定默认使用的环境 -->
    <environment id="MySQL">		<!-- 配置环境 -->
        <!-- 事务处理 -->
        <transactionManager type="JDBC"/>
        <!-- 配置数据源 -->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>
事务处理
  • <transactionManager type="JDBC"/>

    • transactionManager:事务处理机制

    • type:事务处理的类型

      • JDBC

        • MyBatis 底层调用 JDBC 中 Connection 对象的 commit、rollback 等方法处理事务
      • MANAGED

        • 把 MyBatis 的事物处理委托给其他的容器
          • 服务器软件、框架(Spring) 等
dataSource
  • <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </dataSource>
    
    • dataSource:数据源

      • Java 中 规定实现了 javax.sql.DataSource 接口的都是数据源
        • 接口中抽象方法:getConnection()
      • 用来获取数据库连接对象
        • 设置数据库连接参数
    • type:指定数据源的类型

      • POOLED:使用连接池

        • MyBatis 创建 PooledDataSource 类
      • UNPOOLED:不使用连接池

        • 每次执行 sql 语句先创建连接、执行 sql

          、关闭连接

        • MyBatis 创建 UnpooledDataSource 类

      • JNDI~

        • Java 命名和目录服务
          • 类似 windows 注册表
        • 很古老的方式
属性配置文件
  • 把数据库连接信息放到单独的文件,和 MyBatis 主配置文件分开

    • 便于修改、保存,处理多个数据库的信息
  • 流程

    1. resource 目录中定义属性配置文件

      • 例如:mybatis.properties
    2. 文件中 key 值一般以目录名拼接方便区分所属的环境配置

      • # MySQL 环境的外部属性配置文件
        MySQL.driver=com.mysql.jdbc.Driver
        MySQL.url=jdbc:mysql://loaclhost:3306/demo01
        MySQL.username=root
        MySQL.password=123456
        
    3. 在 MyBatis 主配置文件使用 <property> 标签指定文件位置

      • 需要写值的位置引用:${key}

        • key 值即为外部配置文件中的 key
        <!-- 使用外部配置文件 -->
        <!-- 引用配置文件,从类的根路径开始找 -->
        <properties resource="db.properties"/>
        <environments default="MySQL">
            <environment id="MySQL">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <!-- 引用配置文件中的值 -->
                    <property name="driver" value="${MySQL.driver}"/>
                    <property name="url" value="${MySQL.url}"/>
                    <property name="username" value="${MySQL.username}"/>
                    <property name="password" value="${MySQL.password}"/>
                </dataSource>
            </environment>
        </environments>
        
mapper 映射
  • 两种常用方式

    1. 单独指定类路径:资源定位
      • <mapper resource="mapper/DemoMapper.xml" />
        • 一个 mapper 标签对应一个 mapper.xml 文件
        • / 路径分隔符
    2. 指定同一报下所有文件:包定位
      • <package name="empty">
        • name:mapper.xml 文件所在包名全路径
        • 将包内所有 mapper 映射文件注册为映射器
      • 使用条件
        1. mapper.xml 文件和 dao 接口名称必须一样
          • 区分大小写
        2. mapper.xml 文件和 dao 接口在同一目录下
  • 其他方式

    1. 使用 dao 接口全限定名:类定位
      • <mapper class="dao接口全限定名">
        • dao 接口和 mapper.xml 文件必须在同一个包下
        • dao 接口和 mapper.xml 文件名称必须完全一致
    2. 使用完全限定资源定位符
      • <mapper url="file:mapper.xml文件路径">
<!-- 注册 mapper 文件到 mybatis-config.xml 中-->
<mappers>
    <!-- 一个 Mapper 对应一个映射文件;resource 值为编译后 Mapper.xml 文件全路径 -->
    <mapper resource="mapper/DemoMapper.xml" />
    <!-- mapper 包中所有 mapper.xml 文件都加载给 MyBatis -->
    <package name="mapper">
	<!-- 使用映射器接口实现类的完全限定类名 -->
	<mapper class="org.mybatis.builder.AuthorMapper"/>
	<!-- 使用完全限定资源定位符(URL) -->
	<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>

日志

  • 输出执行日志以查询执行流程

  • <setting name="logImpl" value="日志类型">
    
STDOUT_LOGGING
  • 标准日志输出
  • 依靠 Java 的 System 执行,将 sql 执行流程打印到控制台显示
LOG4J
  • 作用

    • 可以将日志输出到控制台、文件、组件、服务器 等位置
    • 控制日志输出格式
    • 定义日志信息级别,控制日志生成过程
    • 通过配置文件灵活配置,不需要修改应用代码
  • 要求

    • 需要添加 jar 包才能使用

    • 配置 log4j 日志设置

    • 在要使用的类中加载日志对象

      • 参数为当前类 Class 对象

        static Logger log = Logger.getLogger(要使用类的 Class 对象)

在这里插入图片描述

缓存

作用
  • 存在内存中的临时数据
  • 将用户常查询的数据放到缓存中
    • 再次查询时就不用再到磁盘读取
    • 关系型数据库
  • 从缓存中查询效率更高,提高性能
  • 使用缓存可以减少数据库的交互次数
    • 减少系统开销,提高系统效率
  • 经常查询且不经常改变的数据应放在缓存中
MyBatis 缓存
  • 可以方便的定制和配置缓存
  • MyBatis 系统默认定义了两级缓存
    • 一级缓存、二级缓存
    • 默认只开启一级缓存
      • SqlSession 级别,本地缓存
    • 二级缓存需要手动开启和配置
      • 基于 namespace 级别的缓存
  • 为提高拓展性 MyBatis 提供缓存接口 Cache
    • 可以通过实现 Cache 接口自定义二级缓存
一级缓存
  • 当一个新 session 被创建,MyBatis 就会创建一个相关联的本地缓存
    • 任何在 session 执行过的查询结果都会被保存在本地缓存中
  • 当再次执行参数相同的相同查询时,不需要实际查询数据库了
    • 本地缓存将会在做出修改、事务提交或回滚,以及关闭 session 时清空
  • 默认情况下,本地缓存数据的生命周期等同于整个 session 的周期
    • 缓存会被用来解决循环引用问题和加快重复嵌套查询的速度
      • 所以无法将其完全禁用
    • 可以通过设置 localCacheScope=STATEMENT 只在语句执行时使用缓存
  • localCacheScope 被设置为 SESSION
    • 对于某个对象,MyBatis 将返回在本地缓存中唯一对象的引用
    • 对返回的对象(例如 list)做出的任何修改将会影响本地缓存的内容
      • 进而将会影响到在本次 session 中从缓存返回的值
      • 因此,不要对 MyBatis 所返回的对象作出更改,以防后患
二级缓存
开启
  • 启用全局的二级缓存需要在的 SQL 映射文件中添加一行

    • mapper.xml 文件中
  • 缓存只作用于 cache 标签所在的映射文件中的语句

    • 混合使用 Java API 和 XML 映射文件
      • 在共用接口中的语句将不会被默认缓存
      • 需要使用 @CacheNamespaceRef 注解指定缓存作用域
    <cache/>  <!-- 这一句即可开启全局二级缓存 -->
    
  • 语句作用

    • 映射语句文件中的所有 select 语句的结果将会被缓存

    • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存

    • 缓存会使用最近最少使用算法来清除不需要的缓存,(LRU, Least Recently Used)算法

      • LRU :最近最少使用:移除最长时间不被使用的对象
        • 默认使用
      • FIFO :先进先出:按对象进入缓存的顺序来移除它们
      • SOFT :软引用:基于垃圾回收器状态和软引用规则移除对象
      • WEAK :弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象
  • 缓存不会定时进行刷新,即没有刷新间隔

  • 缓存会保存列表或对象的 1024 个引用

    • 无论查询方法返回哪种
  • 缓存会被视为读/写缓存

    • 意味着获取到的对象并不是共享的
    • 可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改
属性
<!-- 自定义二级缓存的属性 -->
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>
  • 配置创建了一个 FIFO 缓存
    1. 每隔 60 秒刷新
    2. 最多可以存储结果对象或列表的 512 个引用
    3. 返回的对象被认为是只读的
      • 对它们进行修改可能会在不同线程中的调用者产生冲突
  • flushInterval:刷新间隔,任意的正整数
    • 值以毫秒为单位的合理时间量
    • 默认不设置,没有刷新间隔,缓存仅仅会在调用语句时刷新
  • size:引用数目,任意正整数
    • 注意欲缓存对象的大小和运行环境中可用的内存资源
    • 默认值是 1024
  • readOnly:只读;true 或 false
    • 只读:给所有调用者返回缓存对象的相同实例
      • 因此这些对象不能被修改;提供了可观的性能提升
    • 可读写:通过序列化返回缓存对象的拷贝
      • 速度上会慢一些,但是更安全,因此默认使用
    • 默认值是 false
自定义缓存
  • 自己的缓存
    • 或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为
  • 二级缓存的配置不能应用于自定义缓存
    • 如清除策略、可读或可读写等
<cache type="com.domain.something.MyCustomCache"/>
<!-- 使用一个自定义的缓存实现
type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器 
-->

完整示例

  • 使用 Eclipse 完成

    • 真难用这玩意儿
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>li_maven</groupId>
  <artifactId>mybatis-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
    
    <dependencies>
        <!-- mysql连接java的驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <!-- mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <!-- pagehelper分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.0</version>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 属性配置 -->
    <properties>
        <project.build.sourceEncoding>utf8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <!-- 更新测试插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M3</version>
                <configuration>
                    <forkCount>0</forkCount>
                    <testFailureIgnore>true</testFailureIgnore>
                </configuration>
            </plugin>
        </plugins>
        <!-- 检索 src/main/resource 目录下所有文件,包括 properties、xml 文件 -->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
 
</project>
mybatis-config.xml
<?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">
<!-- mybatis-3-config.dtd:约束文件名,固定值 -->

<!-- Mybatis 核心配置文件:配置了数据源、连接池、映射mapper文件 -->
<configuration>
    <settings>
        <!-- 设置 MyBatis 输出日志,将日志打印到控制台输出 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>

    <!-- 此标签下定义所有类型别名 -->
    <typeAliases>
   		<!-- 单独定义类别名 -->
   		<!-- <typeAlias type="demo.empty.Demo" alias="Demo"/> -->
    	<!-- 定义包中所有类类名为别名 -->
        <package name = "demo.empty"/>
    </typeAliases>

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
    </plugins>

    <!--
        环境配置:数据库的连接信息
        default 值必须和某个环境值相同,是当前默认使用的数据库
     -->
    <environments default="MySQL">

        <!--
            environment:一个数据库信息的配置
            id:唯一值,自定义,表示此环境的名称
         -->
        <environment id="MySQL">

            <!--
                transactionManager:MyBatis 的事务类型
                type=JDBC:表示使用 JDBC 中的 Connection 对象的 commit、rollback进行事务处理
            -->
            <transactionManager type="JDBC"/>

            <!--
                dataSource:数据源,连接数据库的配置
                type:表示使用连接池
                name 值固定不能更改,value 是当前对应的数据库连接参数
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>

        </environment>
    </environments>

    <!-- 注册mapper文件到mybatis-config.xml中-->
    <mappers>
        <!-- 一个 Mapper 对应一个映射文件,resource 值为编译后 Mapper.xml 文件全路径 -->
        <!-- <package name="demo/mapper/DemoMapper.xml"/> -->
        <!-- 包中所有 xml 文件都加载到 mybatis -->
		<mapper resource="demo/mapper/DemoMapper.xml" />
    </mappers>

</configuration>
empty
Demo
package demo.empty;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

public class Demo {
	private int id;
	private String name;
	private int age;
	private Date date;
	
	public Demo() {}
	public Demo(Date date) {
		this.date = date;
	} 
	public Demo(int id, String name, int age, Date date) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.date = date;
	}
	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 int getAge() {	return age;}
	public void setAge(int age) 		this.age = age	}
	public Date getDate() {		return date;	}
	public void setDate(Date date) {		this.date = date;	}	
	private String changeDate(Date date) {
		return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date);
	}
	@Override
	public int hashCode() {	return Objects.hash(age, date, id, name);}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Demo other = (Demo) obj;
		return age == other.age && Objects.equals(date, other.date) && id == other.id
				&& Objects.equals(name, other.name);
	}

	@Override
	public String toString() {
		return "id = " + id +
				"\t name = " + name +
				"\t age = " + age +
				"\t date = " + changeDate(date);
	}
}
mapper
DemoMapper.java
package demo.mapper;

import java.util.List;
import org.apache.ibatis.annotations.Param;
import demo.empty.Demo;

public interface DemoMapper {

	/**
	 * 获取全部记录
	 * @return 返回记录的集合
	 * 一条记录封装到一个对象
	 */
	List<Demo> getAll();
	
	/**
	 * 分页查询所有记录
	 * @param pageNum 页码
	 * @param pageSice 一页记录数
	 * @return 返回查询结果
	 */
	List<Demo> getAllByPage(int pageNum, int pageSice);
	
	/**
	 * 排序查询
	 * @param colName 要排序的列
	 * @return 返回查询结果
	 */
	List<Demo> getByOrder(String colName);
	
	/**
	 * 通过 id name 精确查找
	 * @param id id
	 * @param name name
	 * @return 返回记录
	 */
	Demo getByIdName(@Param("id") int id, @Param("name") String name);
	
	/**
	 * 模糊查询
	 * @param name1 模糊名1
	 * @param name2 模糊名2
	 * @return 返回满足条件的记录
	 */
	List<Demo> getByLike(@Param("name1") String name1, @Param("name2") String name2);
	
	/**
	 * 使用if、where 完成查询
	 * @param demo 传入对象参数,使用对象属性作为参数
	 * @return 返回查询结果
	 */
	List<Demo> getByIf(Demo demo);
	
	/**
	 * 添加默认记录,时间为添加时的时间
	 * 使用 statement 执行,无参数追加
	 * @return 返回受影响行数
	 */
	int addDefault();
	
	/**
	 * 通过 list 集合添加多条记录
	 * @param unm 添加记录数
	 * @param list 传入封装了Demo 对象的 List 集合
	 * @return 返回受影响行数
	 */
	int addSome(int unm, @Param("list") List<Demo> list);
	
	/**
	 * 自定义添加数据
	 * @param demo 传入 Demo 对象
	 * 使用对象属性传参
	 * @return 返回受影响行数
	 */
	int add(Demo demo);
	
	/**
	 * 修改记录,通过 id 修改
	 * @param demo 传入Demo 对象
	 * 根据 demo 属性内容自动进行参数修改,id不改变
	 * @return 返回受影响行数
	 */
	int update(Demo demo) ;
}

DemoMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    上面代码指定约束文件
    mybatis-3-mapper.dtd:约束文件名,拓展名 dtd
    约束文件作用:限制、检查当前文件中出现的标签、属性名必须符合 MyBatis 要求
-->

<!-- namespace:命名空间,自定义唯一值;常规使用 dao 接口全限定名 -->
<mapper namespace="demo.mapper.DemoMapper">
    <!-- 字段名和属性名的映射关系 -->
    <resultMap id="Demo" type="demo.empty.Demo">
        <!-- 主键列使用 id column -->
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="date" property="date"/>
    </resultMap>

	<!-- sql 语句片段,可复用 -->
    <sql id="select*">
        select * from demo
    </sql>
    
    <select id="getAll" resultMap="Demo">
    	<!-- 复用 sql 片段 -->
        <include refid="select*"/>
    </select>
    
    <select id="getAllByPage" resultMap="Demo">
        <include refid="select*"/>
    </select>
    
    <select id="getByOrder" resultMap="Demo">
        <!-- 根据自定义列名排序查找 -->
        <include refid="select*"/> order by ${colName}
    </select>

	<select id="getByIdName" resultMap="Demo">
        <include refid="select*"/> where id = #{id} and name = #{name}
    </select>
    
    <select id="getByLike" resultMap="Demo">
    	<include refid="select*"/> 
    	where name like '%' #{name1} '%' or name like '%' #{name2} '%'
    </select>
    
    <select id="getByIf" resultMap="Demo">
        <!-- 满足 if 条件时按条件查找,否则全部查找 -->
    	<include refid="select*"/> 
		<where>
			<if test="id != 0">
				id > #{id}
			</if>
			<if test="age != 0">
				and age > #{age}
			</if>
		</where>
    </select>
    
    <insert id="addDefault" statementType="STATEMENT"> 
    	insert into demo(date) values (now());
    </insert>

    <!-- 批量添加默认数据 -->
	<insert id="addSome">
		insert into demo values
		<foreach collection="list" item="demo" separator=",">
			(#{demo.id}, #{demo.name}, #{demo.age}, #{demo.date})
		</foreach>
	</insert>
	
    <!-- 添加记录 -->
	<insert id="add">
		insert into demo values(#{id}, #{name}, #{age}, #{date})
	</insert>
	
    <!-- 根据属性值内容修改记录,根据 id 查找且 id 不修改 -->
	<update id="update">
		update demo set id = #{id}
		<if test="name != null">
			, name = #{name}
		</if>
		<if test="age != 0">
			, age = #{age}
		</if>
		<if test="date != null">
			, date = #{date}
		</if>
		where id = #{id}
	</update>
</mapper>
utils
MybatisUtils
  • 工具类
    • 静态加载 MyBatis 配置文件
    • 提供获取 SqlSession 对象的方法
    • 提供关闭 SqlSession 对象的方法
package utils;

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;

public class MybatisUtils {
    private static SqlSessionFactory factory;
    // 静态代码块读取 mybatis 配置文件,获取工厂对象
    static{
        try {
            InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 获取 SqlSession 对象
    public static SqlSession getSqlSession() {
        SqlSession sqlSession = null;
        if (factory != null) {
            sqlSession =  factory.openSession();
        }
        return sqlSession;
    }

    // 关闭连接资源
    public static void close(SqlSession sqlSession){
        if (sqlSession != null){
            sqlSession.close();
        }
    }
}
test
TestDemoMapper
package testDemoMapper;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.github.pagehelper.PageHelper;
import demo.empty.Demo;
import demo.mapper.DemoMapper;
import demo.utils.MybatisUtil;

public class TestDemoMapper {
	
	@Test
	public void testGetAll() {
		SqlSession sql = MybatisUtil.getSqlSession();
		List<Demo> demos = sql.getMapper(DemoMapper.class).getAll();
		for(Demo demo : demos) {
			System.out.println(demo);
		}
		sql.close();
	}

	@Test
	public void testGetAllByPage() {
		SqlSession sql = MybatisUtil.getSqlSession();
		PageHelper.startPage(2, 10);
		List<Demo> demos = sql.getMapper(DemoMapper.class).getAllByPage(2, 10);
		for(Demo demo : demos) {
			System.out.println(demo);
		}
		sql.close();
	}
	
	@Test
	public void testGetByOrder() {
		SqlSession sql = MybatisUtil.getSqlSession();
		List<Demo> demos = sql.getMapper(DemoMapper.class).getByOrder("age");
		for(Demo demo : demos) {
			System.out.println(demo);
		}
		sql.close();
	}
	
	@Test
	public void testGetByIdName() {
		SqlSession sql = MybatisUtil.getSqlSession();
		Demo demo = sql.getMapper(DemoMapper.class).getByIdName(5, "张三");
		System.out.println(demo);
		sql.close();
	}
	
	@Test
	public void testGetByLike() {
		SqlSession sql = MybatisUtil.getSqlSession();
		List<Demo> demos = sql.getMapper(DemoMapper.class).getByLike("二", "三");
		for(Demo demo : demos) {
			System.out.println(demo);
		}
		sql.close();
	}
	
	@Test
	public void testGetByIf() {
		SqlSession sql = MybatisUtil.getSqlSession();
		Demo demo = new Demo();
		demo.setAge(10);
		demo.setName("张三");
		List<Demo> demos = sql.getMapper(DemoMapper.class).getByIf(demo);
		for(Demo demo1 : demos) {
			System.out.println(demo1);
		}
		sql.close();
	}
	
	@Test
	public void testAddDefault() {
		SqlSession sql = MybatisUtil.getSqlSession();
		int rows = sql.getMapper(DemoMapper.class).addDefault();
		sql.commit();
		System.out.println("受影响行数 " + rows);
		sql.close();
	}
	
	@Test
	public void testAddSome() {
		SqlSession sql = MybatisUtil.getSqlSession();
		List<Demo> list = new ArrayList<>();
		for(int i = 0; i < 5; i++) {
			list.add(new Demo(128 + i, "废狗", 22, new Date()));
		}
		int rows = sql.getMapper(DemoMapper.class).addSome(5, list);
		sql.commit();
		System.out.println("受影响行数 " + rows);
		sql.close();
	}
	
	@Test
	public void testAdd() {
		SqlSession sql = MybatisUtil.getSqlSession();
		int rows = sql.getMapper(DemoMapper.class).add(new Demo(110, "废狗", 23, new Date()));
		sql.commit();
		System.out.println("受影响行数 " + rows);
		sql.close();
	}
	
	@Test
	public void testUpdate() {
		SqlSession sql = MybatisUtil.getSqlSession();
		Demo demo = new Demo();
		demo.setId(100);
		demo.setName("李二狗");
		int rows = sql.getMapper(DemoMapper.class).update(demo);
		sql.commit();
		System.out.println("受影响行数 " + rows);
	}	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值