MyBatis 学习笔记

Mybatis官方参考网址:https://mybatis.org/mybatis-3/zh/

MyBatis 是一款持久层框架,用于简化 JDBC 开发。

将硬编码写道配置文件,繁琐操作自动完成。

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

快速入门

已有数据库

1
2
3
4
5
6
7
8
9
10
11
mysql> use mybatis
Database changed
mysql> select * from tb_user;
+----+----------+----------+--------+------+
| id | username | password | gender | addr |
+----+----------+----------+--------+------+
| 1 | zhangsan | 123 | 男 | 北京 |
| 2 | 李四 | 234 | 女 | 天津 |
| 3 | 王五 | 11 | 男 | 西安 |
+----+----------+----------+--------+------+
3 rows in set (0.01 sec)

maven配置依赖

先在maven添加依赖,根据情况添加

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- mybatis 的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>

<!-- mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>

配置mybatis参数文件

在resources下创建mybatis-config.xml 文件,填好连接信息和映射文件路径

此文件写法可直接在mybatis官网寻找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 连接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加载sql的映射文件 -->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>

配置mapper映射文件

在resources下创建UserMapper.xml的映射文件,namespace需要自己设置,<select> 下 id 代表指令id, resultType指向对应的class

此文件写法可直接在mybatis官网寻找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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">


<!--
namespace: 名称空间
id 为指令的id
resultType 指向的是一个class
-->
<mapper namespace="test">
<select id="selectAll" resultType="com.pojo.user">
select * from tb_user;
</select>
</mapper>

执行

对应class类如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.pojo;

public class user {
private Integer id;
private String username;
private String password;
private String gender;
private String addr;

@Override
public String toString() {
return "user{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", addr='" + addr + '\'' +
'}';
}

public Integer getId() {
return id;
}

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

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getGender() {
return gender;
}

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

public String getAddr() {
return addr;
}

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

主程序如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws Exception {
// 1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
// 下面代码可以直接从mybatis的官网找
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();

// 执行sql
// 参数为sql的映射文件map 的 "命名空间.id"
// 返回值为List包装好的类
// 这里可以用mapper代理的形式实现
List<user> users = sqlSession.selectList("test.selectAll");

System.out.println(users);

// 释放资源
sqlSession.close();

}

Mapper 代理开发

在resources下创建和接口同名、同路径的对应xml文件。

xml的namespace为接口类的位置,select id 为 接口中定义的函数。

修改mybatis-config.xml内sql映射文件路径。

调用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void main(String[] args) throws Exception {
// 1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
// 下面代码可以直接从mybatis的官网找
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();

// 执行sql
// 参数为sql的映射文件map 的 "命名空间.id"
// 返回值为List包装好的类
//List<user> users = sqlSession.selectList("test.selectAll");

// 获取UserMapper接口的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<user> users = mapper.selectAll();

System.out.println(users);

// 释放资源
sqlSession.close();

}

编写流程

idea 有一个插件 MyBatisX ,方便开发

  • 编写接口方法:Mapper 接口
  • 编写SQL语句:SQL映射文件
  • 执行方法,测试

在查询过程中,class类的名称和sql的字段名不一致,会导致封装没有值。

解决方法:

  • 起别名:可以在sql语句中起别名,别名和class类中对应字段相同即可。
    • 缺点:每次查询都要定义一次别名。
  • 定义sql片段。 缺点:不灵活。
  • resultMap(最常用)

起别名 如:

1
2
3
4
5
6
7
<mapper namespace="com.mapper.BrandMapper">
<select id="selectAll" resultType="com.pojo.Brand">
<!-- sql里面是下划线命名,java类里面是驼峰命名。 -->
select id, brand_name as brandName, company_name as companyName, ordered, description, status
from tb_brand;
</select>
</mapper>

定义sql 如:

1
2
3
4
5
6
7
8
<sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>

<select id="selectAll" resultType="com.pojo.Brand">
select <include refid="brand_column"/>
from tb_brand;
</select>

resultMap 如:

1
2
3
4
5
6
7
8
9
10
11
12
  <resultMap id="brandResultMap" type="com.pojo.Brand">
<!-- result 对应一般字段, id 对应 id 字段
column:表的列名 property:实体类的属性名
-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>

<select id="selectAll" resultMap="brandResultMap">
select *
from tb_brand;
</select>

参数占位符:

  • #{}:会将其替换成 ? ,防止SQL注入 。

  • ${}:拼接sql,会存在SQL注入问题。

  • 参数传递的时候使用 : #{}

  • 表名或列名不固定时:¥{}

参数类型设置: parameterType = " " (可以省略)。如下:

1
2
3
<select id="selectById" parameterType="int" resultMap="brandResultMap">
select * from tb_brand where id = #{id};
</select>

特殊字符的处理:

  • 转义字符
  • CDATA区:如下: idea 输入CD就会提示
1
2
3
4
<!-- 小于号 -->		
<![CDATA[
<
]]>

多参数查询

BrandMapper 接口中定义多条件查询的方法。

而该功能有三个参数,我们就需要考虑定义接口时,参数应该如何定义。Mybatis针对多参数有多种实现

  • 使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位

    1
    List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);
  • 将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和实体类属性名保持一致。

    1
    List<Brand> selectByCondition(Brand brand);
  • 将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。

    1
    List<Brand> selectByCondition(Map map);

动态SQL

上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的SQL语句就不能那样写的

例如用户只输入 当前状态 时,SQL语句就是

1
select * from tb_brand where status = #{status}

而用户如果只输入企业名称时,SQL语句就是

1
select * from tb_brand where company_name like #{companName}

而用户如果输入了 当前状态企业名称 时,SQL语句又不一样

1
select * from tb_brand where status = #{status} and company_name like #{companName}

针对上述的需要,Mybatis对动态SQL有很强大的支撑:

  • if

  • choose (when, otherwise)

  • trim (where, set)

  • foreach

  • if 标签:条件判断

    • test 属性:逻辑表达式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <select id="selectByCondition" resultMap="brandResultMap">
    select *
    from tb_brand
    where
    <if test="status != null">
    and status = #{status}
    </if>
    <if test="companyName != null and companyName != '' ">
    and company_name like #{companyName}
    </if>
    <if test="brandName != null and brandName != '' ">
    and brand_name like #{brandName}
    </if>
    </select>

    如上的这种SQL语句就会根据传递的参数值进行动态的拼接。如果此时status和companyName有值那么就会值拼接这两个条件。

  • where 标签

    • 作用:
      • 替换where关键字
      • 会动态的去掉第一个条件前的 and
      • 如果所有的参数没有值则不加where关键字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
<where>
<if test="status != null">
and status = #{status}
</if>
<if test="companyName != null and companyName != '' ">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != '' ">
and brand_name like #{brandName}
</if>
</where>
</select>

注意:需要给每个条件前都加上 and 关键字。

单一查询

<choose> 相当于switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
<where>
<choose><!--相当于switch-->
<when test="status != null"><!--相当于case-->
status = #{status}
</when>
<when test="companyName != null and companyName != '' "><!--相当于case-->
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like #{brandName}
</when>
</choose>
</where>
</select>

Mybatis参数传递

Mybatis 接口方法中可以接收各种各样的参数,如下:

  • 多个参数
  • 单个参数:单个参数又可以是如下类型
    • POJO 类型
    • Map 集合类型
    • Collection 集合类型
    • List 集合类型
    • Array 类型
    • 其他类型

多个参数

如下面的代码,就是接收两个参数,而接收多个参数需要使用 @Param 注解,那么为什么要加该注解呢?这个问题要弄明白就必须来研究Mybatis 底层对于这些参数是如何处理的。

1
User select(@Param("username") String username,@Param("password") String password);
1
2
3
4
5
6
7
<select id="select" resultType="user">
select *
from tb_user
where
username=#{username}
and password=#{password}
</select>

我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:

  • 以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:

    map.put("arg0",参数值1);

    map.put("arg1",参数值2);

  • 以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:

    map.put("param1",参数值1);

    map.put("param2",参数值2);

==结论:以后接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。==

单个参数

  • POJO 类型

    直接使用。要求 属性名参数占位符名称 一致

  • Map 集合类型

    直接使用。要求 map集合的键名参数占位符名称 一致

  • Collection 集合类型

    Mybatis 会将集合封装到 map 集合中,如下:

    map.put("arg0",collection集合);

    map.put("collection",collection集合;

    ==可以使用 @Param 注解替换map集合中默认的 arg 键名。==

  • List 集合类型

    Mybatis 会将集合封装到 map 集合中,如下:

    map.put("arg0",list集合);

    map.put("collection",list集合);

    map.put("list",list集合);

    ==可以使用 @Param 注解替换map集合中默认的 arg 键名。==

  • Array 类型

    Mybatis 会将集合封装到 map 集合中,如下:

    map.put("arg0",数组);

    map.put("array",数组);

    ==可以使用 @Param 注解替换map集合中默认的 arg 键名。==

  • 其他类型

    比如int类型,参数占位符名称 叫什么都可以。尽量做到见名知意

注解开发

使用注解开发会比配置文件开发更加方便

使用注解开发会比配置文件开发更加方便。如下就是使用注解进行开发

1
2
@Select(value = "select * from tb_user where id = #{id}")
public User select(int id);

==注意:==

  • 注解是用来替换映射配置文件方式配置的,所以使用了注解,就不需要再映射配置文件中书写对应的 statement

Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:

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