Mybatis详解(超全面)

目录:

一、MyBatis入门

1、什么是MyBatis

​ MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

2、MyBatis、Hibernate 和 JDBC

  1. 和 Hibernate 相比,MyBatis 封装性低于 Hibernate,但性能优秀、小巧、简单易学、应用广泛

  2. 和 JDBC 相比,MyBatis 减少了 50% 以上的代码量,并且满足高并发和高响应的要求

3、优缺点

3.1 优点

  • MyBatis 是免费且开源的。
  • 与 JDBC 相比,减少了 50% 以上的代码量。
  • MyBatis 是最简单的持久化框架,小巧并且简单易学。
  • MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 中,和程序逻辑代码分离,降低耦合度,便于统一管理和优化,提高了代码的可重用性。
  • 提供 XML 标签,支持编写动态 SQL 语句。
  • 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
  • 支持存储过程。MyBatis 以存储过程的形式封装 SQL,可以将业务逻辑保留在数据库之外,增强应用程序的可移植性、更易于部署和测试。

3.2 缺点

  • 编写 SQL 语句工作量较大,对开发人员编写 SQL 语句的功底有一定要求。
  • SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

3.3 使用场景

​ MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。适用于性能要求高,且需求变化较多的项目,如互联网项目

4、安装

​ 要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可,如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中

1.例如在Maven项目中,导入MyBatis3.5.2版本依赖

<!--MyBatis依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
<!--数据库依赖-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

二、第一个MyBatis程序

1、从XML中构建 SqlSessionFactory

​ 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以由SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

1.1 XML配置

mybatis-config.xml配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 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配置内容时,有顺序限制-->
<configuration>
    <!--mybatis环境,default属性指定一个环境-->
    <environments default="development">
        <!--id属性自定义的环境唯一标识符-->
        <environment id="development">
            <!--指定使用jdbc事务管理-->
            <transactionManager type="JDBC"/>
            <!--使用mybatis内部带连接池功能的数据源-->
            <dataSource type="POOLED">
                <!--获取配置驱动-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--获取配置url-->
                <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;characterEncoding=utf8"/>
                <!--获取配置账号-->
                <property name="username" value="root"/>
                <!--获取配置密码-->
                <property name="password" value="112112"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
    <mappers>
        <!--使用相对于类路径的资源引用  这是相对于类路径中-->
		<mapper resource="mapper/UserMapper.xml"/>
        <!--<mapper resource="com/hqyj/mapper/UserMapper.xml"/>-->
    </mappers>
</configuration>

1.1.1 注意事项

1.注意 XML 头部的声明,它用来验证 XML 文档的正确性

2.mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息

3.对于映射器mappers,通过<mapper resource="mapper/UserMapper.xml"/>引入XML文件的时候,要么XML文件在maven项目的resources下,要么就需要在pom.xml文件中导入以下内容

<build>
    <!--resources配置解决mybatis 的mapperXml配置在java路径不被扫描的问题 -->
    <resources>
        <resource>
            <directory>src/main/java</directory>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!--跳过测试 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
    </plugins>
</build>

1.2 获取SqlSessionFactory

// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);

1.3 设计数据库表

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(255) NULL DEFAULT NULL,
  `state` int(255) NULL DEFAULT 1,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

INSERT INTO `user` VALUES (1, 'admin', '111', 20, 1);
INSERT INTO `user` VALUES (2, '王五', '789', 24, 1);
INSERT INTO `user` VALUES (3, '小王', '123', 88, 1);

1.4 编写JavaBean

首先,在IDEA下载插件Lombok

然后在pom.xml中导入依赖

<!--Lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
</dependency>

最后使用注解代替setter、getter、toString、构造函数

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
    private int age;
    private int deptId;
    private int state;
}

1.5 编写UserMapper

<?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.hqyj.mapper.UserMapper">
    <select id="getUserList" resultType="com.hqyj.pojo.User">
        select * from `user`
    </select>
</mapper>

1.6 测试

// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL---方式一
// SqlSession执行文件中定义的SQL,并返回映射结果,参数可以直接输入方法名
// List<User> userList = sqlSession.selectList("com.hqyj.cl.mapper.UserMapper.getUserList");
// 执行SQL---方式二
// 通过SqlSession对象获取StudentMapper的代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// 使用代理mapper对象查询全部的用户信息
List<Student> studentList = mapper.selectAll();
// 输出内容
System.out.println(userList);

1.7 #{}和${}

#{}方式传值是安全的,起占位符作用,为参数占位符 ?,即sql 预编译,能防止sql 注入

${}为字符串替换,即 sql 拼接,不能防止sql 注入

1.7.1 注入案例

注意:

​ 使用${} 的方式会取到jdbc.properties中的配置属性内容,所以要注意

​ 如果UserMapper接口中的参数大于等于2个的时候,那么有3种方式解决参数找不到问题

​ 1.封装成Map集合方式(暂时不讲,比较繁琐)

​ 2.封装成对应pojo类对象(login方法的参数直接换成User对象)

​ 3.使用注解@Param来指定参数名调整mapper接口方法

​ login(@Param(“username”)String username,@Param(“password”)String password)

​ 官方推荐:参数个数小于5个用注解,大于5个用对象

List<Student> login(@Param("username")String username,@Param("password")String password);
<select id="login" resultType="com.hqyj.cl.pojo.Student">
    select * from `student` where username='${username}' and password='${password}'
</select>
public class TestStudentDao {
    public static void main(String[] args) throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        //可以调整login参数只有username以及mapper的sql 发现可以查出,但是2个参数不行
        List<Student>  student = mapper.login("admin","111222' or '1'='1");
        System.out.println(student);
    }
}

三、核心对象

MyBatis 有三个基本要素:

  • 核心接口和类
  • MyBatis核心配置文件(mybatis-config.xml)
  • SQL映射文件(mapper.xml)

MyBatis 的核心接口和类,如下所示

                           bulid()                        openSession()
SqlSessionFactoryBuilder  --------->  sqlSessionFactory  --------------->  sqlSession 

每个 MyBatis 应用程序都以一个 SqlSessionFactory 对象的实例为核心。

首先获取 SqlSessionFactoryBuilder 对象,可以根据 XML 配置文件或者 Configuration 类的实例构建该对象。

然后获取 SqlSessionFactory 对象,该对象实例可以通过 SqlSessionFactoryBuilder 对象来获取。

