第二期:[特殊字符] 深入理解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 usersSET 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 usersSET name = #{name}, age = #{age}WHERE id = #{id}</update>

  • 多个参数(使用 @Param)

  <update id="updateUser" parameterType="map">UPDATE usersSET 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: truelog-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("bob@example.com");int insertRows = dao.insertUser(newUser);System.out.println("JDBC 插入行数:" + insertRows + ", 生成的ID:" + newUser.getId());// 更新数据int updateRows = dao.updateUserEmail(newUser.getId(), "bob_new@example.com");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=UTCusername: rootpassword: root123driver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.example.demo.modelconfiguration:map-underscore-to-camel-case: truelog-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 {@Autowiredprivate UserMapper userMapper;@Testpublic void testInsertUser() {User user = new User();user.setUsername("Alice");user.setPassword("alicepass");user.setEmail("alice@example.com");int rows = userMapper.insertUser(user);System.out.println("MyBatis 插入行数:" + rows);System.out.println("生成的主键 ID:" + user.getId());}@Testpublic void testUpdateUserEmail() {int rows = userMapper.updateUserEmail(1, "alice_new@example.com");System.out.println("MyBatis 更新行数:" + rows);}@Testpublic void testGetUserById() {User user = userMapper.getUserById(1);System.out.println("MyBatis 查询到的用户:" + user.getUsername() + ", Email:" + user.getEmail());}@Testpublic void testDeleteUser() {int rows = userMapper.deleteUserById(1);System.out.println("MyBatis 删除行数:" + rows);}@Testpublic 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:bob_new@example.com
JDBC 删除行数:1

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


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

==>  Preparing: INSERT INTO users(username, password, email) VALUES (?,?,?)
==>  Parameters: Alice(String), alicepass(String), alice@example.com(String)
<==    Updates: 1
MyBatis 插入行数:1
生成的主键 ID:12==>  Preparing: UPDATE users SET email = ? WHERE id = ?
==>  Parameters: alice_new@example.com(String), 1(Integer)
<==    Updates: 1
MyBatis 更新行数:1==>  Preparing: SELECT * FROM users WHERE id = ?
==>  Parameters: 1(Integer)
<==      Total: 1
MyBatis 查询到的用户:Alice, Email:alice_new@example.com==>  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 及其它持久化技术,你的开发技能必将更上一层楼!😊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76706.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java面试黄金宝典46

1. Python 如何写爬虫 定义:Python 爬虫是借助 Python 语言编写程序,模拟浏览器行为向目标网站发送 HTTP 请求,获取网页内容,再通过解析工具提取所需数据的程序。其本质是自动化的数据采集过程。要点: 发送请求:利用requests库发送 HTTP 请求,如 GET、POST 等,获取网页…

建设“大数据智慧招商平台”,助力园区突破招商瓶颈!

在数字经济高速发展的今天&#xff0c;传统招商模式正面临信息不对称、效率低下、匹配不精准等瓶颈。产业园区作为区域经济发展的核心载体&#xff0c;亟需借助智能化手段提升招商效能。构建大数据智慧招商平台&#xff0c;利用大数据、人工智能等技术获取精准招商线索、促进产…

Vue事件修饰符课堂练习

Vue事件修饰符课堂练习 题目‌&#xff1a;基于 Vue 2.0&#xff0c;使用事件修饰符 .stop、.prevent、.capture、.self 和 .once&#xff0c;为按钮绑定 click 事件&#xff0c;并展示每个修饰符的作用。 要求‌&#xff1a; 创建一个 Vue 实例&#xff0c;并绑定到一个 HT…

【C#】线程回调

在 C# 中&#xff0c;线程回调是一种常见的编程模式&#xff0c;用于在线程完成任务后执行某些操作。通过使用 Thread 类或其他更高层次的并发工具&#xff08;如 Task&#xff09;&#xff0c;可以实现线程回调的功能。 回调机制 特点 直接性&#xff1a;回调通常是通过委托…

【C++游戏引擎开发】第14篇:视图空间与相机坐标系

