MyBatis学习笔记(2022-11-30)

熬过无人问津的日子才会有诗和远方。


文章目录

  • 一、MyBatis简述
  • 二、快速入门
  • 三、MyBatis配置文件详解
    • 1. MyBatis核心配置文件
      • 1.1 configuration(配置)
      • 1.2 properties(属性)
      • 1.3 environments(环境配置)
      • 1.4 typeAliases(类型别名)
      • 1.5 settings(设置)
      • 1.6 mappers(映射器)
      • 1.7 其它配置
    • 2. MyBatis映射配置文件
  • 四、CRUD操作
    • 1. 使用MyBatis实现查询用户
    • 2. 使用MyBatis实现添加用户
    • 3. 使用MyBatis实现修改用户
    • 4. 使用MyBatis实现删除用户
    • 5. MyBatis工具类
  • 五、MyBatis实现复杂查询
    • 1. MyBatis实现模糊查询
    • 2. MyBatis集成Log4j
    • 3. #{} 和 ${} 的区别 (面试题)
    • 4. Limit实现分页
      • 4.1 Limit实现分页
  • 六、MyBatis参数详解
    • 1. 为函数传递简单类型参数
    • 2. 为函数传递多个参数
    • 3. 为函数传递POJO或者Map
    • 4. 为函数传递POJO包装类对象
  • 七、MyBatis结果集详解
    • 1. 查询结果是单行单列的值
    • 2. 查询结果是单行多列的值
      • 2.1 封装成pojo对象
      • 2.2 解决字段名与属性名不一致问题
      • 2.3 封装成map
    • 3. 查询结果是多行多列的值
  • 八、MyBatis动态SQL详解
    • 1. MyBatis动态SQL介绍
    • 2. if 标签
    • 3. where 标签
    • 4. choose (when,otherwise)标签
    • 5. foreach 标签
    • 6. sql 标签(sql片段)
    • 7. set标签
  • 九、MyBatis注解开发

一、MyBatis简述