有了 SqlSessionFactory 对象之后,就可以进而获取 SqlSession 实例。SqlSession 对象中完全包含以数据库为背景的所有执行 SQL 操作的方法,用该实例可以直接执行已映射的 SQL 语句。

**需要注意的是:**每个线程都有自己的 SqlSession 实例,SqlSession 实例不能被共享,也不是线程安全的。因此 SqlSession 的作用域范围是 request 作用域或方法体作用域内。

提及request回想一下上个阶段学习过的重定向和转发。重定向不是同一个请求,是两个请求,不会共享request作用域。

1、SqlSessionFactoryBuilder

SqlSessionFactoryBuilder 会根据配置信息或者代码生成 SqlSessionFactory,并且提供了多个 build() 方法重载,这些方法都是在调用同一签名方法,即:

build(Reader reader, String environment, Properties properties)

由于参数 environment 和 properties 都可以为 null,去除重复的方法,真正的重载方法其实只有如下三种:

  • build(InputStream inputStream, String environment, Properties properties)
  • build(Reader reader, String environment, Properties properties)
  • build(Configuration config)

通过上述分析,发现配置信息可以以三种形式提供给 SqlSessionFactoryBuilder 的 build() 方法,分别是 InputStream(字节流)、Reader(字符流)、Configuration(类)。

由于字节流和字符流都属于读取配置文件的方式,所以就很容易想到构建一个 SqlSessionFactory 有两种方式,即:读取 XML 配置文件和编写代码。一般习惯为采取 XML 配置文件的方式来构造 SqlSessionFactory,这样一方面可以避免硬编码,另一方面方便日后配置人员修改,避免重复编译代码。

1.1 SqlSessionFactoryBuilder的生命周期和作用域

SqlSessionFactoryBuilder 的最大特点就是用过即丢。创建 SqlSessionFactory 对象后,这个类就不存在,因SqlSessionFactoryBuilder 的最佳范围就是存在于方法体内,也就是局部变量。

2、SqlSessionFactory

SqlSessionFactory 是工厂接口而不是现实类,他的任务就是创建 SqlSession;所有的 MyBatis 应用都以 SqlSessionFactory 实例为中心,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 对象来获取

2.1 SqlSessionFactory的生命周期和作用域

SqlSessionFactory 对象一旦创建,就会在整个应用程序过程中始终存在。没有理由去销毁或再创建它,并且在应用程序运行中也不建议多次创建 SqlSessionFactory。因此 SqlSessionFactory 的最佳作用域是 Application,即随着应用程序的生命周期一直存在。这种“存在于整个应用运行期间,并且只存在一个对象实例”的模式就是所谓的单例模式(指在运行期间有且仅有一个实例)。

3、SqlSession

SqlSession 是用于执行持久化操作的对象,类似于 JDBC 中的 Connection。它提供了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例直接运行已映射的 SQL 语句。

SqlSession 的用途主要有两种:

  1. 获取映射器。让映射器通过命名空间和方法名称找到对应的 SQL,并发送给数据库,执行后返回结果。
  2. 直接通过“命名空间(namespace)+SQL id”的方式执行 SQL,不需要获取映射器。

3.1 SqlSession生命周期和作用域

SqlSession 对应一次数据库会话。由于数据库会话不是永久的,因此 SqlSession 的生命周期也不是永久的,每次访问数据库时都需要创建 SqlSession 对象。建议封装方法回收资源。

四、配置文件

在第一个MyBatis程序中,我们已经编写了 MyBatis 的配置文件mybatis-config.xml,下面我们详细的说一下该配置文件中的内容

1、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 /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

1.1 configuration标签

configuration 元素是整个 XML 配置文件的根节点,其角色就相当于是 MyBatis 的总管,MyBatis 所有的配置信息都会存放在它里面。

1.2 properties标签

properties 标签可以通过 resource 属性指定外部 properties 文件(dbconfig.properties),也可以通过 properties 子元素配置。如下

<!--驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--获取连接对象地址-->
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;characterEncoding=utf8"/>
<!--数据库用户名-->
<property name="username" value="root"/>
<!--数据库密码-->
<property name="password" value="112112"/>

在 environments 节点中引用以上变量

<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>

mybatis3.4.2版本开始,提供属性占位符的功能(了解)
默认是关闭状态,手动开启设置
1、设置开启<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
如果你在属性名中使用了 “:” 字符(如:db:username),或者在 SQL 映射中使用了 OGNL 表达式的三元运算符
(如:

t

a

b

l

e

N

a

m

e

!

=

n

u

l

l

?

t

a

b

l

e

N

a

m

e

:

g

l

o

b

a

l

c

o

n

s

t

a

n

t

s

),就需要设置特定的属性来修改分隔属性名和默认值的字符

2

、设置其他配置

<

p

r

o

p

e

r

t

y

n

a

m

e

=

"

o

r

g

.

a

p

a

c

h

e

.

i

b

a

t

i

s

.

p

a

r

s

i

n

g

.

P

r

o

p

e

r

t

y

P

a

r

s

e

r

.

d

e

f

a

u

l

t

v

a

l

u

e

s

e

p

a

r

a

t

o

r

"

v

a

l

u

e

=

"

?

:

"

/

>

3

、修改回去账号

p

r

o

p

e

r

t

y

<

p

r

o

p

e

r

t

y

n

a

m

e

=

"

u

s

e

r

n

a

m

e

"

v

a

l

u

e

=

"

{tableName != null ? tableName : 'global_constants'}),就需要设置特定的属性来修改分隔属性名和默认值的字符 2、设置其他配置`<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>` 3、修改回去账号property` <property name="username" value="

tableName!=null?tableName:globalconstants),就需要设置特定的属性来修改分隔属性名和默认值的字符2、设置其他配置<propertyname="org.apache.ibatis.parsing.PropertyParser.defaultvalueseparator"value="?:"/>‘3、修改回去账号property<propertyname="username"value="{username?:root}"/> 4、这样,如果没有设置`,就是用root

1.3 settings标签

​ settings 标签用于配置 MyBatis 的运行时行为,它能深刻的影响 MyBatis 的底层运行,一般不需要大量配置,大部分情况下使用其默认值即可。大多数情况用它配置日志、开启自动驼峰命名规则映射、开启二级缓存(默认开启)。

1.3.1 开启自动驼峰命名规则映射

<settings>
    <!-- 开启驼峰命名规则 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

1.3.2 配置日志

<settings>
    <!-- 配置log4j日志 -->
    <setting name="logImpl" value="LOG4J"/>
