第二期:[特殊字符] 深入理解MyBatis[特殊字符]MyBatis基础CRUD操作详解[特殊字符]

前言 🌟

在掌握了 MyBatis 的基本配置与环境搭建之后,接下来的重点便是深入理解其核心功能------CRUD 操作(增删改查)。💻

数据库操作是任何应用开发中不可或缺的一环,而 MyBatis 正是通过灵活的 SQL 映射机制,极大地简化了这些操作的实现过程。本篇将围绕最常见的数据库基本操作展开,带你从传统 JDBC 的冗杂代码,迈向 MyBatis 优雅高效的开发方式。

我们将通过实际案例,结合注解与 XML 两种方式,逐一讲解如何使用 MyBatis 实现:

  • 新增数据 🆕

  • 删除数据 ❌

  • 更新数据 🔁

  • 查询数据 🔍

同时,还将分析每种实现方式的优劣,帮助你根据项目需求做出合理的选择。

无论你是初学者,还是希望进一步深入理解 MyBatis 的开发者,都能从这篇内容中收获实用技巧与开发灵感。🚀


基本操作回顾 🔁

MyBatis 的核心用途之一就是对数据库的 增删改查(CRUD) 操作进行简化和优化。在传统 JDBC 中,每次都需要写大量重复性的代码来完成这些操作,而在 MyBatis 中,你可以通过 注解XML 映射 两种方式优雅地完成同样的工作。

下面我们分别回顾这四种操作的基本语法,并通过两个方式(注解和 XML)进行展示。📘

1. 增、删、改、查的基本语法和实现方式 🔄

在数据库操作中,通常有四种基本操作:增(Insert)💾、删(Delete)🗑️、改(Update)🔧和查(Select)🔍。每种操作的基本语法如下:

  • 增(Insert) 🆕

    复制代码
    INSERT INTO table_name (column1, column2, column3, ...)
    VALUES (value1, value2, value3, ...);
  • 删(Delete)

    复制代码
    DELETE FROM table_name WHERE condition;
  • 改(Update) 🔄

    复制代码
    UPDATE table_name
    SET column1 = value1, column2 = value2, ...
    WHERE condition;
  • 查(Select) 👀

    复制代码
    SELECT column1, column2, column3, ...
    FROM table_name
    WHERE condition
    ORDER BY column1;

这些语法是进行数据库操作的基础,通常会结合特定的框架和技术来简化实现。🚀

2. SQL映射的定义(注解与XML方式) 📝

在 Java 中,使用 MyBatis 框架时,通常需要将 SQL 操作映射到 Java 方法。MyBatis 提供了两种方式来定义这些 SQL 映射:注解方式XML方式

注解方式 💡

在 MyBatis 中,你可以通过注解在接口方法上定义 SQL 语句。以下是常用的注解:

  • @Insert ✍️:用于插入数据。

  • @Update 🔄:用于更新数据。

  • @Delete 🗑️:用于删除数据。

  • @Select 🔍:用于查询数据。

示例:

复制代码
public interface UserMapper {
  
    @Insert("INSERT INTO users (name, age) VALUES (#{name}, #{age})")
    void insertUser(User user);
  
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectUserById(int id);
  
    @Update("UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}")
    void updateUser(User user);
  
    @Delete("DELETE FROM users WHERE id = #{id}")
    void deleteUser(int id);
}
XML方式 📝

在 XML 映射文件中,SQL 语句被定义在 <mapper> 标签中。每个 SQL 操作通过不同的标签(如 <insert>, <select>, <update>, <delete>)进行映射。

示例:

UserMapper.xml

