Mybatis映射文件解析

Mybatis 映射文件解析

mybatis 中比较符号的写法

第一种写法

直接在外层嵌套 这个标签,例如 = ]]>

标签说明:

CDATA(Character Data)标签,它在 XML 和 HTML 中用于定义一段文本数据,该数据不应被解析器解析为标记。CDATA 部分中的内容将原样输出,即其中的所有字符都将被视为普通文本,即使它们可能包含特殊字符或标记。

sql 举例如下:

select 
	*
from 
	user
where 
	age = ]]> 18

第二种写法

符号替换,如下面表格所示

原符号 替换符号
< <
<= <=
> >
>= >=
& &
&apos
" "

符号区分:

  • 小于号 < – < – 其中 l 代表 less,t 代表 than,合起来是 less than
  • 大于号 > – > – 其中 g 代表 greater,t 代表 than,合起来是 greater than

sql 举例如下:

select 
	*
from 
	user
where 
	age >= 18

顶级元素

SQL 映射文件有以下几个顶级元素(按照它们应该被定义的顺序):

  1. cache – 给定命名空间的缓存配置
  2. cache-ref – 其他命名空间缓存配置的引用
  3. resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象
  4. sql – 可被其他语句引用的可重用语句块
  5. insert – 映射插入语句
  6. update – 映射更新语句
  7. delete – 映射删除语句
  8. select – 映射查询语句

查询技巧

获取参数值的两种方式

有两种形式:

  1. ${} – 字符串替换
  2. #{} – 预编译处理

区别:

  1. #{} 是预编译处理,像传进来的数据会加个 ''(# 将传入的数据都当成一个字符串,会对自动传入的数据加一个单引号)
    • 会按照预编译SQL语句(PreparedStatement)的方式来处理这些占位符,即将参数绑定到SQL语句中的问号(?)位置上,而不是直接将参数值拼接到SQL语句中
    • 对于数字类型的参数不会额外加引号,对于字符串类型则会加上单引号或者双引号
  2. ${} 就是字符串替换。直接替换掉占位符。$ 方式一般用于传入数据库对象,例如传入表名。使用 ${} 的话会导致 sq| 注入

什么是 SQL 注入呢?

比如:

select * from user where id = ${value}

value 应该是一个数值吧。然后如果对方传过来的是 001 and name= tom。这样不就相当于多加了一个条件嘛?

把 SQL 语句直接写进来了。如果是攻击性的语句呢?001;drop table user直接把表给删了

建议

所以为了防止 SQL 注入,能用 #{} 的不要去用 ${}

如果非要用 ${} 的话,那要注意防止 SQL 注入问题,可以手动判定传入的变量,进行过滤,一般 SQL 注入会输入很长的一条 SQL 语句。

动态设置表名

只能使用 ${},因为表名不能加单引号

例如:

<select id="getUserByTable" resultType="User">
	select * from t_info_${tableName}
select>

新增返回主键 id⭐️

在 MyBatis 中,当你插入一条新记录并希望获取这条新记录的主键 ID 时,你可以使用数据库提供的自动生成主键的机制(例如自增字段),也可以通过 MyBatis 提供的一些特性来获取主键值。

以下是一些常见的方法来获取新增记录的主键 ID:

1. 使用数据库自增主键

这是最常见的方法。许多数据库系统(如 MySQL、PostgreSQL、SQL Server 等)都支持自动生成主键。在你的数据库表中,你可以设置一个字段为自增(例如,在 MySQL 中使用 AUTO_INCREMENT 属性),这样每次插入新记录时,数据库会自动为这个字段生成一个新的值。

你的表结构可能如下所示:

CREATE TABLE your_table (
    id INT NOT NULL AUTO_INCREMENT,
    other_column VARCHAR(255),
    PRIMARY KEY (id)
);

在你的 MyBatis 映射文件中,你可以这样写:

<insert id="insertUser" parameterType="User">
    INSERT INTO your_table (other_column) VALUES (#{otherColumn})
insert>

然后,在你的 Java 代码中,你可以在插入记录后从数据库获取主键 ID:

在调用插入方法时,会自动将生成的主键 ID 设置到实体对象中,因此可以直接通过实体对象获取主键 ID。

User user = new User();
user.setOtherColumn("some value");
// sqlSession.insert("insertUser", user);
// sqlSession.insert("com.xxx.project.mapper.XxxMapper.insertUser", user);
userMapper.insertUser(user);

// 获取数据库自动生成的主键 ID
Long id = user.getId();
2. 使用 MyBatis 的 useGeneratedKeys 属性

如果你的数据库支持返回生成的主键,你可以在 MyBatis 映射文件中设置 useGeneratedKeys="true",这样 MyBatis 会在执行插入操作后返回生成的主键。

<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO your_table (other_column) VALUES (#{otherColumn})
insert>

# <insert id="insertEntity" parameterType="YourEntityClass" useGeneratedKeys="true" keyProperty="id">
#    INSERT INTO your_table (column1, column2, ...)
#    VALUES (#{property1}, #{property2}, ...)
# insert>

在这个例子中,keyProperty 属性指定了要将生成的主键值设置到哪个属性上。

  • useGeneratedKeys="true":表示使用数据库自动生成的主键。
  • keyProperty="id":表示将自动生成的主键值设置到实体类的指定属性中。

在你的 Java 代码中,你可以直接获取这个属性的值:

User user = new User();
user.setOtherColumn("some value");
// sqlSession.insert("insertUser", user);
// sqlSession.insert("com.xxx.project.mapper.XxxMapper.insertUser", user);
userMapper.insertUser(user);

// 现在 user 对象的 id 属性已经被设置为新生成的主键值
Long id = user.getId();
3. 使用 MyBatis 的 @SelectKey 注解

如果你使用的是 MyBatis 注解而不是映射文件,你可以使用 @SelectKey 注解来获取生成的主键。

public interface UserMapper {
    @Insert("INSERT INTO your_table (other_column) VALUES (#{otherColumn})")
    @SelectKey(statement = "SELECT LAST_INSERT_ID()", keyProperty = "id", before = false)
    int insertUser(User user);
}

在这个例子中,

  • @SelectKey 注解指定了一个 SQL 语句 SELECT LAST_INSERT_ID() 来获取最后插入记录的 ID。
  • keyProperty 属性指定了要将这个值设置到哪个属性上。
  • before 属性指定了生成的主键是在执行插入操作之前还是之后被选择的。
User user = new User();
user.setOtherColumn("some value");
// sqlSession.insert("insertUser", user);
// sqlSession.insert("com.xxx.project.mapper.XxxMapper.insertUser", user);
userMapper.insertUser(user);

// 现在 user 对象的 id 属性已经被设置为新生成的主键值
Long id = user.getId();

查询一条数据为 map 集合

1、Mapper 接口

/**  
 * 根据用户id查询用户信息为map集合  
 * @param id  
 * @return  
 */  
Map<String, Object> getUserToMap(@Param("id") int id);

2、对应的映射文件

    
    <select id="getUserToMap" resultType="map">
        select * from t_user where id = #{id}
    select>
    

查询多条数据为 map 集合

方法一

1、Mapper 接口

    /**
     * 查询所有用户信息为map集合
     * 

* 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合, * 此时可以将这些map放在一个list集合中获取 * * @return */ List<Map<String, Object>> getAllUserToMap();

2、对应的映射文件

    
    <select id="getAllUserToMap" resultType="map">
        select * from t_user
    select>
    
方法二

1、Mapper 接口(用 @MapKey 注解)

    /**
     * 查询所有用户信息为map集合
     * 

* 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据, * 此时需要通过 @MapKey 注解设置 map 集合的键,值是每条数据所对应的 map 集合 * * @return */ @MapKey("id") Map<String, Object> getAllUserToMap();

2、对应的映射文件

    
    <select id="getAllUserToMap" resultType="map">
        select * from t_user
    select>
    

动态 sql

MyBatis 动态 SQL 提供了多种标签,用于在运行时动态构建 SQL 语句,极大地提高了 SQL 语句的灵活性和可重用性。

以下是几个关键动态 SQL 标签及其用法的总结:

1、if

含义

用于根据表达式的值来决定是否包含某段 SQL 语句。

用法

    <if test="condition">
        SQL 片段
    if>

解释

if 标签可通过 test 属性(即传递过来的数据)的表达式进行判断

  • 若表达式的结果为 true,则标签中的内容会执行;
  • 反之标签中的内容不会执行

2、choose / when / otherwise

含义

类似于 Java 中的 switch-case 结构,根据多个条件分支执行不同 SQL 片段。

用法

    <choose>
        <when test="condition1">SQL片段1when>
        <when test="condition2">SQL片段2when>
        
        <otherwise>默认SQL片段(当所有when都不满足时执行)otherwise>
    choose>

3、where

含义

用于动态地添加 WHERE 子句,避免不必要的 ANDOR 关键词出现在 SQL 语句中。

用法

包裹多条可能的条件语句,MyBatis 会智能地忽略那些条件未满足(表达式结果为假)时产生的 ANDOR 关键词。

where 和 if 一般结合使用

  • 若 where 标签中的 if 条件都不满足,则 where 标签没有任何功能,即不会添加 where 关键字

  • 若 where 标签中的 if 条件满足,则 where 标签会自动添加 where 关键字,并将条件【最前方】多余的 and/or 去掉

注意:where 标签不能去掉【条件后】多余的 and/or

示例

    
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <where>
            <if test="empName != null and empName !=''">
                emp_name = #{empName}
            if>
            <if test="age != null and age !=''">
                and age = #{age}
            if>
            <if test="sex != null and sex !=''">
                and sex = #{sex}
            if>
            <if test="email != null and email !=''">
                and email = #{email}
            if>
        where>
    select>

4、set

含义

动态地添加 UPDATE 语句的 SET 部分,同样可以避免不必要的逗号问题。

用法

在 UPDATE 语句中,根据条件决定哪些列需要更新。(搭配 trim 标签使用。)

示例

    <update id="updateUserInfo">
        UPDATE
            t_user
        <set>
            <if test="dto.gender != null">
                gender = #{dto.gender},
            if>
            <if test="dto.birthday != null">
                birthday = #{dto.birthday},
            if>
        set>
        WHERE
            user_id = #{userId}
    update>

解释

在更新数据的时候,使用 标签,使用了 set 标签会自动帮你删除尾部的逗号

5、trim

含义

用来【删除】SQL 片段首尾的特定字符或关键字,也可以在首尾【添加】字符或关键字。

常用属性

  1. prefix:在 trim 标签中的内容的前面【添加】某些内容
  2. suffix:在 trim 标签中的内容的后面【添加】某些内容
  3. prefixOverrides:在 trim 标签中的内容的前面【去掉】某些内容
  4. suffixOverrides:在 trim 标签中的内容的后面【去掉】某些内容(suffixOverrides="and|or")

用法

		<trim prefix="(" suffix=")" prefixOverrides="," suffixOverrides=",">
  		column1,
  		column2
		trim>


    <update id="updateUserInfo">
        UPDATE
            t_user
        <set>
            <if test="dto.gender != null">
                gender = #{dto.gender},
            if>
            <if test="dto.birthday != null">
                birthday = #{dto.birthday}
            if>
            <trim suffixOverrides=","/>
        set>
        WHERE
            user_id = #{userId}
    update>


    <trim prefix="SET" suffixOverrides=",">
        ...
    trim>

6、foreach

含义

遍历集合,适用于 in 语句或者批量插入等场景。

用法

    <insert id="batchInsert">
        INSERT INTO tbl_user (id,name,age,sex,is_delete)
        VALUES
        <foreach collection="userList" item="item" index="index" open="(" separator="),(" close=")">
            #{item.id},#{item.name},#{item.age},#{item.sex},#{item.isDelete}
        foreach>
    insert>

上述示例为批量插入语句,会遍历名为 userList 的集合,依次将每个元素 item 插入 SQL 语句中,每一条数据用 "),(" 分隔。

7、include

  • sql 片段,可以记录一段公共 sql 片段,在使用的地方通过 include 标签进行引入
  • 声明 sql 片段: 标签
<sql id="empColumns">eid,emp_name,age,sex,emailsql>
  • 引用 sql 片段: 标签

<select id="getEmpByCondition" resultType="Emp">
	select <include refid="empColumns">include> from t_emp
select>

模糊查询的 5 种方式

方式一:&{}

&{} 这种方式,简单,但是无法防止 SQL 注入,所以不推荐使用

<if test="dto.name != null"> 
	AND name LIKE '%${dto.name}%'
if>

方式二:#{}

#{} 左右两边写上字符串 "%"

<if test="dto.name != null"> 
	AND name LIKE "%" #{dto.name} "%"
if>

方式三:字符串拼接函数

concat() 函数

<if test="dto.name != null"> 
	AND name LIKE concat('%', #{dto.name}, '%')
if>

方式四:bind 标签

<select id="searchstudents" resultType="com.example.entity.studentEntity"
        parameterType="com.example.entity.studentEntity">
  
    <bind name="pattern1" value="'%' + parameter.name + '%'" />
    <bind name="pattern2" value="'%' + parameter.address + '%'" />
  
    SELECT * FROM test student
    <where>
        <if test="name != null and name != ''">
            name LIKE #{pattern1}
        if>
        <if test="address != null and address != ''">
            AND address LIKE #{pattern2}
        if>
    where>
    ORDER BY id
  
select>

方式五:java 代码里写

1、直接在 java 代码里写,在值的两边加上 %

dto.setName("%张三%");

2、然后,在映射文件中直接传参就行

<if test="dto.name != null"> 
	AND name LIKE #{dto.name}
if>

延迟加载

标签

  • property: 指定父对象中的属性名,该属性应该是一个集合类型,如 List 或 Set。
  • ofType: 指定集合中元素的类型
  • select: 指定一个 MyBatis 查询,该查询返回集合中的元素。
  • column: 用于传递参数给