</settings>

1.3.3 开启二级缓存

<settings>
    <!--开启二级缓存,默认是开启的,显示定义出来-->
    <setting name="cacheEnabled" value="true"/>
</settings>

1.4 typeAliases标签

​ 为了不在任何地方都指定类的全限定名,我们可以使用 typeAliases 标签定义一个别名。分为两种方式:

  1. 对某个类起别名
<typeAliases>
    <!--方式一,逐个设置,大小写不受影响-->
    <typeAlias type="com.hqyj.cl.pojo.User" alias="User"/>
    <typeAlias type="com.hqyj.cl.pojo.Person" alias="Person"/>
</typeAliases>
  1. 对某个文件夹下的内容起别名
<typeAliases>
    <!--方式二,统一设置
    在没有注解@Alias()的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
    比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值-->
    <package name="com.hqyj.cl.pojo"/>
</typeAliases>

1.5 typeHandlers标签

​ typeHandlers 主要将获取的值合理地转化为 Java 类型。在 typeHandler 中,分为 jdbcType 和 javaType,其中 jdbcType 用于定义数据库类型,而 javaType 用于定义 Java 类型,typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换。我们不需要手动设置,因为MyBatis有默认类型处理器。

1.6 environments标签

​ 在 environments 标签中,可以配置 MyBatis 的多套运行环境,将 SQL 映射到多个不同的数据库上。必须指定一个默认的运行环境(通过default指定),目前我们只需要配置一套MySQL就行。

1.6.1 transactionManager标签

​ MyBatis 支持两个事务管理器,即 JDBC 和 MANAGED。我们选择JDBC。

1.6.2 dataSource标签

​ 用于配置数据库的连接属性,例如要连接的数据库的驱动程序名称、URL、用户名和密码等。有三个值(UNPOOLED、POOLED、JNDI),我们选择POOLED

<!--数据库连接源-->
<dataSource type="POOLED">
    <!--获取配置驱动-->
    <property name="driver" value="${driver}"/>
    <!--获取配置url-->
    <property name="url" value="${url}"/>
    <!--获取配置账号-->
    <property name="username" value="${username!=null?username:root}"/>
    <!--获取配置密码-->
    <property name="password" value="${password}"/>
</dataSource>

1.7resultMap映射(Mapper.xml文件中)

<!--定义结果集映射-->
<resultMap id="userMap" type="com.hqyj.pojo.User">
    <!--id主键 使用id标签-->
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
</resultMap>

1.9 mappers标签

​ mappers 标签用于指定 MyBatis SQL 映射文件的路径。

1.9.1 mapper标签

​ mapper 是 mappers 的子标签,mapper 中的 resource 属性用于指定 SQL 映射文件的路径(类资源路径),有多种方式,这里使用其中三种

<!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
<mappers>
    <!--使用相对于类路径的资源引用,这是相对于类路径,UserMapper.xml存在resources下的mapper包中-->
    <mapper resource="mapper/UserMapper.xml"/>

    <!-- 使用映射器接口实现类的完全限定类名 mapper文件必须和接口在同一个包下,类名和mapper文件名必须相同-->
    <mapper class="com.hqyj.cl.mapper.UserMapper"/>

    <!-- 将包内的映射器接口实现全部注册为映射器 只需要指定到包名即可,mapper文件必须和接口在同一个包下,接口名和mapper文件名必须相同-->
    <package name="com.hqyj.cl.mapper"/>
</mappers>

五、Mapper(映射器)

​ 映射器是 MyBatis 中最重要的文件,文件中包含一组 SQL 语句(例如查询、添加、删除、修改),这些语句称为映射语句或映射 SQL 语句。

映射器由 Java 接口和 XML 文件(或注解)共同组成,它的作用如下:

  • 定义参数类型
  • 配置缓存,默认是一级缓存
  • 提供 SQL 语句和动态 SQL
  • 定义查询结果和 POJO 的映射关系

映射器有以下两种实现方式

  • 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。推荐方式。
  • 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。

1、XML实现映射器

​ XML 定义映射器分为两个部分:接口和XML。

1.1 定义接口

public interface UserMapper {
    List<User> getUserList();
}

1.2 定义UserMapper.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">
<mapper namespace="com.hqyj.cl.mapper.UserMapper">
    <select id="getUserList" resultType="com.hqyj.cl.pojo.User">
        select * from `user`
    </select>
</mapper>

1.2.1 说明

  1. namespace 用来定义命名空间,该命名空间和定义接口的全限定名一致。
  2. <select> 元素表明这是一条查询语句,属性 id 用来标识这条 SQL。resultType 表示返回的是一个 User类型的值。

1.3 修改 MyBatis 配置文件

在 MyBatis 配置文件中添加以下代码

<!--该语句用来引入 XML 文件,MyBatis 会读取 UserMapper.xml 文件,生成映射器-->
<mapper resource="com/hqyj/cl/mapper/UserMapper.xml"/>

1.4 测试代码

@Test
public void getUserList() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 通过SqlSession对象获取StudentMapper的代理对象
    StudentMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 使用代理mapper对象查询全部的用户信息
    List<User> userList = mapper.getUserList();
    System.out.println(userList);
}

2、注解实现映射器

​ 使用注解的方式实现映射器,只需要在接口中使用 Java 注解,注入 SQL 即可

2.1 测试代码

public interface UserMapper {
    @Select(value = "select * from user")
    public List<User> getUserList();
}

2.1.1 说明

  1. 我们使用了 @Select 注解,并且注入了和 XML 中相同的 select 语句。
  2. 如果使用注解和 XML 文件两种方式同时定义,那么 XML 方式将覆盖掉注解方式。(只使用一种方式配置,有可能会报错)
  3. 简单的语句,可以使用注解方式,复杂语句,推荐使用XML方式。

3、MyBatis 映射器的主要元素

后续学习中会逐一讲解