复制代码
<mapper namespace="com.example.UserMapper">
  
    <insert id="insertUser" parameterType="User">
        INSERT INTO users (name, age)
        VALUES (#{name}, #{age})
    </insert>
  
    <select id="selectUserById" resultType="User">
        SELECT * FROM users WHERE id = #{id}
    </select>
  
    <update id="updateUser" parameterType="User">
        UPDATE users
        SET name = #{name}, age = #{age}
        WHERE id = #{id}
    </update>
  
    <delete id="deleteUser" parameterType="int">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>
🧩 注解 vs XML 映射方式对比
方式 优点 🌟 缺点 ⚠️ 适用场景 📌
注解方式 简洁直观,代码集中在一处 不适合复杂 SQL 语句 简单的增删改查
XML方式 结构清晰,适合复杂 SQL & 动态 SQL 映射文件和接口分离,维护略复杂 SQL 语句复杂、需灵活配置时
选择注解还是 XML? 🤔
  • 注解方式 ✅:简洁,适合小型项目或简单的 SQL 操作。

  • XML方式 📂:更灵活,适合复杂查询,且可以分离 SQL 和 Java 代码,提高可维护性。

一般来说,如果项目较小,且 SQL 操作较为简单,可以使用注解;而如果 SQL 语句复杂,或者希望保持 Java 代码与 SQL 的分离,XML 方式更加合适。🔧

在 MyBatis 中,插入操作(Insert)可以通过注解或 XML 配置两种方式实现。此外,利用 @Options 注解或 XML 配置中的 useGeneratedKeys 属性,可以实现插入数据后返回自增主键的功能。以下是对这两种方式的详细介绍和示例代码。


🆕 增操作(Insert)

在 MyBatis 中,执行插入操作主要有两种方式:使用 @Insert 注解 和 使用 XML 映射中的 <insert> 标签。两者都可以完成插入任务,但在处理返回主键时,写法稍有不同。

@Insert 注解与 XML 中 <insert> 标签的差异

1. 使用 @Insert 注解

@Insert 注解用于在 Mapper 接口中直接编写 SQL 语句,适用于简单的插入操作。例如:

复制代码
@Insert("INSERT INTO users(name, age) VALUES(#{name}, #{age})")
int addUser(User user);

这种方式简洁明了,便于快速开发。然而,当 SQL 语句较为复杂或需要动态构建时,注解方式的可读性和维护性会降低。

2. 使用 XML 中的 <insert> 标签

在 XML 配置中,使用 <insert> 标签定义插入操作,适用于复杂的 SQL 语句和动态 SQL 的构建。例如:

复制代码
<insert id="addUser" parameterType="User">
    INSERT INTO users(name, age)
    VALUES(#{name}, #{age})
</insert>

XML 配置方式提供了更高的灵活性和可维护性,特别是在处理复杂逻辑和动态 SQL 时更为合适。


利用 Options 配置返回自增主键

在插入数据后获取数据库生成的自增主键,可以通过以下两种方式实现:

1. 注解方式

在使用 @Insert 注解的同时,结合 @Options 注解配置返回主键的相关属性。例如:

复制代码
@Insert("INSERT INTO users(name, age) VALUES(#{name}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
int addUser(User user);

  • useGeneratedKeys = true:表示使用数据库自动生成的主键。

  • keyProperty = "id":指定将生成的主键值赋给 User 对象的 id 属性。

  • keyColumn = "id":指定数据库表中的主键列名。

这样配置后,插入操作执行完毕后,User 对象的 id 属性将自动填充为数据库生成的主键值。

2. XML 配置方式

在 XML 中的 <insert> 标签中设置相关属性,实现相同的功能。例如:

复制代码
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    INSERT INTO users(name, age)
    VALUES(#{name}, #{age})
</insert>

配置说明与注解方式相同,执行插入操作后,User 对象的 id 属性将被自动赋值为生成的主键。


示例代码

以下是一个完整的示例,展示如何使用注解方式插入数据并返回自增主键:

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

在调用 addUser 方法后,User 对象的 id 属性将被自动填充为数据库生成的主键值。

如果使用 XML 配置方式,Mapper 接口方法可以简化为:

复制代码
int addUser(User user);

对应的 XML 配置如下:

复制代码
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    INSERT INTO users(name, age)
    VALUES(#{name}, #{age})
</insert>

这样配置后,同样可以在插入数据后自动获取并设置主键值。


❌ 删操作(Delete) & 🔁 改操作(Update)

在 MyBatis 中,删除(Delete)和更新(Update)操作可以通过注解或 XML 配置两种方式实现。在使用这些操作时,参数传递和绑定是关键,尤其是在方法参数较多或参数名与 SQL 中不一致时,合理使用 @Param 注解至关重要,除了掌握基本语法外,参数传递与绑定方式 是非常关键的一环,尤其是当方法中传入多个参数或参数名与 SQL 占位符不一致时,可能导致绑定失败或运行报错。

一.🧠 参数传递注意点

  1. 单个参数

    • 当方法只接受一个参数时,MyBatis 可以自动识别参数名,无需使用 @Param 注解。

    • 例如:

      复制代码
      @Delete("DELETE FROM users WHERE id = #{id}")
      int deleteUserById(int id);
  2. 多个参数

    • 当方法接受多个参数时,MyBatis 默认将参数命名为 param1param2 等,可能导致 SQL 中的参数名与方法参数不一致。

    • 为避免这种情况,建议使用 @Param 注解为每个参数指定名称。

    • 例如:

      复制代码
      @Update("UPDATE users SET name = #{name} WHERE id = #{id}")
      int updateUser(@Param("id") int id, @Param("name") String name);
  3. 使用 JavaBean 作为参数

    • 如果方法参数是一个 JavaBean 对象,MyBatis 会根据对象的属性名进行参数绑定,无需使用 @Param 注解。

    • 例如:

      复制代码
      @Update("UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}")
      int updateUser(User user);
  4. 使用 Map 作为参数

    • 当方法参数为 Map 类型时,SQL 中的参数名应与 Map 的键一致。

    • 例如:

      复制代码
      @Delete("DELETE FROM users WHERE id = #{userId}")
      int deleteUser(Map<String, Object> params);

二、注解方式实现删除和更新操作

1. 删除操作

  • 单个参数

    复制代码
    @Delete("DELETE FROM users WHERE id = #{id}")
    int deleteUserById(int id);

  • 多个参数(使用 @Param)

    复制代码
    @Delete("DELETE FROM users WHERE id = #{id} AND status = #{status}")
    int deleteUser(@Param("id") int id, @Param("status") String status);

2. 更新操作

  • 使用 JavaBean 作为参数

    复制代码
    @Update("UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}")
    int updateUser(User user);

  • 多个参数(使用 @Param)

    复制代码
    @Update("UPDATE users SET name = #{name} WHERE id = #{id}")
    int updateUser(@Param("id") int id, @Param("name") String name);


三、XML 方式实现删除和更新操作

1. 删除操作

复制代码
<delete id="deleteUserById" parameterType="int">
    DELETE FROM users WHERE id = #{id}
</delete>

2. 更新操作

  • 使用 JavaBean 作为参数

    复制代码
    <update id="updateUser" parameterType="User">
        UPDATE users
        SET name = #{name}, age = #{age}
        WHERE id = #{id}
    </update>

  • 多个参数(使用 @Param)

    复制代码
    <update id="updateUser" parameterType="map">
        UPDATE users
        SET name = #{name}
        WHERE id = #{id}
    </update>


四、使用 @Param 注解的最佳实践

  • 多个参数时使用 @Param :当方法有多个参数时,使用 @Param 注解为每个参数指定名称,以确保 SQL 中的参数名与方法参数一致。

  • **避免使用 {}** :在 SQL 中使用 `{}会直接拼接字符串,可能导致 SQL 注入风险。建议使用#{}` 进行参数绑定。

  • 参数名与 SQL 中一致 :确保方法参数名与 SQL 中的参数名一致,或通过 @Param 注解进行明确绑定。


🔍 查询操作(Select)

在 MyBatis 中,查询是使用最频繁、最关键的操作之一。无论是查询单条记录还是查询列表,MyBatis 提供了丰富的方式来处理返回单个对象和列表的场景,同时也支持自动映射(result mapping)的机制,以便把数据库查询结果转换为 Java 对象。

但自动映射不是"万能"的,字段名不一致时容易出现问题,本节将详解如何解决这些常见坑点 👇


一、查询单个对象与列表的差异

  • 单个对象查询

    通常使用方法返回类型为对象(如 User)的查询接口。当查询结果只返回一行数据时,这样能直接将返回值自动映射到对应的 Java 对象。例如:

    复制代码
    @Select("SELECT id, name, age FROM users WHERE id = #{id}")
    User getUserById(int id);

    如果查询结果为空或多条记录,则可能会导致异常,此时需要保证查询条件唯一。

  • 列表查询

    当查询的结果可能包含多行数据时,接口返回值一般为 List<User>。MyBatis 会遍历每一行数据并调用映射规则将每行数据封装成一个 Java 对象,最终返回一个列表。例如:

    复制代码
    @Select("SELECT id, name, age FROM users")
    List<User> getAllUsers();

二、自动映射原理与字段映射问题

MyBatis 能自动将数据库查询的结果映射到 Java 对象中,但前提是 SQL 返回的列名和 Java 对象的属性名需要保持一致。实际应用中常遇到如下问题及相应解决方法:

1. 起别名(Alias)

如果数据库表中的列名与 Java 对象的属性名不一致,可以在 SQL 中使用别名。例如:

复制代码
@Select("SELECT id, name AS userName, age FROM users WHERE id = #{id}")
User getUserById(int id);

这样 MyBatis 就能将查询结果中的 userName 列映射到 Java 对象中的 userName 属性上。

2. 结果映射(Result Map)

对于更复杂的情况,可以在 XML 中通过 <resultMap> 标签来定义映射关系。这样不仅灵活,而且可在一个地方统一管理映射规则。例如:

复制代码
<resultMap id="userResultMap" type="com.example.User">
    <id column="id" property="id"/>
    <result column="name" property="userName"/>
    <result column="age" property="age"/>
</resultMap>

<select id="getUserById" resultMap="userResultMap" parameterType="int">
    SELECT id, name, age FROM users WHERE id = #{id}
</select>

此方式适用于字段较多、数据结构较复杂或者需要复用映射规则的场景。

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

MyBatis 还支持通过全局配置来自动将下划线命名转换为驼峰命名,这样数据库列 user_name 会自动映射到 Java 属性 userName。可以在配置文件中启用该功能:

复制代码
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- 也可以设置日志输出,见下节 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

或者在 application.yml 中(使用 MyBatis-Spring Boot Starter 时)这样配置:

复制代码
mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

这种方式使得数据库列名与 Java 属性之间的差异无需在每个 SQL 中显式别名,从而减少了维护成本。


三、配置日志打印 SQL、参数及执行结果

为了帮助调试,MyBatis 允许开发者配置日志输出,打印 SQL 语句、参数绑定以及执行结果。主要方法有:

1. 配置 MyBatis 日志实现

MyBatis 支持多种日志实现(例如:STDOUT_LOGGING、LOG4J、SLF4J 等)。可以在 MyBatis 配置文件中设置日志实现:

复制代码
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

此配置会将所有 SQL 执行、参数绑定以及执行结果输出到控制台。

2. 配置日志框架

如果项目中使用 Log4j2 或 SLF4J 等日志框架,则需要在日志配置文件中启用 MyBatis 相关的日志记录器。例如,在 Log4j2 的配置文件中:

复制代码
<Logger name="org.apache.ibatis" level="DEBUG" additivity="false">
    <AppenderRef ref="Console"/>
</Logger>

这样便能确保 MyBatis 执行过程中的详细日志输出到控制台,便于调试和性能分析。

3. 配置 application.yml 打印日志

复制代码
logging:
  level:
    com.yourpackage.mapper: debug
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4. 运行时调试效果

调试过程中,会看到类似下面的日志输出:

复制代码
DEBUG - ==>  Preparing: SELECT id, name, age FROM users WHERE id = ? 
DEBUG - ==> Parameters: 1(Integer)
DEBUG - <==      Total: 1

这显示了待执行的 SQL 语句、参数绑定情况以及执行结果的行数,极大地帮助开发者定位问题。


示例代码总结

注解方式示例(带驼峰配置)

复制代码
@Mapper
public interface UserMapper {
    @Select("SELECT id, name AS userName, age FROM users WHERE id = #{id}")
    User getUserById(int id);

    @Select("SELECT id, name AS userName, age FROM users")
    List<User> getAllUsers();
}

XML 配置示例(使用 resultMap)

复制代码
<resultMap id="userResultMap" type="com.example.User">
    <id column="id" property="id"/>
    <result column="name" property="userName"/>
    <result column="age" property="age"/>
</resultMap>

<select id="getUserById" resultMap="userResultMap" parameterType="int">
    SELECT id, name, age FROM users WHERE id = #{id}
</select>

<select id="getAllUsers" resultMap="userResultMap">
    SELECT id, name, age FROM users
</select>

全局配置开启驼峰命名与日志输出(例如,在 mybatis-config.xml 中)

复制代码
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

案例说明(JDBC和Mybaits操作数据库案例对比)

假设有一个简单的 User 表,结构如下(MySQL 语法示例):

复制代码
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(50) NOT NULL,
  password VARCHAR(255) NOT NULL,
  email VARCHAR(100),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

对应的 Java 实体类 User 如下:

复制代码
public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private Timestamp createdAt;

    // Getters and Setters
}

Part 1:基于 JDBC 实现 CRUD

1.1 JDBC 工具类

创建一个简单的 JDBC 工具类,用于获取数据库连接并释放资源。

复制代码
import java.sql.*;

public class JDBCUtil {
    private static final String URL = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root123";

    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USERNAME, PASSWORD);
    }

    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        try { if(rs != null) rs.close(); } catch (Exception e) { e.printStackTrace(); }
        try { if(stmt != null) stmt.close(); } catch (Exception e) { e.printStackTrace(); }
        try { if(conn != null) conn.close(); } catch (Exception e) { e.printStackTrace(); }
    }
}

1.2 JDBC 实现 CRUD 的 DAO

复制代码
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class UserJDBCDao {

    // 增
    public int insertUser(User user) {
        String sql = "INSERT INTO users(username, password, email) VALUES (?, ?, ?)";
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = JDBCUtil.getConnection();
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, user.getUsername());
            pstmt.setString(2, user.getPassword());
            pstmt.setString(3, user.getEmail());
            int rows = pstmt.executeUpdate();

            // 获取自增主键
            ResultSet rs = pstmt.getGeneratedKeys();
            if (rs.next()) {
                user.setId(rs.getInt(1));
            }
            rs.close();
            return rows;
        } catch (SQLException e) {
            e.printStackTrace();
            return 0;
        } finally {
            JDBCUtil.close(conn, pstmt, null);
        }
    }

    // 删
    public int deleteUserById(int id) {
        String sql = "DELETE FROM users WHERE id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = JDBCUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, id);
            return pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
            return 0;
        } finally {
            JDBCUtil.close(conn, pstmt, null);
        }
    }

    // 改
    public int updateUserEmail(int id, String newEmail) {
        String sql = "UPDATE users SET email = ? WHERE id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = JDBCUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, newEmail);
            pstmt.setInt(2, id);
            return pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
            return 0;
        } finally {
            JDBCUtil.close(conn, pstmt, null);
        }
    }

    // 查 ------ 查询单个
    public User getUserById(int id) {
        String sql = "SELECT * FROM users WHERE id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, id);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setPassword(rs.getString("password"));
                user.setEmail(rs.getString("email"));
                user.setCreatedAt(rs.getTimestamp("created_at"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(conn, pstmt, rs);
        }
        return null;
    }

    // 查 ------ 查询列表
    public List<User> getAllUsers() {
        String sql = "SELECT * FROM users";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        List<User> list = new ArrayList<>();
        try {
            conn = JDBCUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            while (rs.next()) {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setPassword(rs.getString("password"));
                user.setEmail(rs.getString("email"));
                user.setCreatedAt(rs.getTimestamp("created_at"));
                list.add(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(conn, pstmt, rs);
        }
        return list;
    }
}

1.3 JDBC 测试代码示例

复制代码
public class TestJDBC {
    public static void main(String[] args) {
        UserJDBCDao dao = new UserJDBCDao();
        
        // 插入数据
        User newUser = new User();
        newUser.setUsername("Bob");
        newUser.setPassword("pass123");
        newUser.setEmail("[email protected]");
        int insertRows = dao.insertUser(newUser);
        System.out.println("JDBC 插入行数:" + insertRows + ", 生成的ID:" + newUser.getId());
        
        // 更新数据
        int updateRows = dao.updateUserEmail(newUser.getId(), "[email protected]");
        System.out.println("JDBC 更新行数:" + updateRows);
        
        // 查询数据
        User queryUser = dao.getUserById(newUser.getId());
        System.out.println("JDBC 查询到的用户:" + queryUser.getUsername() + ", Email:" + queryUser.getEmail());
        
        // 删除数据
        int deleteRows = dao.deleteUserById(newUser.getId());
        System.out.println("JDBC 删除行数:" + deleteRows);
    }
}

JDBC 实现的优缺点

  • 优点:

    • 灵活度高,对连接、事务、异常处理有完全控制
  • 缺点:

    • 大量重复代码(获取连接、释放资源、异常处理)

    • 代码冗长,可读性和维护性较差


Part 2:基于 MyBatis 重构 CRUD

2.1 MyBatis 配置

配置文件(例如 application.yml 部分):

复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
    username: root
    password: root123
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.demo.model
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.2 MyBatis Mapper 接口与映射文件

Mapper 接口(UserMapper.java)
复制代码
package com.example.demo.mapper;

import com.example.demo.model.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

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

    @Delete("DELETE FROM users WHERE id = #{id}")
    int deleteUserById(@Param("id") int id);

    @Update("UPDATE users SET email = #{email} WHERE id = #{id}")
    int updateUserEmail(@Param("id") int id, @Param("email") String email);

    @Select("SELECT * FROM users WHERE id = #{id}")
    User getUserById(@Param("id") int id);

    @Select("SELECT * FROM users")
    List<User> getAllUsers();
}
对应 XML 映射文件(可选,下面以注解方式为主,略)

如果需要使用 XML 映射,可以将 SQL 语句放在 resources/mapper/UserMapper.xml 中,并在接口中不采用注解。

2.3 MyBatis 测试代码示例

使用 Spring Boot 进行单元测试:

复制代码
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testInsertUser() {
        User user = new User();
        user.setUsername("Alice");
        user.setPassword("alicepass");
        user.setEmail("[email protected]");
        
        int rows = userMapper.insertUser(user);
        System.out.println("MyBatis 插入行数:" + rows);
        System.out.println("生成的主键 ID:" + user.getId());
    }

    @Test
    public void testUpdateUserEmail() {
        int rows = userMapper.updateUserEmail(1, "[email protected]");
        System.out.println("MyBatis 更新行数:" + rows);
    }

    @Test
    public void testGetUserById() {
        User user = userMapper.getUserById(1);
        System.out.println("MyBatis 查询到的用户:" + user.getUsername() + ", Email:" + user.getEmail());
    }

    @Test
    public void testDeleteUser() {
        int rows = userMapper.deleteUserById(1);
        System.out.println("MyBatis 删除行数:" + rows);
    }

    @Test
    public void testGetAllUsers() {
        List<User> users = userMapper.getAllUsers();
        users.forEach(user -> System.out.println("用户:" + user.getUsername() + ", Email:" + user.getEmail()));
    }
}

