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

在这里插入图片描述

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


一、MyBatis简述


  • 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.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>



二、快速入门


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

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

2、引入依赖坐标

<!-- mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>
<!-- mysql 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>
<!-- junit 单元测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope><!--作用范围只在测试环境有效-->
</dependency>
<!-- slf4j日志api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.20</version>
</dependency>
<!-- logback-classic -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
<!-- logback-core -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>

在这里插入图片描述


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",'男',"黑河");

在这里插入图片描述


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 + '\'' +
                '}';
    }
}

在这里插入图片描述

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();
}

在这里插入图片描述

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

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

在这里插入图片描述

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?> <!--xml文档声明-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    引入外部的dtd约束:  <!DOCTYPE 根标签名 PUBLIC "dtd文件名字" "dtd文件的位置">
           根标签:         mapper
           dtd文件名:      -//mybatis.org//DTD Mapper 3.0//EN
           dtd文件的位置:  https://mybatis.org/dtd/mybatis-3-mapper.dtd
-->


<!--
    mapper根标签:映射器
         namespace:名称空间,用来绑定对应的mapper接口
-->
<mapper namespace="com.baidou.mapper.UserMapper">
    <!--
        select标签: selct查询语句
             id: 是这里的唯一标识用来绑定接口中对应的方法
             resultType: 返回值类型
    -->
    <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

在这里插入图片描述
在这里插入图片描述

<?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核心配置文件-->
<configuration>
    <!--可以多环境切换
		 default="development"
    -->
    <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&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!--映射器-->
    <mappers>
        <!--之后mapper的配置文件需要在这里注册-->
        <mapper resource="com/baidou/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

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

在这里插入图片描述

<?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()方法执行结果:
在这里插入图片描述

test2()方法执行结果:
在这里插入图片描述

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

官网解释:
在这里插入图片描述

MyBatis执行流程:

在这里插入图片描述
总结:

  • 导入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约束文件中查看相应的配置:

在这里插入图片描述


1. MyBatis核心配置文件


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

在这里插入图片描述

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


1.1 configuration(配置)


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

在这里插入图片描述


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

<?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>
    <!--引入外部配置文件-->
    <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>
        <!--<mapper resource="com/baidou/mapper/UserMapper.xml"/>-->
        <package name="com.baidou.mapper"/>
    </mappers>
</configuration>

1.3 environments(环境配置)


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

在这里插入图片描述

在公司做项目的时候,如果开发阶段连接开发环境的数据库,那么数据库的配置要管运维或者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 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名映射的类型
_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

1.5 settings(设置)


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

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

在这里插入图片描述


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

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

1.6 mappers(映射器)


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

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

<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册 -->
<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,并保持参数类型和返回值类型一致;

在这里插入图片描述



四、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:

<?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>
    <!--  引入外部配置文件  -->
    <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>
        <!-- 使用扫包的方式加载SQL映射文件(xxx.xml) -->
        <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映射文件:

<?xml version="1.0" encoding="UTF-8" ?> <!--xml文档声明-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

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

    <!--根据id查询用户信息-->
    <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();
    }
}

在这里插入图片描述


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();
}

在这里插入图片描述

因为我们配置主键是自动增加的,也就是不给主键穿值,当同时插入两张表的时候会出现问题,装进对象的时候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();
}

在这里插入图片描述


4. 使用MyBatis实现删除用户


编写接口:

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

编写mapper映射文件:

<!--通过id删除用户-->
<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默认把自动提交事务改成手动提交,可以在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拼接中使用通配符。


2. MyBatis集成Log4j


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

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

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

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

1、导入依赖坐标

<!-- log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</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标签的第二个位置)

在这里插入图片描述


4、测试:

在这里插入图片描述

扩展:配置标准日志输出

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

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

测试:

在这里插入图片描述


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参数详解


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

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

  • POJO或者Map,例如 Student、HashMap

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

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


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


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

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

MyBatis常用的类型别名如下:

在这里插入图片描述


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为开发者提供了一个注解@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结果集详解


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

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

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

  • 单行单列的值

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

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