元素名称 描述 备注
mapper 映射文件的根节点,只有 namescape 一个属性 namescape 作用如下:用于区分不同的 mapper,全局唯一绑定DAO接口,即面向接口编程。当 namescape 绑定某一接口后,可以不用写该接口的实现类,MyBatis 会通过接口的完整限定名查找到对应的 mapper 配置来执行 SQL 语句。因此 namescape 的命名必须要跟接口同名。
select 查询语句,最常用、最复杂的元素之一 可以自定义参数,返回结果集等
insert 插入语句 执行后返回一个整数,代表插入的条数
update 更新语句 执行后返回一个整数,代表更新的条数
delete 删除语句 执行后返回一个整数,代表删除的条数
parameterMap 定义参数映射关系 即将被删除的元素,不建议使用
sql 允许定义一部分的 SQL,然后在各个地方引用它 例如,一张表列名,我们可以一次定义,在多个 SQL 语句中使用
resultMap 用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素 提供映射规则
cache 配置给定命名空间的缓存 -
cache-ref 其它命名空间缓存配置的引用 -

六、MyBatis执行SQL的两种方式

MyBatis 有两种执行 SQL 语句的方式,如下:

  1. 通过 SqlSession 发送 SQL
  2. 通过 SqlSession 获取 Mapper 接口,通过 Mapper 接口发送 SQL

1、SqlSession

有映射器之后就可以通过 SqlSession 发送 SQL 了,MyBatis 常用的查询方法有 2 种,分别为 selectOne 和 selectList。

1.1 selectOne

selectOne 方法表示使用查询并且只返回一个对象,必须指定查询条件。只能查询 0 或 1 条记录,大于 1 条记录则运行错误。常用格式如下(也有其它重载方法,根据需要选择)

sqlSession.selectOne(String arg0, Object arg1)

1.2 selectList

selectList 方法表示使用查询并且返回一个列表。可以查询 0 或 N 条记录。常用格式如下

sqlSession.selectList(String arg0, Object arg1)

1.3 说明

  1. 以上语法格式中,String 对象由一个命名空间加 SQL id 组合而成,它完全定位了一条 SQL,这样 MyBatis 就会找到对应的 SQL。Object 对象为需要传递的参数,也就是查询条件
  2. selectOne 实现的 selectList 都可以实现,即 list 中只有一个对象。但 selectList 能实现的,selectOne 不一定能实现
  3. 如果 MyBatis 中只有一个 id 为 getUserList 的 SQL,那么可以去掉包路径名

2、Mapper接口

SqlSession 还可以获取 Mapper 接口,通过 Mapper 接口发送 SQL,如下所示

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();

2.1 说明

  1. 通过 SqlSession 的 getMapper 方法获取一个 Mapper 接口,然后就可以调用它的方法了。因为 XML 文件或者接口注解定义的 SQL 都可以通过“类的全限定名+方法名”查找,所以 MyBatis 会启用对应的 SQL 运行,并返回结果。

七、CRUD标签

​ 只有 select 标签有 resultType 属性,因为只有查询操作才需要对返回结果类型进行相应的指定。

1、select标签

在 MyBatis 中,select 标签是最常用也是功能最强大的 SQL 语言标签,用于执行查询操作。格式如下

<select id="queryUserByUsername" resultType="user" parameterType="string">
    select * from `user` where `username` like concat ('%',#{username},'%')
</select>

1.1 说明

  1. 执行 SQL 语句时可以定义参数,参数可以是一个简单的参数类型,例如 int、float、String;也可以是一个复杂的参数类型,例如JavaBean、Map 等。MyBatis 提供了强大的映射规则,执行 SQL 后,MyBatis 会将结果集自动映射到 JavaBean 中。
  2. 为了使数据库的查询结果和返回值类型中的属性能够自动匹配,通常会对 MySQL 数据库和 JavaBean 采用同一套命名规则,即 Java 命名驼峰规则,这样就不需要再做映射了(数据库表字段名和属性名不一致时需要手动映射)
  3. 参数的传递使用#{参数名},相当于告诉 MyBatis 生成 PreparedStatement 参数。

1.2 常用属性

属性名称 描 述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+id不唯一,那么 MyBatis 抛出异常
parameterType 表示传入 SQL 语句传入参数类型的全限定名或别名。它是一个可选属性,MyBatis 能推断出具体传入语句的参数 支持基本数据类型和 JavaBean、Map 等复杂数据类型
resultType SQL 语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一 -
resultMap 它是映射集的引用,与 <resultMap> 元素一起使用,返回时可以使用 resultType 或 resultMap 之一 是 MyBatis 最复杂的元素,可以配置映射规则、级联、typeHandler 等
flushCache 用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存 默认值为 false,如果设置为 true,则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存
useCache 启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中 -
timeout 用于设置超时参数,单位是秒(s),超时将抛出异常 -
fetchSize 获取记录的总条数设定 默认值是数据库厂商提供的 JDBC 驱动所设置的条数
statementType 告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、CALLABLE(CallableStatement) -
resultSetType 这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新) -

1.3 多参数

​ 有的时候,我们需要传递多个参数来执行SQL语句,比如用户登录;这个时候,有三种方式可以实现,分别是使用Map传递参数、使用注解传递参数、使用JavaBean传递参数;考虑到性能等问题,不推荐使用Map传递参数,所以,我们现在对后面两种情况进行分析

1.3.1 使用注解传递参数

使用 MyBatis 的注解 @Param() 传递参数,如下所示

1.3.1.2 案例
  1. 接口
//用户登录
User login(@Param("username")String username,@Param("password")String password);
  1. Mapper
<!--用户登录-->
<select id="login" resultType="User">
    select * from user where username = #{username} and password = #{password}
</select>
1.3.1.2 说明

​ 当我们把参数传递给后台时,MyBatis 通过 @Param 提供的名称就会知道 #{name} 代表 name 参数,提高了参数可读性。但是如果这条 SQL 拥有 10 个参数的查询,就会造成可读性下降,增强了代码复杂性。

1.3.2 使用JavaBean传递参数

在参数过多的情况下,MyBatis 允许组织一个 JavaBean,通过简单的 setter 和 getter 方法设置参数,提高可读性。如下所示

1.3.2.1 案例
  1. 接口
//增加用户
int selectUser(User user);
  1. Mapper
<!--增加用户-->
<select id="selectUser" resultType="User">
    select * from user where username = #{username} and password = #{password} and position = #{position}
</select>

1.3.3 区别

以上 2 种方式的区别如下。

  • 使用 @Param 注解传递参数会受到参数个数的影响。当 n≤5 时,它是最佳的传参方式,因为它更加直观;当 n>5 时,多个参数将给调用带来困难。
  • 当参数个数大于 5 个时,建议使用 JavaBean 方式。

2、insert标签

MyBatis insert 标签用来定义插入语句,执行插入操作。当 MyBatis 执行完一条插入语句后,就会返回其影响数据库的行数。

2.1 案例

