Mybatis基础操作

一、设置日志

复制代码
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

在Mybatis当中我们可以借助⽇志, 查看到sql语句的执⾏、执⾏传递的参数以及执⾏结果

二、XML 映射 SQL

在 MyBatis 中,你可以选择两种主要方式来编写 SQL 映射:使用 XML 映射文件或者使用 Java 注解。这两种方法各有优缺点,适用于不同的场景。以下是这两种方法的详细比较:

XML 映射是 MyBatis 最传统也是最灵活的配置方式。它将 SQL 语句和 Java 接口分离,使得 SQL 语句更易于管理和优化。

优点:
  • 集中管理:XML 文件可以集中管理 SQL 语句,易于维护和查阅。
  • 支持动态 SQL :XML 方式支持更为复杂的动态 SQL 语句,如条件语句(<if>)、循环语句(<foreach>)等,这些可以使 SQL 根据不同条件灵活生成。
  • 易于版本控制:作为文件的一部分,XML 映射可以轻松地纳入版本控制系统中。
缺点:
  • 间接性:代码与 SQL 分离,可能导致开发中来回查找对应 SQL 语句,降低开发效率。
  • 冗余:可能需要为每一个 SQL 语句编写大量的 XML 配置代码

三、注解 映射 SQL

注解方式是 MyBatis 在较新版本中引入的,它将 SQL 语句直接写在 Mapper 接口的方法上,减少了配置的复杂性。

优点:
  • 直接性:SQL 语句与 Java 方法紧密绑定,易于阅读和修改。
  • 简洁:减少了 XML 的冗余配置,使得项目结构更为简洁。
缺点:
  • 有限的动态 SQL 支持 :虽然注解支持基本的动态 SQL,如 @Select@Insert 等,但对于复杂的动态 SQL,如条件判断、循环等支持不如 XML 方式。

  • 可扩展性有限 :对于非常复杂的 SQL,注解方式可能显得力不从心。

    java 复制代码
    public interface UserMapper {
        @Select("SELECT * FROM users WHERE id = #{id}")
        User selectUserById(int id);
    }

四、参数传递

1)一个参数

如果 SQL 语句只需要一个参数,你可以直接在 mapper 接口的方法中传递这个参数。MyBatis 会自动将它识别为 SQL 语句中的参数。

java 复制代码
public interface UserMapper {
    User selectUserById(int id);
}

Mapper XML

XML 复制代码
<select id="selectUserById" parameterType="int" resultType="com.example.mybatisdemo.model.User">
    SELECT * FROM users WHERE id = #{id}
</select>

在这个例子中,#{id} 是一个占位符,用于在执行时替换成方法 selectUserById(int id) 中的参数。

2)多个参数

当方法中有多个参数时,MyBatis 提供了几种处理方式:

  • @Param 注解 :最常用的方式,可以给参数命名,然后在 SQL 语句中引用这些名称、Mapper 接口

    java 复制代码
    public interface UserMapper {
        User findUserByNameAndEmail(@Param("name") String name, @Param("email") String email);
    }

    Mapper XML

    XML 复制代码
    <select id="findUserByNameAndEmail" resultType="com.example.mybatisdemo.model.User">
        SELECT * FROM users WHERE name = #{name} AND email = #{email}
    </select>

    使用 @Param 注解可以明确指定 SQL 语句中参数的名称,这样即使参数的顺序改变,也不会影响 SQL 语句的执行

3) 返回主键

在 MyBatis 中,将操作结果的自动生成的主键(如数据库自增主键)返回到应用程序中是一个常见需求。这通常在执行插入(INSERT)操作时使用。MyBatis 提供了几种方式来处理自动生成的主键,并将其返回。

a) 使用 XML 映射

当你使用 XML 映射文件定义 SQL 语句时,可以通过 useGeneratedKeyskeyProperty 属性来指定如何返回自动生成的主键。

示例:

假设你有一个名为 users 的表,其中 id 字段是自增主键。

XML 复制代码
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>

在这个例子中:

  • useGeneratedKeys="true" 告诉 MyBatis 使用 JDBC 的 getGeneratedKeys() 方法来检索由数据库自动生成的键。
  • keyProperty="id" 指定 MyBatis 应将获取的键值设置到哪个属性上。在本例中,它将设置到 User 对象的 id 属性上。
b) 使用注解

当你使用注解来配置 MyBatis 映射时,可以使用 @Options 注解来指定如何处理自动生成的键。

Mapper 接口方法:

java 复制代码
public interface UserMapper {
    @Insert("INSERT INTO users (name, email) VALUES (#{name}, #{email})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insertUser(User user);
}

这里的 @Options 注解具有与 XML 相同的属性:

  • useGeneratedKeys = true 这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据库内 部⽣成的主键(⽐如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的⾃动递增字 段),默认值:false
  • keyProperty = "id" 指定能够唯⼀识别对象的属性,MyBatis 会使⽤ getGeneratedKeys 的返回值或 insert 语句的 selectKey ⼦元素设置它的值,默认值:未设置(unset)

五、SELECT 查找

在 MyBatis 中,字段映射的默认行为是按照属性名和数据库字段名进行匹配。如果 Java 对象的属性名与数据库表的字段名不一致,你需要显式地指定这种映射关系,以确保正确的数据赋值

1. 起别名 (Alias)

在 SQL 查询中直接为字段起别名,使其与 Java 对象的属性名匹配。这是一种非常简单且直接的方式,适用于简单的场景。

示例:

假设数据库字段名为 emp_name,而 Java 对象的属性名为 name,可以在 SQL 语句中直接为 emp_name 起别名为 name

sql 复制代码
SELECT emp_id as id, emp_name as name, emp_email as email FROM employees

使用这种方法,MyBatis 在映射结果到 Java 对象时会自动将 name 列的值赋给 name 属性。

2. 结果映射 (Result Mapping)

使用 MyBatis 的 resultMap 元素来定义字段映射关系。这种方式提供了高度的灵活性和控制力,适用于复杂的数据结构和关系。

示例:

在 MyBatis 映射文件中使用 resultMap 来定义字段与属性的映射关系:

XML 复制代码
<mapper namespace="com.example.mapper.EmployeeMapper">
    <resultMap id="EmployeeMap" type="Employee">
        <result property="id" column="emp_id"/>
        <result property="name" column="emp_name"/>
        <result property="email" column="emp_email"/>
    </resultMap>

