持久化框架--Mybatis的xml中到底有什么?

This article was last updated on <span id="expire-date"></span> days ago, the information described in the article may be outdated.

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

在SpringBoot框架的基础之上导入相关启动器依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>

在 application.yml中简单配置数据库地址池与Mybatis

1
2
3
4
5
6
7
8
9
10
11
12
spring:
datasource:
url: jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=utf-8
username: root
password: root

# MyBatis
mybatis:
# 实体类包路径
typeAliasesPackage: com.example.**.entity
# mapper.xml 文件地址
mapperLocations: classpath*:mapper/**/*Mapper.xml

创建实体类

1
2
3
4
5
6
7
public class Student {
private Integer id;
private String name;
private String age;
private String address;
// 省略set与get方法
}

mapper接口创建

1
2
3
4
5
6
public interface StudentMapper {
public int insertName();
public int deleteinfo();
public int updateName();
public Student selectName();
}

在resources下创建Mapper.StudentMapper.xml文件

导入Mybatis头文件

1
2
3
4
<?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用于绑定Mapper的接口

1
2
3
4
5
6
7
8
<mapper namespace="com.example.demo.Mapper.StudentMapper">
<resultMap type="Student" id="resultMapId">
<result property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<result property="address" column="address" />
</resultMap>
</mapper>

结果映射将结果映射给Java对象

标签详细介绍

resultMap标签用于定义结果映射规则,将查询结果映射到Java对象。其常用属性如下:

  • id:标识符,用于在配置文件中引用该结果映射规则。映射数据表的主键ID,
  • type:指定映射结果的Java类型。
  • result:标识符,用于在配置文件中引用该结果映射规则。注入到字段或 JavaBean 属性的结果
  • property:column 指定的列进行映射的 JavaBean 变量名称。(Java实体类字段名)
  • column:用来指定我们需要将那一列进行和 JavaBean 属性映射。(数据库字段名)

添加基本的sql操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<mapper namespace="com.example.demo.Mapper.StudentMapper">
<resultMap type="Student" id="resultMapId">
<id property="id" column="student_id"/>
<result property="name" column="student_name"/>
<result property="age" column="student_age"/>
<result property="address" column="student_address"/>
</resultMap>