通过insert标签实现添加用户功能

  1. 接口
//增加用户
int addUser(User user);
  1. Mapper
<!--增加用户-->
<insert id="addUser">
    insert into user values (null,#{username},#{password},#{userPhone},#{createTime},#{position})
</insert>

测试类

sqlSession.commit(); 需要commit

2.2 常用属性

属性名称 描述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
keyProperty 该属性的作用是将插入操作的返回值赋给 PO 类的某个属性,通常为主键对应的属性。如果是联合主键,可以将多个值用逗号隔开。 -
useGeneratedKe 该属性用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。 该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置执行该操作的最大时限,如果超时,就抛异常。 -
databaseId 取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过 <if test="_databaseId = 'oracle'"> 来为特定数据库指定不同的 sql 语句。 MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。
keyColumn 该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。 -

2.3 多参数

和select标签规则一样

3、update标签

MyBatis update 标签用于定义更新语句,执行更新操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

3.1 案例

  1. 接口
//通过用户姓名修改用户
int updateUserByUserName(User user);
  1. Mapper
<!--通过用户姓名修改用户-->
<update id="updateUserByUsername">
    update user set phone = #{phone},password = #{password} where username = #{username}
</update>

3.2 常用属性

属性名称 描述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 -
statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 -

3.3 多参数

和select标签规则一样

4、delete标签

MyBatis delete 标签用于定义 delete 语句,执行删除操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

4.1 案例

  1. 接口
//删除某个用户
int delUserById(int id);
  1. Mapper
<!--删除某个用户-->
<delete id="delUserById">
    delete from user where id = #{id}
</delete>

4.2 常用属性

属性名称 描述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 -
statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 -

4.3 多参数

和select标签规则一样

八、resultMap

​ resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。现有的 MyBatis 版本只支持 resultMap 查询,我们通过案例分析

1、构成

<resultMap id="" type="">
<constructor><!-- 类再实例化时用来注入结果到构造方法 -->
    <idArg/><!-- ID参数,结果为ID -->
    <arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/><!-- 用于表示哪个列是主键 -->
<result/><!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
    <case value=""/><!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>

1.1 说明

  • <resultMap>元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识
  • 子元素 <constructor> 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。一般不用
  • 子元素 <id> 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
  • 子元素<result> 用于表示 POJO 和 SQL 列名的映射关系。
  • 子元素 <association><collection><discriminator> 用在级联的情况下。后续学习

id 和 result 元素都有以下属性

元素 说明
property 映射到列结果的字段或属性。如果 POJO 的属性和 SQL 列名(column元素)是相同的,那么 MyBatis 就会映射到 POJO 上
column 对应 SQL 列
javaType 配置 Java 类型。可以是特定的类完全限定名或 MyBatis 上下文的别名
jdbcType 配置数据库类型。这是 JDBC 类型,MyBatis 已经为我们做了限定,基本支持所有常用数据库类型
typeHandler 类型处理器。允许你用特定的处理器来覆盖 MyBatis 默认的处理器。需要指定 jdbcType 和 javaType 相互转化的规则

1.2 获取结果集的两种方式

1.2.1 使用Map存储结果集

不推荐使用该方式

  1. 接口
// 用户列表
List<Map<String,Object>> queryAllUser();
  1. Mapper
<select id="selectAllUser" resultType="map">
    select * from `user`
</select>

**注意事项:**Map 的 key 是 select 语句查询的字段名(必须完全一样),而 Map 的 value 是查询返回结果中字段对应的值,一条记录映射到一个 Map 对象中。

1.2.2 使用POJO存储结果集

推荐使用该方式

  1. POJO类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String username;
    private String password;
}
  1. 接口
public interface UserMapper {
    List<User> getUserList();
}
  1. 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.hqyj.cl.dao.UserDao">
    <resultMap id="userMap" type="User">
        <id column="id" property="id"/>
        <result column="username" property="userName"/>
        <result column="password" property="password"/>
    </resultMap>
	<!--查询用户列表-->
    <select id="getUserList" resultMap="userMap">
        select * from `user`
    </select>
</mapper>
1.2.2.1 说明
  1. resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。
  2. 使用 property 元素指定 User的属性名称 username,column 表示数据库中 user表的 SQL 列名 username,将 POJO 和 SQL 的查询结果一 一对应。
  3. MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。
  4. resultMap 和 resultType 不能同时使用。

九、注解

为了简化 XML 的配置,MyBatis 提供了注解,这些注解主要分为三大类,即 SQL 语句映射、结果集映射和关系映射。我们就逐一学习

1、SQL 语句映射

1.1 @Insert

实现新增功能

@Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age})")
int addUser(User user);

1.2 @SelectKey

插入后,获取id的值

@Insert("insert into user(id,name) values(#{id},#{name})")
@SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int.class,before = false)
int insert(User user);

@SelectKey 各个属性含义如下

  • statement:表示要运行的 SQL 语句;
  • keyProperty:可选项,表示将查询结果赋值给代码中的哪个对象;
  • keyColumn:可选项,表示将查询结果赋值给数据表中的哪一列;
  • resultType:指定 SQL 语句的返回值;
  • before:默认值为 true,在执行插入语句之前,执行 select last_insert_id()。值为 flase,则在执行插入语句之后,执行 select last_insert_id()。

1.3 @Select

实现查询功能

增加templateMapper.xml文件在mapper包中 namespace属性空着

mybatis-config.xml中要扫描包

@Select("Select * from user")
@Results({
    @Result(id = true, column = "id", property = "id"),
    @Result(column = "name", property = "name"),
    @Result(column = "sex", property = "sex"),
    @Result(column = "age", property = "age")
})
List<User> queryAllUser();

@Results配置映射关系,如果mybatis-config.xml中关闭了驼峰命名,数据库字段与pojo类成员变量不能自动映射可以使用该注解

1.4 @Update

实现更新功能

@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
void updateUserById(User user);

1.5 @Delete

实现删除功能

@Delete("delete from  user  where id =#{id}")
void deleteById(Integer id);

1.6 @Param

用于在 Mapper 接口中映射多个参数,之前已经说明并演示

2、结果集映射

@Result、@Results、@ResultMap 是结果集映射的三大注解。

2.1 案例