MyBatis 实现的优缺点

  • 优点:

    • 极大减少重复代码,无需手动管理连接和资源

    • 通过注解或 XML 映射,SQL 与 Java 代码分离,维护性更高

    • 内置功能强大,例如动态 SQL、自动映射、日志输出等

  • 缺点:

    • 初期配置及学习曲线略高

    • 对于非常简单的场景,可能显得"杀鸡用牛刀"


Part 3:实际测试结果对比(IDEA 运行日志截图模拟)

JDBC 版运行日志(示例输出):

复制代码
JDBC 插入行数:1, 生成的ID:10
JDBC 更新行数:1
JDBC 查询到的用户:Bob, Email:[email protected]
JDBC 删除行数:1

说明:运行过程中,各步骤都需要手动捕获异常及关闭连接,在 IDEA 控制台可见大量重复日志信息。


MyBatis 版运行日志(示例输出):

复制代码
==>  Preparing: INSERT INTO users(username, password, email) VALUES (?,?,?)
==>  Parameters: Alice(String), alicepass(String), [email protected](String)
<==    Updates: 1
MyBatis 插入行数:1
生成的主键 ID:12

==>  Preparing: UPDATE users SET email = ? WHERE id = ?
==>  Parameters: [email protected](String), 1(Integer)
<==    Updates: 1
MyBatis 更新行数:1