MyBatis学习笔记(2022-11-30)_第1张图片
  • MyBatis 是一款优秀的持久层框架,用于简化JDBC开发。(半自动化的ORM框架)
  • 它支持自定义SQL、存储过程以及高级映射。
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和Java的POJOs(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录。
  • MyBatis 是 Apache的一个开源项目iBatis,在2010年6月 Apache 将项目交与Google进行管理,更名MyBatis。于2013年11月迁移到GitHub上。
  • Mybatis官网:https://mybatis.org/mybatis-3/zh/index.html
  • 项目地址:https://github.com/mybatis/mybatis-3/releases
  • 目前最新版本是:3.5.11,其发布时间是2022年9月18日。

什么是框架?

  • 框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。
  • 在架构的基础之上构建软件编写更加高效、规范、通用、可扩展。

三层架构和SSM框架的关系

三层架构:数据访问层、业务逻辑层和表示层(web层),区分层次的目的即为了高内聚低耦合的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。

MVC:模型(model)、视图(view)、控制器(controller。

SSM:Spring Framework、Spring MVC、MyBatis(扩展MyBatis-Plus)。

SSH:Spring、Struts、Hibernate(扩展JPA)。

JavaSE => JDBC => Servlet+JSP => SSM =>SpringBoot(分布式) => SpringCould(微服务)


MyBatis缺点

  • 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
  • SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
  • 框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
  • 二级缓存机制不佳。

软件设计:(open-close开闭原则)尽量不修改源代码,对程序进行扩展。

  • 对扩展是开放的。
  • 对修改源码是关闭的。

如何使用MyBatis?

1、使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。

2、如果使用 Maven 来构建项目,则需将下面的依赖引入 pom.xml 文件中:

<dependency>
  <groupId>org.mybatisgroupId>
  <artifactId>mybatisartifactId>
  <version>x.x.xversion>
dependency>



二、快速入门


通过本节可以初步了解MyBatis的使用以及logback的配置

1、创建一个普通的Maven项目

2、引入依赖坐标


<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.5version>
dependency>

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>5.1.46version>
dependency>

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.12version>
    <scope>testscope>
dependency>

<dependency>
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-apiartifactId>
    <version>1.7.20version>
dependency>

<dependency>
    <groupId>ch.qos.logbackgroupId>
    <artifactId>logback-classicartifactId>
    <version>1.2.3version>
dependency>

<dependency>
    <groupId>ch.qos.logbackgroupId>
    <artifactId>logback-coreartifactId>
    <version>1.2.3version>
dependency>

MyBatis学习笔记(2022-11-30)_第2张图片


3、导入SQL脚本

-- 创建数据库
drop database if exists `mybatis`;
create database `mybatis` character set = utf8;

use mybatis;

-- 创建数据表
create table `user`(
	`id` int auto_increment primary key comment '编号',
	`name` varchar(20) unique comment '姓名', -- 唯一约束
	`pwd` varchar(20) comment '密码',
	`gender` char(1) comment '性别',
	`addr` varchar(30) comment '家庭住址'
)engine=innodb default charset=utf8;

-- 插入数据
insert into user(name,pwd,gender,addr) values
("张三","123456",'男',"北京"),
("李四","123456",'女',"南京"),
("王五","777777",'男',"杭州"),
("赵六","123456",'女',"温州"),
("陈七","666666",'男',"黑河");

MyBatis学习笔记(2022-11-30)_第3张图片


4、编写实体类

package com.baidou.pojo;

/**
 * 用户实体类
 *
 * @author 白豆五
 * @version 2022/11/30 11:07
 * @since JDK8
 */
public class User {
    private Integer id;  // 编号
    private String name; // 姓名
    private String pwd;  // 密码
    private char gender; // 性别
    private String addr; // 家庭住址

    // 满参构造方法
    public User(Integer id, String name, String pwd, char gender, String addr) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
        this.gender = gender;
        this.addr = addr;
    }

    // 无参构造方法
    public User() {
    }

    // set/get方法
    public Integer getId() {
        return id;
    }

    public void setId(Integer 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;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    
    // 重写Object类的toString方法,返回对象里的内容
    // Object类的toString方法返回的是对象的地址值,即: "类的全限定名@十六进制的哈希码"
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                ", gender=" + gender +
                ", addr='" + addr + '\'' +
                '}';
    }
}

MyBatis学习笔记(2022-11-30)_第4张图片

5、编写mapper接口

package com.baidou.mapper;

import com.baidou.pojo.User;

import java.util.List;

/**
 * 基于MyBatis实现用户接口
 * mapper、dao都是对数据库进行持久化操作的(crud)
 */
public interface UserMapper {
    // 查询所有用户
    List<User> selectAll();
}

MyBatis学习笔记(2022-11-30)_第5张图片

6、编写UserMapper.xml(SQL映射文件)

参考MyBatis官网的入门案例:https://mybatis.org/mybatis-3/zh/getting-started.html

MyBatis学习笔记(2022-11-30)_第6张图片

MyBatis学习笔记(2022-11-30)_第7张图片

 
DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">




<mapper namespace="com.baidou.mapper.UserMapper">
    
    <select id="selectAll" resultType="com.baidou.pojo.User">
        select * from user
    select>
mapper>

7、编写MyBatis核心配置文件

参考MyBatis官网的入门案例:https://mybatis.org/mybatis-3/zh/getting-started.html

MyBatis学习笔记(2022-11-30)_第8张图片
MyBatis学习笔记(2022-11-30)_第9张图片


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://127.0.0.1:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        
        <mapper resource="com/baidou/mapper/UserMapper.xml"/>
    mappers>
configuration>

8、编写logback.xml日志配置文件

MyBatis学习笔记(2022-11-30)_第10张图片

<?xml version="1.0" encoding="UTF-8"?>
<!-- 配置文件修改时重新加载,默认true -->
<configuration scan="true">

    <!--控制台输出-->  
    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">                
        <encoder> 
            <!-- 输出的日志格式 -->
            <pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg%n</pattern>   
        </encoder>    
    </appender>

    <!--level:用来设置日志输出级别,OFF FATAL ERROR WARN INFO DEBUG TRACE ALL,默认DEBUG-->
    <logger name="com.baidou" level="DEBUG" additivity="false">
        <appender-ref ref="Console"/>
    </logger>
    <root level="DEBUG">
        <appender-ref ref="Console"/>
    </root>
</configuration>

9、编写测试

import com.baidou.mapper.UserMapper;
import com.baidou.pojo.User;
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 org.junit.Test;

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

/**
 * mybatis快速入门
 * @author 白豆五
 * @version 2022/11/30 12:55
 * @since JDK8
 */
public class MyTest {

    /**
     * 方式一: 使用qlSession.selectList()查询所有用户(旧版mybatis的API,不推荐使用)
     * @throws IOException
     */
    @Test
    public void test1() throws IOException {
        // 1、加载mybatis核心配置文件,获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        // 2、获取对应的SqlSession对象,用来执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3、执行sql语句,  selectList()方法的参数是一个字符串,该字符串必须是映射配置文件的 namespace.id
        List<User> list = sqlSession.selectList("com.baidou.mapper.UserMapper.selectAll");
        // 输出结果
        for (User user :
                list) {
            System.out.println(user);
        }
        // 4、释放资源
        sqlSession.close();
    }

    /**
     * 使用Mapper代理开发,查询所有用户
     * @throws IOException
     */
    @Test
    public void test2() throws IOException {
        // 1、加载Mybatis核心配置文件,获取SqlSessionFactory对象
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 2、获取对应的SqlSession对象,用来执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3、获取mapper,来调用mapper中的方法
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.selectAll();
        users.forEach(System.out::println);
        // 4、关闭SqlSession
        sqlSession.close();
    }
}

test1()方法执行结果:
MyBatis学习笔记(2022-11-30)_第11张图片

test2()方法执行结果:
MyBatis学习笔记(2022-11-30)_第12张图片

  • 第一种方式:sqlSession.selectList(),属于方法的调用(mybatis旧版的api),需要传入映射文件的名称空间和查询语句的id(即 "namespace.id"),然后它每次返回的类型都是Object,会存在类型转换问题,导致不太安全。
  • 第二种方式:sqlSession.getMapper(),更灵活的使用方法,不依赖上述的字符串字面值而且安全,只需传参UserMapper.class,返回的是mapper接口的代理对象,然后通过mapper调用对应方法完成sql的执行。

官网解释:
MyBatis学习笔记(2022-11-30)_第13张图片

MyBatis执行流程:

MyBatis学习笔记(2022-11-30)_第14张图片
总结:

  • 导入MyBatis依赖
  • 编写核心配置文件,mybatis-config.xml
  • 编写实体、接口
  • 编写mapper.xml(mapper接口 对应mapper.xml)
  • 在核心配置文件中注册这个mapper.xml
  • 编写工具类(用来获取SqlSession实例)
  • 通过工具类得到SqlSession,然后通过SqlSession来获取mapper代理对象,之后通过这个mapper来调用它的方法
  • 关闭SqlSession



三、MyBatis配置文件详解


MyBatis核心配置文件:mybatis-config.xml

MyBatis映射配置文件:XxxMapper.xml

后期在使用MyBatis框架做数据库操作(持久化操作)会经常使用这两个配置文件。

当然我们也可以在这个XML的dtd约束文件中查看相应的配置:

MyBatis学习笔记(2022-11-30)_第15张图片


1. MyBatis核心配置文件


核心配置文件(mybatis-config.xml)的层级结构如下:

MyBatis学习笔记(2022-11-30)_第16张图片

注:配置每个标签时,需要遵守前后顺序!


1.1 configuration(配置)


configuration表示核心配置文件的根标签。

MyBatis学习笔记(2022-11-30)_第17张图片


1.2 properties(属性)


  • properties属性可以引用外部配置文件;
  • properties属性也可以在内部增加一些属性配置;
  • 如果两个文件有共同的字段,会优先使用外部配置文件的!

1、编写db.properties配置文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8
username=root
password=123456

2、在mybatis-config.xml中引用db.properties


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    
    <properties resource="db.properties"/>

    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        
        <package name="com.baidou.mapper"/>
    mappers>
configuration>

1.3 environments(环境配置)


  • MyBatis 可以配置多种环境,但每个 SqlSessionFactory 实例只能选择一种环境;
  • 用ID这个唯一标识来切换默认使用的环境,如开发环境、测试环境、生产环境/线上;
  • MyBatis默认的事务管理器是JDBC,后期在Spring中会替换成声明式事务;
  • MyBatis默认连接池是POOLED,后期我们可以使用第三方数据库连接池(如Druid、C3P0等)。

MyBatis学习笔记(2022-11-30)_第18张图片

在公司做项目的时候,如果开发阶段连接开发环境的数据库,那么数据库的配置要管运维或者DBA的同事要。


1.4 typeAliases(类型别名)


类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写;

使用场景:1、给实体类起别名(跟map挺像,K-V键值对)

<typeAliases>
    <typeAlias type="com.baidou.pojo.User" alias="user"/>
typeAliases>

使用场景:2、扫描实体类的包,它的的默认别名为类名,首字母小写

<typeAliases>
    <package name="com.baidou.pojo"/>
typeAliases>

在实体类比较少的时候使用第一种方式,如果实体类比较多建议使用第二种方式!

区别:第一种可以自定义别名,第二种它就不行(如果非要改 需要在实体类上增加@Alias注解)。


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

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

1.5 settings(设置)


在MyBatis全局配置文件中通过标签控制MyBatis全局开关。

MyBatis的设置:https://mybatis.org/mybatis-3/zh/configuration.html#settings

MyBatis学习笔记(2022-11-30)_第19张图片


示例:在mybatis-config.xml配置日志输出

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

1.6 mappers(映射器)


mappers(映射器)的作用是告诉 MyBatis 到哪里去找映射文件;

使用场景:1. 使用相对于类路径的资源引用(推荐使用)


<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
mappers>

使用场景:2. 使用映射器接口实现类绑定注册

<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
mappers>

使用场景:3. 使用包扫描进行注入绑定

<mappers>
  <package name="org.mybatis.builder"/>
mappers>

注:接口和它的配置文件必须同名,接口和它的映射文件必须在同级包下(SQL映射文件可以放在src目录中,也可以放在resources目录中 );


1.7 其它配置


  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • databaseIdProvider(数据库厂商标识)

2. MyBatis映射配置文件


MyBatis 真正强大之处在于它的语句映射,这是它的魔力所在。由于它的功能强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于写SQL代码。

SQL 映射配置文件标签结构如下:

  • mapper – 作为映射配置文件的根标签,namespace属性值就是绑定mapper接口的全限定名(包名.接口名)。
    • cache – 该命名空间的缓存配置。
    • cache-ref – 引用其它命名空间的缓存配置。
    • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
    • parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
    • sql – 可被其它语句引用的可重用语句块。
    • select – 映射查询语句,resultType – SQL语句执行的返回值;parameterType – 参数类型;
    • insert – 映射插入语句。
    • update – 映射更新语句。
    • delete – 映射删除语句。

在mapper接口中定义方法,方法名就是该SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致;

MyBatis学习笔记(2022-11-30)_第20张图片



四、CRUD操作


使用MyBatis针对User表实现增删改查操作以及工具类的使用。

前期准备:配置数据库资源文件db.properties以及MyBatis核心配置文件。

sql脚本:

-- 创建数据库
drop database if exists `mybatis`;
create database `mybatis` character set = utf8;

use mybatis;

-- 创建数据表
create table `user`(
	`id` int auto_increment primary key comment '编号',
	`name` varchar(20) unique comment '姓名', -- 唯一约束
	`pwd` varchar(20) comment '密码',
	`gender` char(1) comment '性别',
	`addr` varchar(30) comment '家庭住址'
)engine=innodb default charset=utf8;

-- 插入数据
insert into user(name,pwd,gender,addr) values
("张三","123456",'男',"北京"),
("李四","123456",'女',"南京"),
("王五","777777",'男',"杭州"),
("赵六","123456",'女',"温州"),
("陈七","666666",'男',"黑河");

db.properties:

db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&useUnicode=true&acharacterEncoding=UTF-8
db.username=root
db.password=123456

mybatis-config.xml:


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    
    <properties resource="db.properties"/>

    
    <typeAliases>
        <package name="com.baidou.pojo"/>
    typeAliases>

    
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        
        <package name="com.baidou.mapper"/>
    mappers>
configuration>

1. 使用MyBatis实现查询用户


1、编写实体类

package com.baidou.pojo;

/**
 * 用户实体类
 *
 * @author 白豆五
 * @version 2022/11/30 11:07
 * @since JDK8
 */
public class User {
    private Integer id;  // 编号
    private String name; // 姓名
    private String pwd;  // 密码
    private char gender; // 性别
    private String addr; // 家庭住址

    // 满参构造方法
    public User(Integer id, String name, String pwd, char gender, String addr) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
        this.gender = gender;
        this.addr = addr;
    }

    // 无参构造方法
    public User() {
    }

    // set/get方法
    public Integer getId() {
        return id;
    }

    public void setId(Integer 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;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }


    // 重写toString方法
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                ", gender=" + gender +
                ", addr='" + addr + '\'' +
                '}';
    }
}