<insert id="insertName">
INSERT INTO student (student_name,student_age,student_address) VALUES (#{name}, #{age}, #{address})
</insert>
<update id="updateName">
UPDATE student SET student_name = #{name}, student_age = #{age} WHERE student_id = #{id}
</update>
<delete id="deleteinfo">
DELETE FROM student WHERE student_id = #{id}
</delete>
<select id="selectName" resultType="com.example.demo.entity.Student">
SELECT * FROM student WHERE student_id = #{id}
</select>
</mapper>
标签详细介绍
  • <select>:插入操作的标签
  • <update>:更新操作的标签
  • <delete>:删除操作的标签
  • <select>:查询操作的标签
  • id:与Mapper方法名对应,使用此id调用SQL语句
  • resultType:指定 SQL 语句执行后返回的结果类型,Java对象或者基本数据类型
  • parameterType:删除操作的标签
#{}与${}的区别
  • #{}:占位符, 可以防止 SQL 注入攻击,并且会自动进行参数类型转换和字符转义,相对安全。#{} 在执行前会被预编译成一个占位符,MyBatis 会将 SQL 语句中的 #{} 部分替换为问号(?),然后将参数值通过 PreparedStatement ( Java 中用于执行预编译 SQL 语句的接口,它可以确保参数值被安全地传递给数据库,并且不会受到恶意输入的影响)的方式绑定到这些问号上。它可以确保参数值被安全地传递给数据库,因此可以防止 SQL 注入。
  • ${}:字符串替换,在 SQL 语句中使用 ${} 会直接将参数的值拼接到 SQL 语句中,存在 SQL 注入的风险。

一对一映射

一实体对应一实体相关联。

改动下原来的实体类,并添加子映射实体类

1
2
3
4
5
6
7
8
public class Student {
private Integer id;
private String name;
private String age;
private String address;
private IdCard idCard; //
//   省略set与get方法
}
1
2
3
4
5
public class IdCard {
private Integer idCardId;
private String idCardNumber;
//   省略set与get方法
}
1
2
3
4
5
6
7
public interface StudentMapper {
public int insertName();
public int deleteinfo();
public int updateName();
public Student selectName();
public Student StudentWithIdCard();
}

创建新的Mapper接口

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
<mapper namespace="com.example.demo.Mapper.StudentMapper">
<resultMap type="Student" id="resultMapIdOne">
<!-- Student映射 -->
<id property="id" column="student_id"/>
<result property="name" column="student_name"/>
<result property="age" column="student_age"/>
<result property="address" column="student_address"/>

<!-- 一对一关联映射 身份证 -->
<association property="idCard" javaType="IdCard">
<!-- idCard字段映射 -->
<id property="idCardId" column="id_card_id"/>
<result property="idCardNumber" column="id_card_number"/>
</association>
</resultMap>

<insert id="insertName">
INSERT INTO student (student_name,student_age,student_address) VALUES (#{name}, #{age}, #{address})
</insert>
<update id="updateName">
UPDATE student SET student_name = #{name}, student_age = #{age} WHERE student_id = #{id}
</update>
<delete id="deleteinfo">
DELETE FROM student WHERE student_id = #{id}
</delete>
<select id="selectName" resultType="com.example.demo.entity.Student">
SELECT * FROM student WHERE student_id = #{id}
</select>

<!-- 查询学生及其身份证信息 resultMap对应<resultMap>标签id,处理复杂的映射规则 -->
<select id="StudentWithIdCard" resultMap="resultMapIdOne">
SELECT
s.student_id,
s.student_name,
s.student_age,
s.student_address,
i.id_card_id,
i.id_card_number
FROM student s
LEFT JOIN id_card i ON s.id_card_id = i.id_card_id
WHERE s.student_id = #{id}
</select>
</mapper>

使用LEFT JOIN将student与id_card表进行关联,使用字段id将其关联,将select标签内查询出来的数据通过resultMap处理映射,使用resultMap进行指定。

Tip

查询到的数据会映射到相应的实体类,此时,Student中除原本属性外idCard中的数据将Set到IdCard对象中,”一对象“对应另一个对象中的”其中一组数据

一对多映射

一实体对应一实体多条数据,甚至多个实体多条记录

改动原实体类

Student中添加了LIst<>属性,它代表学生与学生证、学生与教师与之间的一对多关系。

1
2
3
4
5
6
7
8
9
public class Student {
private Integer id;
private String name;
private String age;
private String address;
private List<IdCard> idCardList;
private List<Teacher> teacherList;
//   省略 getter 和 setter 方法
}
1
2
3
4
5
public class IdCard {
private Integer idCardId;
private String idCardNumber;
//   省略 getter 和 setter 方法
}

添加新的实体类Teacher

1
2
3
4
5
public class Teacher {
private Integer teacherId;
private String teacherName;
// 省略 getter 和 setter 方法
}
1
2
3
4
5
6
7
8
9
public interface StudentMapper {
public int insertName();
public int deleteinfo();
public int updateName();
public Student selectName();
public Student StudentWithIdCard();
// 查询所有学生及其相关信息(包括学生证和教师)
List<Student> StudentsWithDetailsAll();
}
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
<mapper namespace="com.example.demo.Mapper.StudentMapper">
<resultMap type="Student" id="resultMapIdTwo">
<id property="id" column="student_id"/>
<result property="name" column="student_name"/>
<result property="age" column="student_age"/>
<result property="address" column="student_address"/>

<!-- 一对多映射,身份证 -->
<collection property="idCardList" ofType="IdCard">
<id property="idCardId" column="id_card_id"/>
<result property="idCardNumber" column="id_card_number"/>
<!-- 其他属性映射 -->
</collection>

<!-- 一对多关联映射,教师 -->
<collection property="teacherList" ofType="Teacher">
<id property="teacherId" column="teacher_id"/>
<result property="teacherName" column="teacher_name"/>
<!-- 其他属性映射 -->
</collection>
</resultMap>

<!-- 查询所有学生及其相关信息(包括身份证和教师) 映射到resultMapIdTwo -->
<select id="StudentsWithDetailsAll" resultMap="resultMapIdTwo">
SELECT
s.student_id,
s.student_name,
s.student_age
s.student_address,
i.id_card_id,
i.id_card_number,
t.teacher_id,
t.teacher_name
FROM student s
LEFT JOIN id_card i ON s.student_id = i.student_id
LEFT JOIN teacher t ON s.student_id = t.student_id;
</select>
</mapper>

使用student_id将三张表进行关联,student表映射到Student类,idCard映射到idCardList中,teacher映射到teacherList中,实现一实体对应多个实体的关系

association与collection标签

注意我分别使用了association与collection它们在两个不同的内对应id分别为resultMapIdOne与resultMapIdTwo

两个元素都用于处理关联关系

动态拼接与格式化标签

在resultMapIdOne映射基础上对基本语句进行调整,添加Mybatis标签使用

改动mapper接口,改变insertName接收

1
2
3
4
5
6
7
8
9
public interface StudentMapper {
public int insertName(@Param("students") List<Student> students);
public int deleteinfo();
public int updateName();
public Student selectName();
public Student StudentWithIdCard();
// 查询所有学生及其相关信息(包括学生证和教师)
List<Student> StudentsWithDetailsAll();
}
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
<mapper namespace="com.example.demo.Mapper.StudentMapper">

<insert id="insertName">
INSERT INTO student (student_name,student_age,student_address)
VALUES
<!-- 实现类似的批量插入功能。 -->
<foreach collection="students" item="student" separator=",">
(#{student.name}, #{student.age}, #{student.address})
</foreach>
</insert>

<update id="updateName">
UPDATE student
<set>
<if test="name != null">student_name = #{name},</if><!-- 动态判断当属性不为空时,将值拼入Sql -->
<if test="age != null">student_age = #{age},</if>
</set>
WHERE student_id = #{id}
</update>

<delete id="deleteinfo">
DELETE FROM student WHERE
<choose>
<when test="id != null"><!-- 所有when条件只会被选择一个,类似Java的switch case -->
student_id = #{id}
</when>
<when test="name != null">
student_name = #{name}
</when>
<otherwise>
1=1 <!-- 当所有条件都不满足时执行otherwise -->
</otherwise>
</choose>
</delete>

<select id="selectName" resultType="com.example.demo.entity.Student">
SELECT student_id,student_name,student_age,student_address FROM student
<where>
<trim prefix="AND" suffixOverrides="AND">
<if test="age != null">
student_age = #{age} AND
</if>
<if test="address != null">
student_address = #{address} AND
</if>
</trim>
<!-- 根据字段需求可以筛选更多条件 -->
</where>
</select>
</mapper>
标签详细介绍
  • if:判断是否要添加相应的语句。

  • choose:类似if判断,搭配when与otherwisse使用,当所有when条件都不成立/不符合时,使用otherwise,所有when条件只会执行一次

  • foreach:动态生成 SQL 语句,用于遍历集合生成Sql片段,

    内部标签结构:

    • collection: 遍历的集合或数组的名称。对应Mapper传递参数名
    • item: 集合中的每个元素在循环中的别名。
    • index(可选): 当遍历的是 Map 时,表示 Map 的键。
    • open: 遍历开始时的字符串。
    • close: 遍历结束时的字符串。
    • separator: 每个元素之间的分隔符,注意:控制的是在每两个元素之间插入的分隔符。对于最后一个元素,不会在其后添加分隔符。
  • where:用于在 SQL 查询中动态生成 WHERE 子句。

  • set:用于 UPDATE 语句中,用于动态生成 SET 子句

  • trim:定制 SQL 语句的前缀、后缀,以及连接条件。它可以用于处理动态 SQL 语句的前缀和后缀。

    内部标签结构:

    • prefix:字符串开头添加的前缀。
    • prefixOverrides:字符串开头删除的前缀。
    • suffix:字符串结尾添加的后缀。
    • suffixOverrides:字符串结尾删除的后缀。

标签补充

  • sql:定义SQL的常量。

  • include :用于引用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
    26
    27
    28
    29
    30
    31
    32
    33
    <select id="selectName"  resultType="com.example.demo.entity.Student">
    SELECT student_id,student_name,student_age,student_address FROM student
    <where>
    <trim prefix="AND" suffixOverrides="AND">
    <if test="age != null">
    student_age = #{age} AND
    </if>
    <if test="address != null">
    student_address = #{address} AND
    </if>
    </trim>
    <!-- 根据字段需求可以筛选更多条件 -->
    </where>
    </select>

    <sql id="sqlIdOne">
    SELECT student_id,student_name,student_age,student_address FROM student
    </sql>

    <select id="selectName" resultType="com.example.demo.entity.Student">
    <include refid="sqlIdOne"/> <!-- refid引用sql定义的id -->
    <where>
    <trim prefix="AND" suffixOverrides="AND">
    <if test="age != null">
    student_age = #{age} AND
    </if>
    <if test="address != null">
    student_address = #{address} AND
    </if>
    </trim>
    <!-- 根据字段需求可以筛选更多条件 -->
    </where>
    </select>

结语

希望和朋友们一起学习、一起进步

Author: oGnok

Permalink: http://lovelypanda.top/2023/12/17/mybatis/