    <select id="selectEmployee" resultMap="EmployeeMap">
        SELECT emp_id, emp_name, emp_email FROM employees
    </select>
</mapper>

这个 resultMap 显式地告诉 MyBatis 如何将数据库字段映射到 Java 对象的属性。

3. 开启驼峰命名 (Camel Case Mapping)

通常数据库列使⽤蛇形命名法进⾏命名(下划线分割各个单词), ⽽ Java 属性⼀般遵循驼峰命名法约定. 为了在这两种命名⽅式之间启⽤⾃动映射,需要将 mapUnderscoreToCamelCase 设置为 true。

XML 复制代码
mybatis:
 configuration:
  map-underscore-to-camel-case: true #配置驼峰⾃动转换

开启这个配置后,MyBatis 会自动将诸如 emp_name 的字段名映射为 empName 的 Java 属性。这样,你不需要在每个 SQL 语句中为字段起别名,也不需要在每个映射中定义 resultMap

六、#{} 和 ${}

在 MyBatis 中,#{}${} 是两种基本的参数占位符,用于在 SQL 语句中插入参数值,但它们的工作方式和用途有明显的区别:

1. #{} (Prepared Statement 占位符)

#{} 是用来创建预处理语句 (Prepared Statement) 的参数占位符。当你使用 #{} 时,MyBatis 会使用 JDBC 的 PreparedStatement 功能,这意味着参数值在传递给数据库之前会进行适当的转义,从而防止 SQL 注入攻击。这是处理用户输入或不可信数据时推荐的做法。

示例:
sql 复制代码
SELECT * FROM users WHERE id = #{id}

在这里,#{id} 会被替换为一个参数标记,如 ?,并且 id 的值会在执行时被安全地绑定到这个问号上。如果 id5,那么实际执行的 SQL 会是:

sql 复制代码
SELECT * FROM users WHERE id = ?

并且 5 作为参数传递,这样可以防止 SQL 注入。

2. ${} (文本替换占位符)

${} 用于直接将参数值插入 SQL 语句中。这种方式可以用于动态更改表名、列名或者是 SQL 语句的其它部分,而不仅仅是值。使用 ${} 时,必须非常小心,因为它不会对参数值进行任何处理或转义,从而有可能导致 SQL 注入风险。

sql 复制代码
SELECT * FROM ${tableName} WHERE id = ${id}

如果 tableNameusers,而 id5,那么生成的 SQL 将直接是:

sql 复制代码
SELECT * FROM users WHERE id = 5

这种方式使得你可以灵活地构建 SQL 语句,但如果 tableNameid 来自于用户输入或其他不可信的来源,则可能会带来安全风险。

选择使用哪一个
  • 当你需要插入安全的、转义后的数据值时,使用 #{}
  • 当你需要在 SQL 语句中动态插入表名、列名或其他不会改变 SQL 逻辑的元素时,可以使用 ${},但必须非常小心确保这些值是安全的,避免 SQL 注入。

通常,除非绝对必要,建议尽量避免使用 ${} 来避免潜在的安全问题。使用 #{} 可以利用 PreparedStatement 的优势,有效防止 SQL 注入,是更安全的选择。

${}的必要性
1.排序
java 复制代码
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +
 "from userinfo order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);

使⽤ {sort} 可以实现排序查询, ⽽使⽤ #{sort} 就不能实现排序查询了. 注意: 此处 sort 参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 '' 的, 所以此时的 {sort} 也不加引号

当使⽤ #{sort} 查询时, asc 前后⾃动给加了引号, 导致 sql 错误 #{} 会根据参数类型判断是否拼接引号 '' 如果参数类型为String, 就会加上 引号

除此之外, 还有表名作为参数时, 也只能使⽤ ${}

2.like 查询

在 MyBatis 中使用 #{} 对于大多数情况都是正常工作的,但在使用 LIKE 语句时,可能会遇到问题。这是因为 #{} 在转换过程中会将输入视为字面值,并且会自动在值周围添加单引号。这在 LIKE 查询中通常不是预期的行为,因为通配符(如 %)需要与字段值直接相连。

当你使用 LIKE 并希望包含通配符时,如下

sql 复制代码
SELECT * FROM users WHERE name LIKE '%#{name}%'

MyBatis 会处理这个语句并将其转换为:

sql 复制代码
SELECT * FROM users WHERE name LIKE '%\'someName\'%'

这里的单引号是自动添加的,导致 SQL 语句错误,因为 SQL 不期望在 %someName 之间有额外的单引号。

为了正确使用 LIKE,你应该让 MyBatis 在不添加额外单引号的情况下处理变量。有两种主要方法可以实现:

使用 CONCAT 函数

你可以使用 SQL 的 CONCAT 函数来确保通配符和变量被正确组合,而不是将它们作为字符串字面值直接插入。这在大多数数据库中都有效。

sql 复制代码
SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')
安全性考虑

尽管 ${} 在这种情况下是必要的,但它引入了潜在的 SQL 注入风险。为了安全地使用 ${},你需要确保:

  1. 输入验证 :对传入的列名和排序方向进行严格验证,确保它们是预定义的有效值。例如,你可以在应用层或服务层检查 orderByColumn 是否确实是数据库中的列名,orderDir 是否仅为 "ASC" 或 "DESC"。

  2. 使用白名单:仅允许特定的、安全的字段名和排序方向,可以通过在服务层设置一个白名单来实现。

通过这种方式,尽管使用了 ${},但通过严格的输入验证和白名单策略,可以最大程度地减少 SQL 注入的风险,使得动态排序既灵活又安全。这种方法适用于需要高度动态性的 SQL 操作,如动态排序、动态查询字段等场景。

相关推荐
GEM的左耳返3 小时前
Java面试全攻略:Spring生态与微服务架构实战
spring boot·redis·spring cloud·微服务·kafka·java面试
愿你天黑有灯下雨有伞3 小时前
Spring Boot SSE实战:SseEmitter实现多客户端事件广播与心跳保活
java·spring boot·spring
KNeeg_5 小时前
Spring循环依赖以及三个级别缓存
java·spring·缓存
AI_Gump6 小时前
【AI阅读】20250717阅读输入
java·spring boot·spring
ldj20206 小时前
Docker的docker-compose类比Spring的ApplicationContext
spring·docker
qhd吴飞7 小时前
mybatis 差异更新法
java·前端·mybatis
angushine8 小时前
logstash采集springboot微服务日志
spring boot·微服务·linq
懂得节能嘛.8 小时前
【SpringAI实战】ChatPDF实现RAG知识库
java·后端·spring
探索java8 小时前
Spring 解析 XML 配置文件的过程(从读取 XML 到生成 BeanDefinition)
xml·java·spring·xmlbeanfactory
武昌库里写JAVA8 小时前
「mysql」Mac osx彻底删除mysql
vue.js·spring boot·毕业设计·layui·课程设计