2、编写接口:

package com.baidou.mapper;

import com.baidou.pojo.User;

import java.util.List;

/**
 * 基于MyBatis实现用户接口
 *
 */
public interface UserMapper {
    // 根据id查询用户信息
    User findUserById(int id);
}

3、编写mapper映射文件:

 
DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.baidou.mapper.UserMapper">

    
    <select id="findUserById" resultType="com.baidou.pojo.User">
        select * from user where id =#{id}
    select>
mapper>
  • #{}表示占位符,可以看做JDBC中PreparedStatement的?占位符。(预处理)

4、编写测试
package com.baidou.mapper;

import com.baidou.pojo.User;
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 org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

/**
 * 测试类
 *
 * @author 白豆五
 * @version 2022/12/1 20:51
 * @since JDK8
 */
public class UserMapperTest {

    @Test
    public void testFindUserById() throws IOException {
        // 加载Mybatis核心配置文件,获取SqlSessionFactory对象
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 通过sqlSession工厂构建SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获取接口的mapper代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 调用mapper中的方法,执行sql语句
        User user = mapper.findUserById(1);
        System.out.println(user);
        // 关闭SqlSession
        sqlSession.close();
    }
}

MyBatis学习笔记(2022-11-30)_第21张图片


2. 使用MyBatis实现添加用户