@Select({"select id, name, class_id from student"})
@Results(id="studentMap", value={
    @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
    @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
    @Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAllStudent();

@Results 各个属性的含义

  • id:表示当前结果集声明的唯一标识,可以通过id重复利用;
  • value:表示结果集映射关系;
  • @Result:代表一个字段的映射关系。其中,column 指定数据库字段的名称,property 指定实体类属性的名称,jdbcType 数据库字段类型,id 为 true 表示主键,默认 false。

使用 @ResultMap 来引用映射结果集,其中 value 可省略

@Select({"select id, name, class_id from student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(int id);

3、关系映射

基础学生类增加成员变量修改数据类型,记得要重构有参构造方法。

3.1 @one

用于一对一关系映射

@Select("select * from stu")
@Results({
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),
   @Result(property="addr",column="fk_addr_id",one=@One(select="com.hqyj.cl.mapper.StudentMapper.getAddr"))
})
List<Student> getAllStudents();

@Select("select * from addr where id = #{id}")
@Results({
    @Result(id=true,property="id",column="id"),
    @Result(property="addrName",column="addr_name")
})
Addr getAddr(int id);

3.2 @many

用于一对多关系映射

@Select("select * from stu")
@Results({
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),
  @Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();

@Select("select * from teacher where id = #{id}")
@Results({
    @Result(id=true,property="id",column="id"),
    @Result(property="teacherName",column="teacher_name")
})
List<Teacher> getTeacher(int id);

3.3 多对多

在一对多的基础上,增加一个外表student_teacher,实现对student表和teacher表的关联

@Select("select * from stu")
@Results({
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),    @Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();

@Select("select * from teacher t,student_fk_teacher st where t.id = st.teacher_id and st.student_id = #{id}")
@Results({
    @Result(id=true,property="id",column="id"),
    @Result(property="teacherName",column="teacher_name"),
})
List<Teacher> getTeacher(int id);

十、MyBatis关联(级联)查询

级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。比如一个人对应一张身份证,一张身份证对应一个人、一个班级拥有多个学生,一个学生只能够属于某个班级、 一个学生可以选修多门课程,一个课程可以被多个学生选修。

1、一对一

在 MyBatis 中,通过 <resultMap> 元素的子元素 <association> 处理一对一级联关系。示例代码如下

POJO类和数据库设计,Student、Teacher、Addr三个类

  1. Student类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String studentName;
    private Addr addr;
    private List<Teacher> teacher;
}
  1. Addr类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Addr {
    private int id;
    private String addr;
}
  1. 接口
List<Student> selectAllStudent();
  1. 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.hqyj.cl.mapper.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <id property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <association property="addr">
            <id property="id" column="id"/>
            <result property="addr" column="addr"/>
        </association>
    </resultMap>

    <select id="selectAllStudent" resultMap="studentMap">
        select * from `stu` s,`addr` a where s.fk_addr_id = a.id 
    </select>

</mapper>
  1. 测试
@Test
public void getAllUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.selectAllStudent();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(studentList);
}

<association> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

2、一对多

在 MyBatis 中,通过 <resultMap> 元素的子元素 <collection> 处理一对多级联关系,collection 可以将关联查询的多条记录映射到一个 list 集合属性中。示例代码如下

  1. Teacher类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String teacherName;
}
  1. 接口
List<Student> selectAllStudent();
  1. 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.hqyj.cl.mapper.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <id property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <collection property="teacher" ofType="teacher">
            <id property="id" column="id"/>
            <result property="teacherName" column="teacher_name"/>
        </collection>
    </resultMap>

    <select id="selectAllStudent" resultMap="studentMap">
        select * from `stu` s,`addr` a,`teacher` t where s.fk_teacher_id = t.id
    </select>

</mapper>
  1. 测试
@Test
public void getAllUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.selectAllStudent();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(studentList);
}

<collection> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

3、多对多

​ 实际应用中,由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。

十一、MyBatis动态SQL

​ 动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。动态 SQL 大大减少了编写代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。MyBatis 也可以在注解中配置 SQL,但是由于注解功能受限,且对于复杂的 SQL 语句来说可读性差,所以使用较少。

1、动态SQL元素

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

元素 作用 备注
if 判断语句 单条件分支判断
choose(when、otherwise) 相当于 Java 中的 switch case 语句 多条件分支判断
trim、where 辅助元素 用于处理一些SQL拼装问题
foreach 循环语句 在in语句等列举条件常用
bind 辅助元素 拼接参数

1.1 if标签

MyBatis if 类似于 Java 中的 if 语句,是 MyBatis 中最常用的判断语句。

1.1.1 格式

<if test="判断条件">
    <!--条件为true时执行SQL语句-->
    SQL语句
</if>

1.1.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user where 1=1
    <if test="name != null">
        and `name` like #{name}
    </if>
    <if test="state != 0">
        and state like #{state}
    </if>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("王五");
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.2 choose、when和otherwise标签

​ MyBatis 中动态语句 choose-when-otherwise 类似于 Java 中的 switch-case-default 语句。由于 MyBatis 并没有为 if 提供对应的 else 标签,如果想要达到<if><else></else> </if> 的效果,可以借助 <choose><when><otherwise> 来实现。

1.2.1 格式

​ choose 标签按顺序判断其内部 when 标签中的判断条件是否成立,如果有一个成立,则执行相应的 SQL 语句,choose 执行结束;如果都不成立,执行 otherwise 中的 SQL 语句。这类似于 Java 的 switch 语句,choose为switch,when 为 case,otherwise则为default。

<choose>
    <when test="判断条件1">
        SQL语句1
    </when >
    <when test="判断条件2">
        SQL语句2
    </when >
    <when test="判断条件3">
        SQL语句3
    </when >
    <otherwise>
        SQL语句4
    </otherwise>
</choose>

1.2.1 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user where 1=1
    <choose>
        <when test="name != null and name !=''">
            AND name LIKE CONCAT('%',#{name},'%')
        </when>
        <when test="state != 0 and state == 1">
            AND state = #{state}
        </when>
        <otherwise>
            AND password is not null
        </otherwise>
    </choose>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.3 where标签

​ 在上述标签SQL代码的编写中,我们不难发现,每一条SQL 语句中加入了一个条件“1=1”,如果没有加入这个条件,那么可能就会变成下面这样一条错误的语句。

SELECT * FROM user AND name LIKE CONCAT('%',#{name},'%')

显然以上语句会出现 SQL 语法异常,但加入“1=1”这样的条件又非常奇怪,所以 MyBatis 提供了 where 标签

1.3.1 格式

where 标签主要用来简化 SQL 语句中的条件判断,可以自动处理 AND/OR 条件,语法如下

<where>
    <if test="判断条件">
        AND/OR ...
    </if>
</where>

**注意事项:if 语句中判断条件为 true 时,where 关键字才会加入到组装的 SQL 里面,否则就不加入。where 会检索语句,它会将 where 后的第一个 SQL 条件语句**的 AND 或者 OR 关键词去掉。

1.3.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user
    <where>
        <if test="name != null">
            name like #{name}
        </if>
        <if test="state != 0">
            AND state like #{state}
        </if>
    </where>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
	user.setName("admin");
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.4 set标签

​ 在 Mybatis 中,update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号

1.4.1 案例

  1. 接口
int updateUser(User user);
  1. Mapper
<update id="updateUser">
    UPDATE user
    <set>
        <if test="password!=null">
            password=#{password}
        </if>
        <if test="state==0 || state==1">
            state=#{state}
        </if>
    </set>
    WHERE name=#{name}
</update>
  1. 测试
@Test
public void updateUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("admin");
    user.setState(1);
    int result = mapper.updateUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(result);
}

**注意事项:**if判断中必须成立一个,并且where后面的条件对应的值必须传递,否则报错

1.5 foreach标签

​ 对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。 Mybatis foreach 标签用于循环语句,它很好的支持了数据和 List、set 接口的集合,并对此提供遍历的功能。语法格式如下

<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
    参数值
</foreach>

foreach 标签主要有以下属性,说明如下。

  • item:表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素;若collection为map,则代表key-value的value,该参数为必选
  • index:在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。
  • open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
  • separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
  • close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。
  • collection表示迭代集合的名称,可以使用@Param注解指定,有以下四种情况
    • 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list;
    • 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array;
    • 如果如果传入的是单参数且参数类型是一个Set集合的时候,可以通过注解@Param("set")指定key值;
    • 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。必须加上注解@Param指定

1.5.1 案例

  1. 接口
List<User> selectUser(List<Integer> ageList);
  1. Mapper
<select id="selectUser" resultMap="userMap">
    SELECT * FROM user WHERE age in
    <foreach item="age" index="index" collection="list" open="("
             separator="," close=")">
        #{age}
    </foreach>
</select>
  1. 测试
@Test
public void selectUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<Integer> ageList = new ArrayList<>();
    ageList.add(1);
    ageList.add(12);
    ageList.add(13);
    List<User> result = mapper.selectUser(ageList);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(result);
}

1.6 bind标签

​ 每个数据库的拼接函数或连接符号都不同,例如 MySQL 的 concat 函数。这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 bind 标签来解决这一问题。

MyBatis bind 标签可以通过 OGNL(对象导航图语言) 表达式自定义一个上下文变量。

bind 元素属性如下

  • value:对应传入实体类的某个字段,可以进行字符串拼接等特殊处理。
  • name:给对应参数取的别名。

1.6.1 案例

  1. 接口
List<User> queryUser(String name);
  1. Mapper
<select id="queryUser" resultMap="userMap">
    <bind name="pattern" value="'%'+_parameter+'%'" />
    SELECT * FROM `user`
    WHERE name like #{pattern} 
</select>
  1. 测试
@Test
public void queryUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.queryUser("a");
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

**注意事项:**以上代码中的“_parameter”代表传递进来的参数,它和通配符连接后,赋给了 pattern,然后就可以在 select 语句中使用这个变量进行模糊查询,提高了可移植性。

1.7 trim标签

​ 在 MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。

1.7.1 格式

<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
    SQL语句
</trim>
1.7.1.1 属性说明
属性 描述
prefix 给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
suffix 给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
prefixOverrides 去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。
suffixOverrides 去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定

1.7.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user
    <trim prefix="where" prefixOverrides="and">
        <if test="name != null">
            and `name` like #{name}
        </if>
        <if test="state != 0">
            and state like #{state}
        </if>
    </trim>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

十二、MyBatis分页功能

MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果。

1、pageHelper插件

在使用MyBatis分页功能时,通常和pageHelper插件配合使用

1.1 参数

PageInfo{
pageNum=1, 	//页数
pageSize=2, 	//页大小
size=2, 大小
startRow=1, 	//开始行
endRow=2, 	//结束行
total=6, 		//总条数
pages=3, 		//总页数
list=Page{count=true, pageNum=1, pageSize=2, startRow=0, endRow=2, total=6, pages=3, reasonable=false, pageSizeZero=false}, 
prePage=0, 	//首页
nextPage=2, 	//下一页
isFirstPage=true, 	//是第一页
isLastPage=false,	// 是最后一页
hasPreviousPage=false, 	//有上一页
hasNextPage=true, 		//有下一页
navigatePages=2, 		//导航页数
navigateFirstPage=1, 	//导航到第一页
navigateLastPage=2,  	//导航到最后一页
navigatepageNums=[1, 2]}	//导航 页数 页码

1.2 使用步骤

  1. 导入依赖
<!--pageHelper-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.4</version>
</dependency>
  1. 配置mybatis-config.xml

在mybatis-config.xml中配置,注意顺序问题

<!-- 配置分页插件 -->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!-- 设置数据库类型-->
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>
  1. 接口
List<User> queryAllUser();
  1. Mapper

一定注意,不要在SQL语句后面加分号,否则MyBatis不能动态的正确拼接limt 分页语句

<select id="queryAllUser" resultMap="userMap">
    select * from user
</select>
  1. 测试
@Test
public void queryAllUser() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 开启分页,pageNum表示当前页码,pageSize表示每页数据条数
    PageHelper.startPage(2,2);
    // 获取数据
    List<User> userList = mapper.queryAllUser();
    // 把数据封装在pageInfo对象中,后期可以通过这种方式返回给前端,进行分页展示
    PageInfo<User> userPageInfo = new PageInfo<>(userList);
    System.out.println(userPageInfo);
}