==>  Preparing: SELECT * FROM users WHERE id = ?
==>  Parameters: 1(Integer)
<==      Total: 1
MyBatis 查询到的用户:Alice, Email:[email protected]

==>  Preparing: DELETE FROM users WHERE id = ?
==>  Parameters: 1(Integer)
<==    Updates: 1
MyBatis 删除行数:1

说明:通过 MyBatis 的日志配置,可直接在控制台看到 SQL 语句、绑定参数及执行结果,大幅提升调试效率与可读性。


总结对比

  • 代码量:

    JDBC 实现需要大量重复代码(连接、语句、资源关闭等),而 MyBatis 通过映射注解和配置文件大大减少了冗余。

  • 易用性:

    使用 MyBatis 后,开发者可以专注于 SQL 和业务逻辑,无需关注细节的资源管理;同时日志配置方便调试。

  • 维护性:

    JDBC 代码中 SQL 嵌入 Java 中导致耦合度高,修改一处需要重新编译;而 MyBatis 通过 XML 或注解实现 SQL 与 Java 的分离,使得后期维护更加高效。

  • 调试:

    MyBatis 内置日志输出(显示具体 SQL、参数、执行结果),使问题排查变得简单直观,而 JDBC 的日志需要自行实现和维护。


结论

通过上面的案例演示,可以看到 MyBatis 在开发体验、代码维护与调试方面均优于传统的 JDBC 实现。在现代项目中,推荐优先考虑基于 MyBatis 或类似框架来进行数据持久化操作,从而提高整体开发效率与代码可维护性。


