MyBatis高效SQL写法指南

一、MyBatis

1. 批量操作优化

批量操作能够显著提升数据库的处理效率,MyBatis通过<foreach>标签支持批量插入、更新和删除。

  • 批量插入
XML 复制代码
<insert id="batchInsert" parameterType="java.util.List">
    INSERT INTO user (username, email, create_time) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.username}, #{item.email}, #{item.createTime})
    </foreach>
</insert>

此示例展示了如何批量插入用户数据。通过<foreach>遍历传入的用户列表,将每个用户的信息插入到数据库中。

  • 批量更新
XML 复制代码
<update id="batchUpdate" parameterType="java.util.List">
    <foreach collection="list" item="item" separator=";">
        UPDATE user
        SET username = #{item.username}, email = #{item.email}
        WHERE id = #{item.id}
    </foreach>
</update>

此示例演示了如何批量更新用户信息。对每个用户,根据其ID更新用户名和邮箱。

  • 批量删除
XML 复制代码
<delete id="deleteBatchByIds" parameterType="java.util.List">
    DELETE FROM my_table
    WHERE id IN
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item}
        </foreach>
</delete>

此示例展示了如何批量删除用户。通过<foreach>生成一个ID列表,并删除这些ID对应的用户。

2. 动态SQL

动态SQL允许根据条件动态构建SQL语句,MyBatis通过<if>标签实现这一功能。

  • 动态查询