十三、log4j

​ 在使用SSM进行开发时, 随着代码量的增大, 配置信息的繁杂, 排错也随之变得越来越难. 而日志就是我们debug的有力工具,常用的日志有很多, 我们选择使用log4j,下文介绍log4j在MyBatis下的简单应用

1、使用步骤

  1. 导入依赖
<!--log4j-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 配置mybatis-config.xml

在mybatis-config.xml中配置,注意顺序问题

<settings>
    <!-- 配置log4j日志 -->
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. 创建 文件(直接在resource文件夹下创建)
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

十四、MyBatis缓存

​ 缓存可以将数据保存在内存中,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 开启二级缓存,但是并不是意味着我们之前写的所有sql都是二级缓存,要使用二级缓存需要在xml文件中使用<cache/>标签指出。

下面案例是演示配置了日志系统后,当你查询的条件一摸一样的时候只执行了一次sql。

1、一级缓存

  1. 一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
  2. 在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
  3. 由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。

1.1 案例

  1. 保证日志开启,方便查看输出数据
  2. 接口
List<User> getUserList();
  1. Mapper
<select id="queryAllUser" resultMap="userMap">
    select * from user
</select>
  1. 测试
@Test
public void getUserList() throws IOException {
        // 读取配置文件mybatis-config.xml
        InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
        // 根据配置文件构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
        // 通过SqlSessionFactory创建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession执行文件中定义的SQL,并返回映射结果
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        System.out.println(userList);
        List<User> userList1 = mapper.getUserList();
        System.out.println(userList1);
        System.out.println(userList1==userList);// true
}