编写接口:

// 添加用户
int addUser(User user);

编写mapper映射文件:

<!-- 添加用户-->
<insert id="addUser" parameterType="user">
    insert into mybatis.user(name, pwd, gender, addr) values(#{name}, #{pwd}, #{gender}, #{addr});
</insert>

编写测试:

@Test
public void testaddUser() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user = new User();
    user.setName("水火");
    user.setAddr("洛杉矶");
    user.setGender('男');
    user.setPwd("shuihuo123");

    int count = mapper.addUser(user);
    sqlSession.commit();

    if (count > 0) {
        System.out.println("添加成功");
    }
    sqlSession.close();
}

MyBatis学习笔记(2022-11-30)_第22张图片

因为我们配置主键是自动增加的,也就是不给主键穿值,当同时插入两张表的时候会出现问题,装进对象的时候id为null,通过如下配置解决这个问题。

在 insert 标签上添加如下属性:

  • useGeneratedKeys:能够获取自动增长的主键值。true表示获取。
  • keyProperty :指定将获取到的主键值封装到哪儿个属性里。

3. 使用MyBatis实现修改用户


编写接口:

// 修改用户
int updateUser(User user);

编写mapper映射文件:

<!--修改用户-->
<update id="updateUser" parameterType="user">
    update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
</update>