实际截图说明:

在 IDEA 中运行测试时,你可以截取各个测试方法的控制台日志。

例如:

  • JDBC 版的日志截图显示"JDBC 插入行数:1, 生成的ID:10"等信息。

  • MyBatis 版截图则包含了标准输出的 SQL 语句、参数及执行统计信息(类似上面的日志示例)。

由于本回答无法直接上传图片,你可以自行在 IDEA 中运行上述测试代码并截图保存以作参考。


希望这个完整的小案例能帮助你深刻理解传统 JDBC 与 MyBatis 在 CRUD 操作上的差异,并体会到 MyBatis 在简化代码和提高开发效率方面的优势!


结语 🎉

本文详细讲解了 MyBatis 基础 CRUD 操作的各个方面,从增删改查的基本语法到注解和 XML 映射方式的具体实现,再到参数绑定、自动映射和日志调试的使用技巧。通过这些示例与对比,我们可以发现:

  • 简化开发流程:MyBatis 通过灵活的 SQL 映射机制,大幅减少了传统 JDBC 开发中冗长且重复的代码。

  • 增强代码可维护性:SQL 语句与 Java 代码的分离使得代码结构更清晰,修改和扩展更加方便,解决了 SQL 与业务逻辑耦合的问题。

  • 方便调试与追踪:借助日志配置,开发者能够直观地查看 SQL 语句、参数绑定和执行结果,显著提高了调试效率。

  • 灵活应对复杂场景:无论是简单 CRUD 操作还是复杂的动态 SQL 构造,MyBatis 都能提供有效的支持,满足不同项目的需求。