一、视图空间的基础数学框架 1.1 齐次坐标与变换矩阵 三维坐标系变换采用44齐次坐标矩阵,其通用形式为: M = [ A 3 3 b 3 1 0 1 3 1 ] \mathbf{M} = \begin{bmatrix} \mathbf{A}_{33} & \mathbf{b}_{31} \\ \mathbf{0}_{13} & 1 \end{bmatrix} M=[A33​013​​…

【大模型理论篇】关于生成式模型中联合分布概率学习必要性以及GPT是生成式模型的讨论

1. 背景 之前我们在《生成式模型与判别式模型对比(涉及VAE、CRF的数学原理详述)》以及《生成式模型算法原理深入浅出&#xff08;涉及Stable Diffusion、生成对抗网络、高斯混合模型、隐马尔可夫模型、朴素贝叶斯等算法原理分析及生成式模型解释&#xff09;》中&#xff0c;我…

DIP支付方式改革下各种疾病医疗费用的影响以及分析方法研究综述

DIP支付方式改革下各种疾病医疗费用的影响以及分析方法研究综述 摘要 本文综述了DIP支付方式改革对不同疾病医疗费用的影响及其分析方法&#xff0c;通过分析12篇相关文献&#xff0c;探讨了DIP支付方式在控制医疗费用、优化费用结构、提升医疗服务效率等方面的作用及其局限性…

嵌入式硬件篇---单片机周期

文章目录 前言 前言 在单片机中&#xff0c;时序控制是其执行指令和协调外设的核心基础。以下是单片机中常见的各种周期及其详细说明&#xff0c;以层次结构展开&#xff1a; 时钟周期&#xff08;Clock Cycle&#xff09; 定义&#xff1a; 时钟周期是单片机的最小时间单位&a…

游戏引擎学习第221天:(实现多层次过场动画)

资产: intro_art.hha 已发布 在下载页面&#xff0c;你会看到一个新的艺术包。你将需要这个艺术包来进行接下来的开发工作。这个艺术包是由一位艺术家精心制作并打包成我们设计的格式&#xff0c;旨在将这些艺术资源直接应用到游戏中。它包含了许多我们会在接下来的直播中使用…

【3GPP核心网】【5G】精讲5G系统的策略和计费控制框架

1. 欢迎大家订阅和关注,精讲3GPP通信协议(2G/3G/4G/5G/IMS)知识点,专栏会持续更新中.....敬请期待! 目录 1. 系统架构 1.1 非漫游架构 1.2 漫游架构 1.3 支持Rx接口 2. 服务化接口及参考点 2.1 PCF 与 AF 间接口 2.2 PCF与SMF间接口 2.3 PCF与AMF间接口 2.4 V-PC…

榕壹云门店管理系统:基于Spring Boot+Mysql+UniApp的智慧解决方案

项目背景&#xff1a;数字化赋能服务行业&#xff0c;破解传统门店管理痛点 在消费升级与数字化转型浪潮下&#xff0c;传统服务行业&#xff08;如美容、美发、美甲、采耳等&#xff09;面临诸多管理挑战&#xff1a;会员流失率高、预约排班混乱、员工绩效统计低效、数据孤岛等…

开发效率提升200%——cursor

cursor带来的编程"革命" 高级语言编程转为"自然语言编程"借助cursor&#xff0c;直接超越初级后台开发、超越初级前端开发、超越初级测试、超越初级UI&#xff0c;产研一体linux命令只用学不用记&#xff0c;语言描述就是命令给一个表结构流程提示词&…

UE4 踩坑记录

1、Using git status to determine working set for adaptive non-unity build 我删除了一个没用的资源&#xff0c;结果就报这个错&#xff0c;原因就是这条命令导致的&#xff0c; 如果这个项目是git项目&#xff0c; ue编译时会优先通过 git status检查哪些文件被修改&#…

蓝桥杯 2025 C++组 省 B 题解

可分解的正整数 算法&#xff1a;思维 因为可以有负数 所以除了1以外的任何数都可以构造 当这个数为x构造方法为 -(x-1) -(x-2) -(x-3) ....-1 0 1...x-3 x-2 x-1 x 除了x&#xff0c;x以前的数都会被负数抵消 #include <bits/stdc.h> #define ll long long ll a…

docker创建容器添加启动--restart选项

一、通过 Docker 命令直接修改已启动的容器&#xff08;推荐-已验证&#xff09; 操作步骤&#xff1a; 1.执行更新命令&#xff1a; docker update --restartalways <容器名或ID>此命令会将容器的重启策略调整为 always&#xff08;无论容器以何种状态退出&#xff0…

redission锁释放失败处理

redission锁释放失败处理 https://www.jianshu.com/p/055ae798547a 就是可以删除 锁的key 这样锁就释放了&#xff0c;但是 还是要结合业务&#xff0c;这种是 非正规的处理方式&#xff0c;还是要在代码层面进行处理。

【语音识别】vLLM 部署 Whisper 语音识别模型指南

目录 1. 模型下载 2. 环境安装 3. 部署脚本 4. 服务测试 语音识别技术在现代人工智能应用中扮演着重要角色&#xff0c;OpenAI开源的Whisper模型以其出色的识别准确率和多语言支持能力成为当前最先进的语音识别解决方案之一。本文将详细介绍如何使用vLLM&#xff08;一个高…

Windows Server 2019 安装 Docker 完整指南

博主本人使用的是离线安装 1. 安装前准备 系统要求 操作系统&#xff1a;Windows Server 2019&#xff08;或 2016/2022&#xff09;权限&#xff1a;管理员权限的 PowerShell网络&#xff1a;可访问互联网&#xff08;或离线安装包&#xff09; 启用容器功能 Install-Win…

C# 混淆代码工具--ConfuserEx功能与使用指南

目录 1 前言1.1 可能带来的问题 2 ConfuserEx2.1 简介2.2 功能特点2.3 基本使用方法2.4 集成到MSBuild2.5 深入设置2.5.1 保护机制2.5.1.1 ConfuserEx Protection 2.5.2 精细的代码保护主要特性1. decl-type(string)2.full-name(string)3. is-public()4. match(string)5. match…

c# 新建不重名的唯一文件夹

在源文件夹内创建唯一目标文件夹 string newFolder GetUniqueFolderName(sourceFolder); Directory.CreateDirectory(newFolder); /// <summary>/// 生成唯一文件夹名称&#xff08;格式&#xff1a;新建文件夹、新建文件夹1、新建文件夹2...&#xff09;…