编写测试:

@Test
public void testUpdateUser() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user = new User();
    user.setId(6);
    user.setName("葛小伦");
    user.setPwd("gexiaolun");

    int count = mapper.updateUser(user);
    sqlSession.commit();

    if (count > 0) {
        System.out.println("修改成功");
    }
    sqlSession.close();
}

MyBatis学习笔记(2022-11-30)_第23张图片


4. 使用MyBatis实现删除用户


编写接口:

// 通过id删除用户
int deleteUserById(int id);

编写mapper映射文件:


<delete id="deleteUserById">
    delete from user where id=#{id}
delete>

编写测试:

@Test
public void testDeleteUserById() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    int count = mapper.deleteUserById(6);
    sqlSession.commit();

    if (count > 0) {
        System.out.println("删除成功");
    }
    sqlSession.close();
}

MyBatis学习笔记(2022-11-30)_第24张图片
注意:增删改需要提交事务。(mybatis默认把自动提交事务改成手动提交,可以在sqlsession.open(true)开启自动提交事务,他这个方法不传参默认是手动提交,但是不推荐直接传参,手动根据场景使用比较灵活)


5. MyBatis工具类


package com.baidou.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;

/**
 * MyBatisUtils工具类
 *
 * @author 白豆五
 * @version 2022/12/1 21:32
 * @since JDK8
 */
