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 是更常见的做法。

相关推荐
isolusion2 分钟前
Springboot的创建方式
java·spring boot·后端
zjw_rp30 分钟前
Spring-AOP
java·后端·spring·spring-aop
Oneforlove_twoforjob43 分钟前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言
TodoCoder1 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
小蜗牛慢慢爬行1 小时前
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
java·架构·hibernate
星河梦瑾2 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富2 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想2 小时前
JMeter 使用详解
java·jmeter
言、雲2 小时前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库