总体来看,MyBatis 为开发者提供了一个高效、灵活和易维护的数据持久层解决方案,使得构建健壮和可扩展的企业级应用成为可能。希望本篇文章能帮助你更好地理解和掌握 MyBatis 的核心技术,并在实际项目中充分发挥其优势!🚀

继续深入探索 MyBatis 及其它持久化技术,你的开发技能必将更上一层楼!😊

相关推荐
silence2501 小时前
解决 Maven 500 错误:无法传输 maven-metadata.xml 文件
maven
一一Null1 小时前
Android studio 动态布局
android·java·android studio
假女吖☌1 小时前
Maven 编译指定模版
java·开发语言·maven
体育分享_大眼3 小时前
从零搭建高并发体育直播网站:架构设计、核心技术与性能优化实战
java·性能优化·系统架构
琢磨先生David4 小时前
Java 在人工智能领域的突围:从企业级架构到边缘计算的技术革新
java·人工智能·架构
计算机学姐5 小时前
基于SpringBoo的地方美食分享网站
java·vue.js·mysql·tomcat·mybatis·springboot·美食
Hanson Huang7 小时前
【数据结构】堆排序详细图解
java·数据结构·排序算法·堆排序
路在脚下@8 小时前
Redis实现分布式定时任务
java·redis
xrkhy8 小时前
idea的快捷键使用以及相关设置
java·ide·intellij-idea
巨龙之路8 小时前
Lua中的元表
java·开发语言·lua