XML 复制代码
<select id="findUsers" resultType="User">
    SELECT * FROM user
    WHERE 1=1
    <if test="username != null and username != ''">
        AND username LIKE CONCAT('%', #{username}, '%')
    </if>
    <if test="email != null and email != ''">
        AND email = #{email}
    </if>
    <if test="status != null">
        AND status = #{status}
    </if>
</select>

此示例展示了如何根据传入的条件动态构建查询语句。如果某个条件为空,则相应的查询部分不会被添加到SQL中。

3. 多条件分支查询

对于更复杂的查询逻辑,MyBatis提供了<choose><when><otherwise>标签。

  • 多条件分支查询
XML 复制代码
<select id="findUsersByCondition" resultType="User">
    SELECT * FROM user
    WHERE 1=1
    <choose>
        <when test="searchType == 'username'">
            AND username LIKE CONCAT('%', #{keyword}, '%')
        </when>
        <when test="searchType == 'email'">
            AND email LIKE CONCAT('%', #{keyword}, '%')
        </when>
        <otherwise>
            AND (username LIKE CONCAT('%', #{keyword}, '%') OR email LIKE CONCAT('%', #{keyword}, '%'))
        </otherwise>
    </choose>
</select>

此示例展示了如何根据不同的搜索类型选择不同的查询条件。如果没有指定搜索类型,则默认搜索用户名和邮箱。

4. 使用<foreach>标签

<foreach>标签用于遍历集合,并构建IN子句或批量操作。

XML 复制代码
<select id="findUsersByIds" parameterType="list" resultType="User">
  SELECT * FROM users
  WHERE id IN
  <foreach item="id" index="index" collection="list" open="(" separator="," close=")">
    #{id}
  </foreach>
</select>

5. SQL语句优化

使用<trim>标签可以优化生成的SQL语句,避免多余的ANDOR关键字。

  • SQL语句优化
XML 复制代码
<select id="findUsers" resultType="User">
    SELECT * FROM user
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="username != null and username != ''">
            AND username LIKE CONCAT('%', #{username}, '%')
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
    </trim>
</select>

此示例展示了如何使用<trim>标签去除多余的ANDOR,并在有查询条件时添加WHERE关键字。

6. 自动生成主键

在插入操作中,经常需要获取数据库自动生成的主键。MyBatis提供了<selectKey>标签和useGeneratedKeys属性来实现这一功能。

  • 自动生成主键
XML 复制代码
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (username, email, create_time)
    VALUES (#{username}, #{email}, #{createTime})
</insert>

(注意:这里的<selectKey>标签通常不是必需的,因为useGeneratedKeys=truekeyProperty已经足够让MyBatis自动处理主键生成。)

此示例展示了如何在插入用户后自动获取生成的主键,并将其赋值给传入的User对象的id属性。

7. 注解方式使用MyBatis

除了XML配置,MyBatis还支持使用注解来定义SQL操作,使代码更简洁。

  • 注解方式示例
java 复制代码
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User getUserById(Long id);

    @Insert("INSERT INTO user (username, email, create_time) VALUES (#{username}, #{email}, #{createTime})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insertUser(User user);

    @Update("UPDATE user SET username = #{username}, email = #{email} WHERE id = #{id}")
    int updateUser(User user);

    @Delete("DELETE FROM user WHERE id = #{id}")
    int deleteUser(Long id);
}

此示例展示了如何使用注解来定义简单的CRUD操作。对于复杂的SQL语句,仍然建议使用XML配置。

8. 高级映射

MyBatis提供了强大的对象关系映射功能,可以处理复杂的表关系。

  • 一对多映射
XML 复制代码
<resultMap id="userWithOrdersMap" type="User">
    <id property="id" column="user_id"/>
    <result property="username" column="username"/>
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="orderNumber" column="order_number"/>
        <result property="createTime" column="order_create_time"/>
    </collection>
</resultMap>

<select id="getUserWithOrders" resultMap="userWithOrdersMap">
    SELECT u.id as user_id, u.username, o.id as order_id, o.order_number, o.create_time as order_create_time
    FROM user u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.id = #{userId}
</select>

此示例展示了如何将用户和订单信息映射到一个复杂的对象结构中,实现一对多映射。

二、MyBatisPlus

MyBatis-Plus 是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus 提供了大量的 CRUD(创建、读取、更新、删除)操作的方法,并允许你通过自定义 SQL 语句来实现更复杂的操作。

以下是一些 MyBatis-Plus 中常见的 SQL 语句写法:

1. 使用内置方法

1.1 查询方法
  • selectById(Serializable id): 根据主键查询记录。
  • selectBatchIds(Collection<?> idList): 根据主键集合批量查询记录。
  • selectByMap(Map<String, Object> params): 根据条件构造器(Map 类型)查询记录。
  • selectList(Wrapper<T> queryWrapper): 根据条件构造器查询记录列表。
  • selectPage(Page<T> page, Wrapper<T> queryWrapper): 分页查询记录。

示例:

java 复制代码
User user = userMapper.selectById(1L);
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
Map<String, Object> params = new HashMap<>();
params.put("name", "John");
List<User> usersByName = userMapper.selectByMap(params);
List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("age", 18));
Page<User> userPage = new Page<>(1, 10); // 第一页,每页10条记录
IPage<User> userPageResult = userMapper.selectPage(userPage, new QueryWrapper<User>().like("name", "Jo"));
1.2 插入方法
  • insert(T entity): 插入一条记录。
  • insertBatchSomeColumn(List<T> entityList): 批量插入仅指定列。

示例:

java 复制代码
User newUser = new User();
newUser.setName("John Doe");
newUser.setAge(25);
userMapper.insert(newUser);
List<User> userList = ...; // 要插入的用户列表
userMapper.insertBatchSomeColumn(userList); // 仅插入 userList 中的部分字段

注意:insertBatchSomeColumn 方法是 MyBatis-Plus 提供的一个高级特性,用于批量插入时只插入指定的列。它通常需要一个额外的配置来指定哪些列需要被插入。

1.3 更新方法
  • updateById(T entity): 根据主键更新记录。
  • update(T entity, Wrapper<T> updateWrapper): 根据条件构造器更新记录。

示例:

java 复制代码
User userToUpdate = new User();
userToUpdate.setId(1L);
userToUpdate.setName("Jane Doe");
userMapper.updateById(userToUpdate);
userMapper.update(null, new UpdateWrapper<User>().eq("age", 18).set("name", "Unknown"));

注意:在第二个示例中,第一个参数为 null 表示更新所有符合条件的记录。如果你想根据实体对象的某些属性来更新,你应该不为该参数传 null

1.4 删除方法
  • deleteById(Serializable id): 根据主键删除记录。
  • deleteBatchIds(Collection<?> idList): 根据主键集合批量删除记录。
  • delete(Wrapper<T> deleteWrapper): 根据条件构造器删除记录。

示例:

java 复制代码
userMapper.deleteById(1L);
userMapper.deleteBatchIds(Arrays.asList(1L, 2L, 3L));
userMapper.delete(new QueryWrapper<User>().eq("age", 18));

这些方法都定义在 BaseMapper 接口中,因此当你创建一个 Mapper 接口并继承 BaseMapper 时,你可以直接使用这些方法。这些方法提供了非常强大的 CRUD 功能,并且支持链式调用和 Lambda 表达式,使得代码更加简洁和易于维护

2. 使用 Wrapper 条件构造器

MyBatis-Plus 的 Wrapper 条件构造器是一种强大的工具,它允许开发者以链式调用的方式构建复杂的 SQL 查询条件。这种构造器不仅简化了 SQL 语句的编写,还提高了代码的可读性和可维护性。以下是对 MyBatis-Plus Wrapper 条件构造器 SQL API 的一些基本解释和示例:

基本概念

  • Wrapper :条件构造器的抽象基类,通常不直接使用,而是使用其子类如 QueryWrapperUpdateWrapper
  • QueryWrapper:用于构建查询条件的构造器,支持链式调用和 Lambda 表达式。
  • UpdateWrapper:用于构建更新条件的构造器,同样支持链式调用和 Lambda 表达式。

常用方法

  • eq(column, value) :等于条件,相当于 SQL 中的 =
  • ne(column, value) :不等于条件,相当于 SQL 中的 <>!=
  • gt(column, value) :大于条件,相当于 SQL 中的 >
  • ge(column, value) :大于等于条件,相当于 SQL 中的 >=
  • lt(column, value) :小于条件,相当于 SQL 中的 <
  • le(column, value) :小于等于条件,相当于 SQL 中的 <=
  • like(column, value) :模糊查询条件,相当于 SQL 中的 LIKE
  • notLike(column, value) :模糊查询非条件,相当于 SQL 中的 NOT LIKE
  • between(column, value1, value2) :范围查询条件,相当于 SQL 中的 BETWEEN ... AND ...
  • notBetween(column, value1, value2) :范围查询非条件,相当于 SQL 中的 NOT BETWEEN ... AND ...
  • in(column, values) :IN 查询条件,相当于 SQL 中的 IN
  • notIn(column, values) :IN 查询非条件,相当于 SQL 中的 NOT IN
  • isNull(column) :字段为空条件,相当于 SQL 中的 IS NULL
  • isNotNull(column) :字段不为空条件,相当于 SQL 中的 IS NOT NULL

示例

以下是一个使用 QueryWrapper 构建查询条件的示例:

2.1 查询
java 复制代码
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "John") // 姓名等于 John
            .ge("age", 18)      // 年龄大于等于 18
            .like("email", "@example.com"); // 邮箱包含 @example.com

List<User> userList = userMapper.selectList(queryWrapper);

这个示例构建了一个查询条件,查询姓名等于 "John"、年龄大于等于 18 且邮箱包含 "@example.com" 的用户列表。

2.2 更新
java 复制代码
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "John") // 姓名等于 John
             .set("age", 25);    // 将年龄更新为 25

int updateCount = userMapper.update(null, updateWrapper); // 这里的 null 表示更新所有符合条件的记录

这个示例构建了一个更新条件,将姓名等于 "John" 的用户的年龄更新为 25。注意,在 update 方法中,第一个参数通常为要更新的实体对象,但在这里我们传入 null,因为我们只关心条件部分,并且要更新的字段已经在 UpdateWrapper 中指定了。在实际使用中,如果需要根据实体对象的某些属性来更新,可以将该实体对象作为第一个参数传入

3. 使用 Lambda 表达式

MyBatis-Plus 提供了对 Lambda 表达式的支持,这主要是为了在构建 SQL 语句时提高类型安全性并减少硬编码的字段名。通过使用 Lambda 表达式,你可以避免在代码中直接引用数据库表的列名,从而减少因列名错误而导致的运行时问题。

在 MyBatis-Plus 中,QueryWrapperUpdateWrapper 都提供了 Lambda 表达式的方法,这些方法允许你以类型安全的方式引用实体类的属性。

3.1 使用 Lambda 表达式构建查询条件

以下是一个使用 QueryWrapper 和 Lambda 表达式构建查询条件的示例:

java 复制代码
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> getUsersByLambda() {
        // 使用 Lambda 表达式构建查询条件
        QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper.lambda()
                .eq(User::getName, "John") // 姓名等于 John
                .ge(User::getAge, 18);     // 年龄大于等于 18

        // 执行查询
        return userMapper.selectList(queryWrapper);
    }
}

在这个示例中,Wrappers.query() 方法创建了一个 QueryWrapper 实例,然后通过调用 lambda() 方法切换到了 Lambda 表达式的模式。之后,你可以使用 eqge 等方法,并传入一个方法引用(如 User::getName)来指定要比较的字段。

3.2 使用 Lambda 表达式构建更新条件

同样地,UpdateWrapper 也支持 Lambda 表达式:

java 复制代码
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;

public class UserService {

    @Autowired
    private UserMapper userMapper;

    public int updateUserByLambda() {
        // 使用 Lambda 表达式构建更新条件
        UpdateWrapper<User> updateWrapper = Wrappers.update();
        updateWrapper.lambda()
                .eq(User::getName, "John") // 姓名等于 John
                .set(User::getAge, 25);    // 将年龄更新为 25

        // 执行更新
        return userMapper.update(null, updateWrapper);
    }
}

在这个示例中,Wrappers.update() 方法创建了一个 UpdateWrapper 实例,同样通过调用 lambda() 方法切换到了 Lambda 表达式的模式。然后,你可以使用 eq 方法来指定更新条件,并使用 set 方法来指定要更新的字段和值。

需要注意的是,在 update 方法中,第一个参数通常为要更新的实体对象,但在这里我们传入 null,因为我们只关心条件部分,并且要更新的字段已经在 UpdateWrapper 中指定了。在实际使用中,如果需要根据实体对象的某些属性来更新(除了那些已经在 UpdateWrapper 中指定的),可以将该实体对象作为第一个参数传入。然而,如果只需要更新特定的字段,并且这些字段已经在 UpdateWrapper 中通过 set 方法指定了,那么传入 null 是更常见的做法。

相关推荐
向哆哆2 分钟前
Netty在Java网络编程中的应用:实现高性能的异步通信
java·网络·php
程序员爱钓鱼11 分钟前
循环语句:for、range -《Go语言实战指南》
java·数据结构·算法
wowocpp30 分钟前
Java项目层级介绍 java 层级 层次
java
码上飞扬41 分钟前
Java大师成长计划之第20天:Spring Framework基础
java·开发语言
wowocpp1 小时前
centos 7 安装 java 运行环境
java·linux·centos
Pluchon1 小时前
硅基计划2.0 学习总结 壹 Java初阶
java·开发语言·学习·算法
大模型最新论文速读1 小时前
在Text-to-SQL任务中应用过程奖励模型
数据库·人工智能·sql·深度学习·语言模型·自然语言处理
wowocpp1 小时前
Java MVC
java·开发语言·mvc
带刺的坐椅1 小时前
jFinal 使用 SolonMCP 开发 MCP(拥抱新潮流)
java·ai·solon·jfinal·mcp
陌尘(MoCheeen)2 小时前
技术书籍推荐(002)
java·javascript·c++·python·go