public class MyBatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    // 加载mybaits核心配置文件,获取SqlSessionFactory对象
    static {
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    private MyBatisUtils() {
    }


    /**
     * 获取SqlSession对象
     *
     * @return SqlSession
     */
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }

    /**
     * 提交事务并释放sqlSession
     *
     * @param sqlSession
     */
    public static void commitAndClose(SqlSession sqlSession) {
        if (sqlSession != null) {
            sqlSession.commit(); //提交事务
            sqlSession.close();
        }
    }

    /**
     * 回滚事务并释放sqlSession
     *
     * @param sqlSession
     */
    public static void rollbackAndClose(SqlSession sqlSession) {
        if (sqlSession != null) {
            sqlSession.rollback(); //回滚事务
            sqlSession.close();
        }
    }

}



五、MyBatis实现复杂查询


1. MyBatis实现模糊查询

编写接口:

// 模糊查询
List<User> getUserLike(String name);

编写mapper映射文件:

<!--  模糊查询  -->
<select id="getUserLike" resultType="user" parameterType="string">
    select * from user where name like #{value}
</select>

方式一:在Java代码执行的时候,传递通配符 % 内容 %

在这里插入图片描述

方式二:在SQL拼接中使用通配符。

MyBatis学习笔记(2022-11-30)_第25张图片


2. MyBatis集成Log4j


参考文章:https://www.cnblogs.com/suhaha/p/11794786.html

我们使用日志除了要收集一些错误信息,还要查看MyBatis执行SQL语句的执行流程以便于日后排错。

目前日志框架要么使用Logback,要么使用Log4j2.x,不会使用最早的Log4j了。

  • LOG4J (它要指定一个配置文件)
  • STDOUT_LOGGING (标准日志输出)

1、导入依赖坐标


<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>

2、在resources目录下创建一个log4j.properties文件

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/mybatis.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3、在mybatis核心配置文件上配置日志(在configuration标签的第二个位置)

MyBatis学习笔记(2022-11-30)_第26张图片


4、测试:

MyBatis学习笔记(2022-11-30)_第27张图片

扩展:配置标准日志输出

在mybatis-config.xml文件中配置STDOUT_LOGGING为日志的实现。

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

测试:

MyBatis学习笔记(2022-11-30)_第28张图片


3. #{} 和 ${} 的区别 (面试题)


  • #{} 是预编译处理,${}是字符串替换。
  • Mybatis 在处理 #{} 时,会将 sql 中的 #{} 替换为 ? 号,然后调用 PreparedStatement 的 set 方法来赋值;
  • Mybatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
  • 使用 #{} 可以有效的防止 SQL 注入,提高系统安全性 。

4. Limit实现分页


分页有很多种实现方式:如Limit实现分页、RowBounds 分页、分页插件(PageHelper)等

4.1 Limit实现分页


不带条件查询的分页

编写接口:

/**
 * 分页查询用户列表(没有条件)
 * 使用Map传参(如 startIndex、pageSize)
 *
 * @param map
 * @return
 */
List<User> getUserByLimit(Map<String, Integer> map);

编写mapper映射文件:


<select id="getUserByLimit" resultType="user" parameterType="map">
    select *
    from user
    limit #{startIndex},#{pageSize}
select>

测试:

@Test
public void testSelectUserByLimit() {
    Map<String, Integer> map = new HashMap<>();
    Integer pageNo = 2;   //码页
    Integer pageSize = 3; //每页显示3条数据
    Integer startIndex = (pageNo - 1) * pageSize; //分页查询的起始位置
    map.put("startIndex",startIndex);
    map.put("pageSize",pageSize);

    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserByLimit(map);
    userList.forEach(System.out::println);
}

MyBatis学习笔记(2022-11-30)_第29张图片



六、MyBatis参数详解


MyBatis的参数指的就是Mapper接口中函数的参数,该Mapepr接口中函数的参数类型可以分为如下四种:

  • 简单类型,例如Stirng、Long、int等等

  • POJO或者Map,例如 Student、HashMap

  • 多个参数,例如 userName、passWord,(注解、map)(不推荐使用)

  • POJO包装类对象,例如 QueryVO ,T表示泛型类型,为了更加通用而设计,适应场景就是复杂查询。


1. 为函数传递简单类型参数


如果方法中只有一个参数,然后以简单类型传参,那么在映射文件中parameterType的值就是简单类型的别名,在SQL语句中用 #{任意字符串} 引入简单类型的参数 。

// 根据ID查询用户
User findUserById(int id);
<select id="findUserById" resultType="user" parameterType="int">
    select *
    from user
    where id = #{id}
select>

MyBatis常用的类型别名如下:

MyBatis学习笔记(2022-11-30)_第30张图片


2. 为函数传递多个参数


使用注解@Param 传递多个参数

// 用户登录
User login(@Param("name") String userName, @Param("pwd") String passWord);
<select id="login" resultType="user" >
    select * from user where name=#{name} and pwd=#{pwd}
select>
@Test
public void testLogin() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    String userName = "张三";
    String passWord = "123456";
    User user = mapper.login(userName, passWord);
    System.out.println(user!=null?"登录成功":"登录失败");
    sqlSession.close();
}

如果传递多个参数,在映射文件中直接调用参数名会出错误的,他会让你以param1,…,paramn或者 arg1…argn方式调用参数。

MyBatis学习笔记(2022-11-30)_第31张图片

MyBatis为开发者提供了一个注解@Param,使用它可以为参数起名字,在映射配置文件中不需要定义parameterType属性,MyBatis它自动获取,从而提高代码的可读性,但是这会带来一个麻烦。如果SQL很复杂拥有的参数大于10个,则需要为接口处理多个参数名,那么使用起来就很不容易了,不过不必担心,MyBatis还提供传递JavaBean的形式。


3. 为函数传递POJO或者Map


通常使用POJO(实体类)对象或者Map来封装多个参数,前者需要定义模板,后者不用插入指定的参数比较灵活。

方案一:将多个参数封装到pojo中,那么映射配置文件中parameterType的值就是POJO的全限定名(即 包名+类名)或者是别名,在SQL语句中使用 #{POJO属性名} 来引入参数。

// 添加用户
int addUser(User user);
// 修改用户
int updateUser(User user);