日志打印信息

[com.hqyj.cl.mapper.UserMapper.getUserList]-==>  Preparing: select * from `user` 
[com.hqyj.cl.mapper.UserMapper.getUserList]-==> Parameters: 
[com.hqyj.cl.mapper.UserMapper.getUserList]-<==      Total: 3
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]
===========================================================================================================
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]

1.2 缓存失效

  • 查询不同的东西,比如同一个SQL语句,但是传递的参数不同。
  • 查询不同的Mapper.xml。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存,也就是说,在两条相同语句中间插入一条其他语句(比如inset),缓存会刷新。
  • 手动清理缓存。sqlSession.clearCache()
  • 把第一个创建的sqlSession通过sqlSession.close()方法关闭后,再创建一个sqlSession,也无法实现缓存。

1.3 总结

一级缓存基本上没啥用,因为用户执行的操作出现一模一样的情况很少;通过debug可以发现,其实就是把查询出来的值存入Map集合。

2、二级缓存

二级缓存就是与namespace级别的缓存,一个名称空间对应一个缓存。

2.1 工作机制

​ 一个会话查询一条数据,这个数据会存放在当前会话的一级缓存中,当会话结束时,一级缓存就会消失,但是我们可以把当前会话数据保存在二级缓存中,新的会话查询信息就可以从二级缓存中获取内容,当然,不同的mapper查询的数据都会放在自己对应的缓存中。

2.2 格式

要启用全局的二级缓存,只需要在你的 SQL 映射文件中(XXXMapper.xml)添加一行<cache/>,比如

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

上述例子创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

配置详解

  • flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
  • size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
  • readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

2.3 可用的清除策略

  • LRU – 最近最少使用:移除最长时间不被使用的对象默认方式。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

2.4 注意事项

  • 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新;
  • 不能使用封装获取sqlSession对象的类;

2.5 案例

  1. 开启全局缓存(mybatis-config.xml)配置二级缓存后可以尝试不同的sqlsession执行方法,控制台只打印一条sql语句,证明

    是从缓存中取出数据

<!--开启全局缓存,默认也是开启的,但是我们一般显示定义-->
<setting name="cacheEnabled" value="true"/>

**注意事项:**在CRUD标签中,可以使用useCache属性指定是否启用二级缓存,默认true。

  1. 接口
List<User> getUserList();
  1. mapper
<!--开启二级缓存-->
<cache
       eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>
  1. 测试
@Test
public void getUserList() throws IOException {
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserList();
    System.out.println(userList);
    // 关闭第一个sqlSession连接,它会把缓存放入二级缓存中
    sqlSession.close();
    System.out.println("=======================================================");
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = mapper1.getUserList();
    System.out.println(userList1);
    System.out.println(userList1==userList);// true
    // 关闭第二个sqlSession连接
    sqlSession1.close();
}

2.6 错误及解决

  1. 错误

如果我们在mapper文件中,直接使用<cache/>方式开启二级缓存,会报错org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: com.hqyj.cl.pojo.User

  1. 解决

因为要使用二级缓存需要把对应的类序列化,所以解决办法如下

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private int id;
    private String name;
    private String password;
    private int age;
    private int deptId;
    private int state;
}

十五、MyBatis逆向工程

Mybatis 提供了一个逆向工程工具,该工具可以根据数据表自动生成针对单表的 pojo 类、mapper 映射文件和 mapper 接口。

1、使用步骤

  1. 创建数据库
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `gender` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `role` varchar(255) NOT NULL DEFAULT '用户',
  `state` int(255) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
  1. 创建项目

新建项目,并且新建资源文件夹 config,在 config 文件夹下创建 generatorConfig.xml 文件,用于配置及指定数据库及表等。

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/servletdemo" userId="root"
                        password="112112" />

        <!-- 默认为false,把JDBC DECIMAL和NUMERIC类型解析为Integer;为true时 把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成POJO类的位置 -->
        <javaModelGenerator
                targetPackage="com.hqyj.cl.pojo" targetProject="D:workspacemybatismybatis_03srcmainjava">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.hqyj.cl.mapper"
                         targetProject="D:workspacemybatismybatis_03srcmainjava">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>

        <!-- targetProject:mapper接口生成的的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.hqyj.cl.mapper" targetProject="D:workspacemybatismybatis_03srcmainjava">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <!-- 指定数据表 -->
        <table tableName="user"></table>
    </context>
</generatorConfiguration>

注意事项:targetProject写成绝对路径,否则可能出现路径不对,加载不出文件的情况

  1. 导入依赖
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.0</version>
</dependency>
  1. GeneratorSqlmap 类
package net.biancheng;

import java.io.File;
import java.util.*;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlMap {
    public void generator() throws Exception {
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;
        // 指定配置文件
        File configFile = new File("D:\workspace\mybatis\mybatis_03\src\main\resources\config\generator-config.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }

    // 执行main方法以生成代码
    public static void main(String[] args) {
        try {
            GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意事项:FILE文件路径写成绝对路径,相对路径可能存在路径不对的情况

2、说明

在 pojo 包中,有一部分是名字为 XxxExample 的类。类中包含以下 3 个成员变量

protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;;

以上变量说明如下:

  • distinct 字段用于指定 DISTINCT 查询。
  • orderByClause 字段用于指定 ORDER BY 条件,这个条件没有构造方法,直接通过传递字符串值指定。
  • oredCriteria 字段用于自定义查询条件。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇

)">
下一篇>>