需要在映射配置文件中配置这三种结果;也就是<select>标签的 resultType、resultMap属性。

resultMap可以解决字段与属性名不一致问题。(当然sql片段和取别名也可以、或者在核心配置文件中配置驼峰命名)


前期准备

1、sql脚本:

use mybatis;

-- 删除 brand表
drop table if exists brand;

-- 创建 brand表
create table brand
(
    -- id 主键
    id           int primary key auto_increment,
    -- 品牌名称
    brand_name   varchar(20),
    -- 企业名称
    company_name varchar(20),
    -- 排序字段
    ordered      int,
    -- 描述信息
    description  varchar(100),
    -- 状态:0:禁用  1:启用
    status       int
);
-- 添加数据
insert into brand (brand_name, company_name, ordered, description, status)
values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
       ('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
       ('小米', '小米科技有限公司', 50, 'are you ok', 1);

2、编写实体类:

package com.baidou.pojo;

/**
 * 品牌实体类
 */
public class Brand {

    private Integer id;          //品牌编号
    private String brandName;    //品牌名称
    private String companyName;  //企业名称

    private Integer ordered;     //排序字段
    private String description;  //描述信息
    private Integer status;      //状态 0禁用 1启用


    public Brand(Integer id, String brandNmae, String companyName, Integer ordered, String description, Integer status) {
        this.id = id;
        this.brandName = brandNmae;
        this.companyName = companyName;
        this.ordered = ordered;
        this.description = description;
        this.status = status;
    }

    public Brand() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBrandName() {
        return brandName;
    }

    public void setBrandName(String brandNmae) {
        this.brandName = brandNmae;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public Integer getOrdered() {
        return ordered;
    }

    public void setOrdered(Integer ordered) {
        this.ordered = ordered;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "Brand{" +
                "id=" + id +
                ", brandNmae='" + brandName + '\'' +
                ", companyName='" + companyName + '\'' +
                ", ordered=" + ordered +
                ", description='" + description + '\'' +
                ", status=" + status +
                '}';
    }
}


BrandMapper接口:

package com.baidou.mapper;

// 基于mybatis操作品牌的接口
public interface BrandMapper {
}

编写mapper映射文件(BrandMapper.xml):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

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

</mapper>

mybatis核心配置文件 (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">

<configuration>
    <!--  引入外部配置文件  -->
    <properties resource="db.properties"/>

    <!-- 配置使用Log4j日志输出 -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
    <!--当前处于开发环境(开发、测试、生产/上线) -->
    <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>
        <!-- 使用扫包的方式加载SQL映射文件(xxx.xml) -->
        <package name="com.baidou.mapper"/>
    </mappers>
</configuration>

数据库资源文件 db.properties:

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

1. 查询结果是单行单列的值


如果查询结果是单行单列的值,那么映射配置文件中的resultType属性值就是Mapper接口的返回值。

编写接口:

// 统计品牌数量
Long brandTotalCount();

编写mapper映射文件:

<!--统计品牌数量-->
<select id="brandTotalCount" resultType="long">
    select count(*) from mybatis.brand;
</select>

编写测试:

/**
 * 测试类
 *
 * @author 白豆五
 * @version 2022/12/1 23:35
 * @since JDK8
 */
public class BrandMapperTest {

    @Test
    public void testBrandTotalCount() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        Long count = mapper.brandTotalCount();
        System.out.println(count);
        sqlSession.close();
    }
}

在这里插入图片描述



2. 查询结果是单行多列的值


当查询结果是单行多列的值时,MyBatis针对查询结果集提供了两种封装方式:

1、封装成pojo对象。

2、封装成map 。(不建议使用map来处理查询结果是单行多列的值)


2.1 封装成pojo对象


如果查询结果是单行多列的值,将结果集封装成pojo对象,配置文件中的resultType属性值就是pojo的全限定名(包名+类名)或者是别名;此时它就要求查询结果的字段、字段类型必须和pojo对象的属性名、属性类型保持一致。

编写接口:

//根据ID查询品牌信息
Brand selectBrandById(int id);

编写mapper映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

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


    <!--统计品牌数量-->
    <select id="brandTotalCount" resultType="long">
        select count(*) from mybatis.brand;
    </select>
</mapper>

编写测试:

@Test
public void testSelectBrandById() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    Brand brand = mapper.selectBrandById(1);
    System.out.println(brand);
    sqlSession.close();
}

在这里插入图片描述
属性名和字段名不一致会导致返回结果集的部分字段映射不到实体类对象,即该属性会显示null。


2.2 解决字段名与属性名不一致问题


解决字段名与属性名不一致问题,有如下4种解决方案:

  • 起别名
  • sql片段
  • 使用resultMap结果集映射
  • 在核心配置文件中配置驼峰命名 (例如 brand_name 转 brandName)

方式一:查询的字段(或列)起别名
<!--根据ID查询品牌信息-->
<select id="selectBrandById" resultType="generator.pojo.Brand">
    select id, brand_name as brandName, company_name  as companyName, ordered, description, status from mybatis.brand where id=#{id}
</select>

在这里插入图片描述

缺点:不够严谨和通用,如果多个查询需要都起别名。


方式二:sql片段

<!--定义一个sql片段-->
<sql id="brandSql">
    id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>

<!--根据ID查询品牌信息-->
<!--<include refid="brandSql"/> 引用sql片段-->
<select id="selectBrandById" resultType="generator.pojo.Brand">
    select
    <include refid="brandSql"/>
    from mybatis.brand where id=#{id}
</select>

在这里插入图片描述
他也是基于起别名方式的。


方式三:resultMap结果集映射

<!--定义resultMap处理属性名和字段名不一致问题-->
<resultMap id="brandMap" type="com.baidou.pojo.Brand">
    <!--
        property 实体类的属性
        column  数据表中的字段
    -->
    <result property="brandName" column="brand_name"></result>
    <result property="companyName" column="company_name"></result>
</resultMap>


<!--根据ID查询品牌信息-->
<select id="selectBrandById" resultMap="brandMap">
    select * from mybatis.brand where id=#{id}
</select>

在这里插入图片描述
推荐使用resultMap处理这个问题(而且resultMap非常强大)。


2.3 封装成map


如果查询结果是单行多列的值,将结果集封装成Map,映射配置文件的resultType属性值就是map,那么此时查询结果中的字段名就是map的key,字段值就是map的value。

 //通过品牌名称查询品牌信息
 Map<String,Object> selectBrandByName(String name);
<!--通过品牌名称查询品牌信息-->
<select id="selectBrandByName" resultType="java.util.Map">
    select * from mybatis.brand where brand_name=#{name};
</select>
@Test
public void testSelectBrandByName() throws IOException {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    Map<String, Object> map = mapper.selectBrandByName("三只松鼠");
    System.out.println("根据品牌名称查询品牌的结果是"+map);
}

在这里插入图片描述

缺点:必须知道map的key,才能找到数据。


3. 查询结果是多行多列的值


查询结果是多行多列的值,将多条数据存储到List中,映射配置文件的resultType属性值就是pojo的全限定名或者是别名。(如果属性名与字段名不一致,可以使用resultMap)

//查询所有品牌
List<Brand> selectBrandAll();
<!--  查询所有用户  -->
<select id="selectBrandAll" resultType="com.baidou.pojo.Brand">
	# 查询的字段(或列)起别名
    select  id, brand_name as brandName, company_name as companyName, ordered, description, status from brand
</select>
@Test
public void testSelectUserAll() throws IOException {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    List<Brand> list = mapper.selectBrandAll();
    for (Brand brand:
            list){
        System.out.println(brand);
    }
}

在这里插入图片描述



八、MyBatis动态SQL详解


1. MyBatis动态SQL介绍


动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,我们通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。例如,拼接时要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。而动态 SQL 恰好解决了这一问题,可以根据场景动态的构建查询。

动态 SQL 只有几个基本元素,与 JSTL 或 XML 文本处理器相似,十分简单明了,大量的判断都可以在 MyBatis 的映射 XML 文件里配置,以达到许多需要大量代码才能实现的功能(注解方式配置SQL可读性差)。

动态 SQL 减少了平时编写SQL代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。

MyBatis 动态SQL 包括以下几种元素:

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

环境搭建

1、新建一个数据库表:blog

字段为:id、title、author、create_time、views。

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;

2、创建一个普通的maven工程

在这里插入图片描述

3、编写IDUtils工具类

public class IDUtils {

    private IDUtils() {
    }

    /**
     * 生成Id
     * a58d257945e64ce4a7908411123eac66
     *
     * @return String
     */
    public static String genId() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
}

4、编写实体类Blog

public class Blog {
    private String id;        //博客id
    private String title;     //博客标题
    private String author;    //博客作者
    private Date createTime;  //创建时间
    private int views;        //浏览量

    public Blog(String id, String title, String author, Date createTime, int views) {
        this.id = id;
        this.title = title;
        this.author = author;
        this.createTime = createTime;
        this.views = views;
    }

    public Blog() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public int getViews() {
        return views;
    }

    public void setViews(int views) {
        this.views = views;
    }

    @Override
    public String toString() {
        return "Blog{" +
                "id='" + id + '\'' +
                ", title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", createTime=" + createTime +
                ", views=" + views +
                '}';
    }
}

5、编写mapper接口以及xml映射文件

public interface BlogMapper {
    //添加一个博客
    int addBlog(Blog blog);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baidou.mapper.BlogMapper">

    <!--添加一个博客-->
    <insert id="addBlog" parameterType="com.baidou.pojo.Blog">
        insert into mybatis.blog(id, title, author, create_time, views)
        values (#{id}, #{title}, #{author}, #{createTime}, #{views});
    </insert>
</mapper>

6、mybatis核心配置文件,下划线驼峰自动转换

<settings>
   <!-- 驼峰命名,自动匹配属性和字段 如userName->user_name -->
   <setting name="mapUnderscoreToCamelCase" value="true"/>
   <!-- 日志配置 -->
   <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

<!--  映射器  -->
<mappers>
    <!-- 使用扫包的方式加载SQL映射文件(xxx.xml) -->
    <package name="com.baidou.mapper"/>
</mappers>

7、初始化数据

public class Test {
    public static void main(String[] args) {
        SqlSession session = MyBatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtils.genId());
        blog.setTitle("Go语言高级");
        blog.setAuthor("KKK");
        blog.setCreateTime(new Date());
        blog.setViews(9999);
        mapper.addBlog(blog);
        session.commit();

        blog.setId(IDUtils.genId());
        blog.setTitle("Java快速入门");
        blog.setAuthor("AAA");
        blog.setCreateTime(new Date());
        blog.setViews(2000);
        mapper.addBlog(blog);
        session.commit();

        blog.setId(IDUtils.genId());
        blog.setTitle("Spring快速入门");
        blog.setAuthor("");
        blog.setCreateTime(new Date());
        blog.setViews(3000);
        mapper.addBlog(blog);
        session.commit();

        blog.setId(IDUtils.genId());
        blog.setTitle("微服务快速入门");
        blog.setAuthor("");
        blog.setCreateTime(new Date());
        blog.setViews(5000);
        mapper.addBlog(blog);
        session.commit();

        session.close();
    }
}

在这里插入图片描述



2. if 标签


if 标签:条件判断( test 属性:逻辑表达式)

test属性传的是入参的参数,不是数据表的字段

需求:使用动态SQL查询指定作者的博客列表,如果没有指定作者名称,那么就查询所有。

//通过作者名称获取博客列表
List<Blog> selectBlogListByAuthor(String name);
<!-- 通过作者名称获取博客列表 -->
<select id="selectBlogListByAuthor" resultType="blog" parameterType="string">
    select *from blog
    # 当作者不为空使拼接条件where author=#{name}
    <if test="author != null and author!=''">
        where author=#{name}
    </if>
</select>
@Test
public void testSelectBlogListByAuthor(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    List<Blog> blogList = mapper.selectBlogListByAuthor(null);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}

多个条件判断:

//查询指定作者,访问量大于2000的博客列表
List<Blog> selectBlogListByAuthorAndViews(Blog blog);
<!--查询指定作者,访问量大于2000的博客列表-->
<select id="selectBlogListByAuthorAndViews" resultType="blog" parameterType="blog">
    select title, author, create_time, views from blog
    # 使用1=1完成条件的过渡,不然sql会报错
    where 1=1
    <if test="author!=null">
        and author=#{author}
    </if>
    <if test="views!=null">
        and views>#{views}
    </if>
</select>
@Test
public void testSelectBlogListByAuthorAndViews(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Blog blog = new Blog();
    blog.setAuthor("KKK");
    blog.setViews(2000);
    List<Blog> blogList = mapper.selectBlogListByAuthorAndViews(blog);
    for (Blog blog1 : blogList) {
        System.out.println(blog1);
    }
    sqlSession.close();
}


3. where 标签


where标签的作用:

1、可以在条件之前加上where标签。

2、可以去掉第一个条件的and(也省略了1=1的过渡)

需求:查询指定作者,访问量大于2000的博客列表(多条查询)

//查询指定作者,访问量大于2000的博客列表
List<Blog> selectBlogListByAuthorAndViews(Blog blog);
<!--查询指定作者,访问量大于2000的博客列表-->
<select id="selectBlogListByAuthorAndViews" resultType="blog" parameterType="blog">
    select title, author, create_time, views from blog
    # where 1=1
    <where>
        <if test="author!=null and author!=''">
            # and author=#{author}
            author=#{author}
        </if>
        <if test="views!=null">
            and views>#{views}
        </if>
    </where>
</select>
@Test
public void testSelectBlogListByAuthorAndViews(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Blog blog = new Blog();
    blog.setAuthor("kkk");
    blog.setViews(2000);
    List<Blog> blogList = mapper.selectBlogListByAuthorAndViews(blog);
    for (Blog blog1 : blogList) {
        System.out.println(blog1);
    }
    sqlSession.close();
}

4. choose (when,otherwise)标签


单个条件(分支)的动态SQL。

  • choose 类似Java中的switch。
  • when类似Java中的case。

需求:单条件动态查询博客列表。

编写接口:

// 单条件动态查询博客列表
List<Blog> selectConditionSingle(Blog blog);

编写sql映射文件:

<!-- 单条件动态查询博客列表 -->
<select id="selectConditionSingle" resultType="com.baidou.pojo.Blog">
    select * from mybatis.blog
    <where>
       <choose><!--相当于switch-->
           <when test="author!=null and author!=''"><!--相当于case-->
               and author=#{author}
           </when>
           <when test="title!=null and title!=''">
               and title like #{title}
           </when>
       </choose>
    </where>
</select>
<!--如果条件都不走,就查所以-->

编写测试:

public class Test {
    public static void main(String[] args) {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setTitle("%Java%");
        // blog.setAuthor("KKK");

        List<Blog> list = mapper.selectConditionSingle(blog);
        for (Blog b : list) {
            System.out.println(b);
        }

        sqlSession.close();
    }
}

5. foreach 标签


foreach标签可以遍历集合。

需求:根据传入的id列表批量删除多个博客

//根据传入的id列表批量删除多个博客
int deleteByIds(List<String> ids);
<!-- 根据传入的id列表批量删除多个博客 -->
<delete id="deleteByIds" parameterType="string">
    # delete from blog where id in(?,?,?); 删除多个数据
    delete from blog where id in
    <!--
              使用foreach标签遍历集合,将遍历出来的数据放到()里面
              collection属性:表示要遍历的集合,如果要遍历的是一个List集合那么值就是list,
                                          如果要遍历的是一个Map集合那么值就是map.
              item属性:表示遍历出来的元素
              separator属性:表示遍历出来的每一个元素的分隔符。
              open属性表示在集合遍历出来的第一个元素之前拼接字符串
              close属性表示在集合遍历出来的最后一个元素之后拼接字符串
     -->
    <foreach collection="list" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
    ;
</delete>
@Test
public void testDeleteByIds() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    List<String> list = new ArrayList<>();
    list.add("a63e183c920f4547aa17ef04555612ef");
    list.add("acf126f6a08a474d8bf47c6575e48c18");
    list.add("eeb1fd8ea29f4d16bf46d7b37be281eb");
    int deleteRows = mapper.deleteByIds(list);
    System.out.println("删除数据受影响的行数是:"+deleteRows);
    MyBatisUtils.commitAndColse(sqlSession); // 提交事务并释放sqlsession
}

6. sql 标签(sql片段)


sql标签可以把公共(多个查询都会使用到)的SQL语句进行抽取,然后再使用include标签引入该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">

<mapper namespace="com.baidou.mapper.BlogMapper">
    <!-- 通过作者名称获取博客列表 -->
    <select id="selectBlogListByAuthor" resultType="blog" parameterType="string">
        select id, title, author, create_time, views from blog
        <if test="author != null">
            where author=#{name}
        </if>
    </select>

    <!--查询指定作者,访问量大于2000的博客列表-->
    <select id="selectBlogListByAuthorAndViews" resultType="blog" parameterType="blog">
        select id, title, author, create_time, views from blog
        # where 1=1
        <where>
            <if test="author!=null">
                author=#{author}
            </if>
            <if test="views!=null">
                and views>#{views}
            </if>
        </where>
    </select>
</mapper>
<?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.baidou.mapper.BlogMapper">

    <!-- 使用sql标签将可以复用的SQL语句抽取出来
         然后在指定的位置使用include标签来引用该SQL语句
     -->
    <sql id="select_all">
        select id, title, author, create_time, views
        from blog
    </sql>

    <!-- 通过作者名称获取博客列表 -->
    <select id="selectBlogListByAuthor" resultType="blog" parameterType="string">
        <include refid="select_all"></include>
        <if test="author != null">
            where author=#{name}
        </if>
    </select>

    <!--查询指定作者,访问量大于2000的博客列表-->
    <select id="selectBlogListByAuthorAndViews" resultType="blog" parameterType="blog">
        <include refid="select_all"></include>
        # where 1=1
        <where>
            <if test="author!=null">
                author=#{author}
            </if>
            <if test="views!=null">
                and views>#{views}
            </if>
        </where>
    </select>
</mapper>

7. set标签


set标签可以用于动态包含需要更新的列,忽略其它不更新的列。

需求:通过id动态修改博客信息。

编写接口:

// 修改博客信息
void update(Blog blog);

编写sql映射文件:

<!-- 动态修改博客信息-->
<update id="update">
    update mybatis.blog
    <set>
        <if test="author!=null and author!=''">
            author = #{author},
        </if>
        <if test="title!=null and title!=''">
            title = #{title},
        </if>
        <if test="views!=null">
            views = #{views}
        </if>
    </set>
    where id=#{id}
</update>

编写测试:

public class Test {
    public static void main(String[] args) {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession .getMapper(BlogMapper.class);
        Blog blog = new Blog();

        blog.setId("1fae806d59d14b089270b403e78cb7cd");
        blog.setAuthor("张三");
        blog.setTitle("分手大师");
        blog.setViews(666);
        mapper.update(blog);

        MyBatisUtils.commitAndClose(sqlSession);
    }
}


九、MyBatis注解开发


使用注解可以代替映射文件中的配置,但是简单的sql可以用注解玩玩,复杂的sql就只能用配置文件啦(提高开发效率,和可读性)。

在这里插入图片描述
在这里插入图片描述

Mybatis 针对 CURD 操作都提供了相应的注解:

  • 查询 :@Select
  • 添加 :@Insert
  • 修改 :@Update
  • 删除 :@Delete

示例:使用注解方式,根据id查询用户。

@Select("select * from user where id=#{id}")
User findUserById(int id);


MyBatis缓存
MyBatis连接池
MyBatis缓存
MyBatis关联查询
MyBatis延迟加载策略

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

白豆五

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

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

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

打赏作者

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

抵扣说明:

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

余额充值