<insert id="addUser" parameterType="user">
    insert into user(name, pwd, gender, addr) value (#{name}, #{pwd}, #{gender}, #{addr})
insert>


<update id="updateUser" parameterType="user">
    update user
    set name=#{name},
    pwd=#{pwd}
    where id = #{id}
update>

方案二:将多个参数封装到一个Map集合中(前提条件是这些参数没有对应的POJO),然后映射配置文件中parameterType的值设置为map,最后在SQL语句中使用 #{map的key} 来引入参数。

// 带条件的分页查询
List<User> getUserByPage(Map<String, Integer> map);
<select id="getUserByPage" resultType="user" parameterType="map">
    select *
    from user
    <where>
        <if test="pwd!=null">
            and pwd = #{pwd}
        if>
    where>
    limit #{startIndex},#{pageSize}
select>
@Test
public void testSelectUserByPage() {
    Map<String, Integer> map = new HashMap<>();
    Integer pageNo = 1;   //码页
    Integer pageSize = 3; //每页显示3条数据
    Integer startIndex = (pageNo - 1) * pageSize; //分页查询的起始位置
    map.put("startIndex",startIndex);
    map.put("pageSize",pageSize);
    map.put("pwd",123456); //筛选密码为123456的用户

    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserByPage(map);
    userList.forEach(System.out::println);
}

Map几乎适用所有场景,但是我们用得不多。原因有两个:首先,Map是一个键值对集合,使用者要通过阅读它的键,才能明白其作用;其次,使用Map不能限定其传递的数据类型,因此业务性质不强,可读性差,使用者要读懂代码才能知道需要传递什么参数给它,所以不推荐用这种方式传递多个参数。



4. 为函数传递POJO包装类对象


POJO包装类对象,也就是在一个对象中嵌入了另一个对象和其他条件。(套娃操作)

需求:使用分页查询,查询性别为男的用户列表

1、封装POJO包装类

package com.baidou.vo;

import com.baidou.pojo.User;

/**
 * 用户查询条件
 *
 * @author 白豆五
 * @version 2022/12/1 23:02
 * @since JDK8
 */
public class QueryVO {
    //页码(当前页)
    private Long pageNo;
    // 每页显示的条数
    private Long pageSize;
    //偏移量
    private Long offSet;
    // 查询用户的条件
    private User userCondition;

    public Long getOffSet() {
        return (pageNo - 1) * pageSize; //分页查询的起始位置
    }

    public Long getPageNo() {
        return pageNo;
    }

    public void setPageNo(Long pageNo) {
        this.pageNo = pageNo;
    }

    public Long getPageSize() {
        return pageSize;
    }

    public void setPageSize(Long pageSize) {
        this.pageSize = pageSize;
    }

    public User getUserCondition() {
        return userCondition;
    }

    public void setUserCondition(User userCondition) {
        this.userCondition = userCondition;
    }

    /*解决问题:
    org.apache.ibatis.exceptions.PersistenceException:

    ### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'gender' in 'class com.baidou.vo.QueryVO'

    ### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'gender' in 'class com.baidou.vo.QueryVO'
    */
    public char getGender() {
        return userCondition.getGender();
    }
}

2、定义UserMapper接口

// 分页查询用户列表(有条件)
List<User> getUserByQueryVO(QueryVO queryVO);

3、编写映射

<select id="getUserByQueryVO" resultType="user" parameterType="com.baidou.vo.QueryVO">
    select *
    from user
    <where>
        <if test="gender!=null">
            and gender=#{userCondition.gender}
        if>
    where>
    limit #{offSet},#{pageSize}
select>

4、测试

@Test
public void testGetUserByQueryVO() {
    QueryVO queryVO = new QueryVO();
    User userCondition = new User();
    userCondition.setGender('男');
    queryVO.setUserCondition(userCondition);
    queryVO.setPageNo(1L); //设置当前页面
    queryVO.setPageSize(3L); //每页显示3条数据

    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userVoList = mapper.getUserByQueryVO(queryVO);
    userVoList.forEach(System.out::println);
    sqlSession.close();
}

MyBatis学习笔记(2022-11-30)_第32张图片



七、MyBatis结果集详解


我们学习MyBatis框架主要学习MyBatis的核心配置文件MyBatis映射配置文件

MyBatis参数、MyBatis结果集、MyBatis动态SQL这三节都是围绕MyBatis映射配置文件的。

MyBatis结果集就是MyBatis 执行查询的SQL语句时返回的结果集,该结果集有三种类型:

  • 单行单列的值

  • 单行多列的值(用对象封装)

  • 多行多列的值 (用List集合封装)

需要在映射配置文件中配置这三种结果;也就是