第二期:[特殊字符] 深入理解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.pswp.cn/pingmian/76706.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76706.shtml
英文地址,請注明出處:http://en.pswp.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;…