欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解如何使用Mybatis的攔截器

 更新時間:2024年03月07日 08:21:14   作者:一只愛擼貓的程序猿  
MyBatis?攔截器是?MyBatis?提供的一個強大特性,它允許你在?MyBatis?執(zhí)行其核心邏輯的關鍵節(jié)點插入自定義邏輯,從而改變?MyBatis?的默認行為,本文給大家詳細介紹了如何使用Mybatis的攔截器,需要的朋友可以參考下

MyBatis 攔截器是 MyBatis 提供的一個強大特性,它允許你在 MyBatis 執(zhí)行其核心邏輯的關鍵節(jié)點插入自定義邏輯,從而改變 MyBatis 的默認行為。這類似于面向切面編程(AOP),允許開發(fā)者在不改變原有代碼的情況下,增強或修改原有功能。

在 MyBatis 中,攔截器可以應用于以下四種類型的對象:

Executor:負責 MyBatis 中的 SQL 執(zhí)行過程,攔截 Executor 可以在 SQL 執(zhí)行前后添加自定義邏輯。

Executor 的核心職責

在 MyBatis 中,Executor 接口定義了數據庫操作的核心方法,如 update、querycommit、rollback 等。MyBatis 提供了幾種 Executor 的實現,例如 SimpleExecutor、ReuseExecutor、BatchExecutor 等,它們各自有不同的特點和用途。

SimpleExecutor 為例,它是最基本的 Executor 實現,每次操作都會創(chuàng)建一個新的 Statement 對象。

源碼解析

以下是 Executor 接口中 query 方法的一個簡化版實現,展示了 MyBatis 如何執(zhí)行一個查詢操作:

public class SimpleExecutor extends BaseExecutor {

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 準備 SQL
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = prepareStatement(handler, ms.getStatementLog());
            return handler.query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

    private Statement prepareStatement(StatementHandler handler, Log statementLog) {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
    }
}

這段代碼展示了查詢操作的基本流程:創(chuàng)建 StatementHandler,準備和參數化 Statement,執(zhí)行查詢,最后關閉 Statement。

攔截器的實現和應用

攔截器允許開發(fā)者在 MyBatis 核心操作執(zhí)行的關鍵節(jié)點插入自定義邏輯。攔截器需要實現 Interceptor 接口,并定義要攔截的目標和方法。

當 MyBatis 初始化時,它會檢測配置中的攔截器,并在執(zhí)行相應操作時調用這些攔截器。攔截器中的 intercept 方法將在目標方法執(zhí)行時被調用。

public class ExampleExecutorInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 在 SQL 執(zhí)行前的邏輯
        System.out.println("Before executing query");

        // 執(zhí)行原有邏輯
        Object returnValue = invocation.proceed();

        // 在 SQL 執(zhí)行后的邏輯
        System.out.println("After executing query");

        return returnValue;
    }
}

在這個例子中,intercept 方法實現了在查詢執(zhí)行前后打印消息的邏輯。invocation.proceed() 是關鍵,它觸發(fā)了原本的操作(如查詢)。通過這種方式,開發(fā)者可以在不修改原有代碼的基礎上增強或改變 MyBatis 的行為。

ParameterHandler:負責 MyBatis 中的參數處理,通過攔截 ParameterHandler,可以在 SQL 語句綁定參數前后添加自定義邏輯。

ParameterHandler 的工作原理

ParameterHandler 的主要職責是為 SQL 語句綁定正確的參數值。在執(zhí)行 SQL 之前,MyBatis 會通過 ParameterHandler 接口的實現類來遍歷方法傳入的參數,并將它們設置到 JDBC 的 PreparedStatement 中。

MyBatis 默認提供了 DefaultParameterHandler 類作為 ParameterHandler 接口的實現,用于處理參數的設置工作。

源碼解析

下面是一個簡化的 ParameterHandler 使用示例,演示了如何在 MyBatis 中處理參數綁定:

public class DefaultParameterHandler implements ParameterHandler {
    private final MappedStatement mappedStatement;
    private final Object parameterObject;
    private final BoundSql boundSql;
    private final TypeHandlerRegistry typeHandlerRegistry;

    public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        this.mappedStatement = mappedStatement;
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    }

    @Override
    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = mappedStatement.getConfiguration().newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = configuration.getJdbcTypeForNull();
                    }
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                }
            }
        }
    }
}

在上述代碼中,setParameters 方法是核心,它負責將參數值綁定到 PreparedStatement。首先,它會遍歷所有的 ParameterMapping,這些映射信息定義了如何從參數對象中獲取具體的值。然后,它使用相應的 TypeHandler 來處理參數值的類型轉換,并將轉換后的值設置到 PreparedStatement 中。

攔截器的應用

通過攔截 ParameterHandlersetParameters 方法,可以在參數綁定前后插入自定義邏輯。例如,可以在參數綁定之前對參數進行日志記錄,或者對參數值進行額外的處理。

@Intercepts({
    @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)
})
public class ExampleParameterHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        // 在參數綁定前的自定義邏輯
        System.out.println("Before parameter binding");

        // 繼續(xù)執(zhí)行原邏輯
        Object result = invocation.proceed();

        // 在參數綁定后的自定義邏輯
        System.out.println("After parameter binding");

        return result;
    }
}

在這個例子中,intercept 方法會在 setParameters 被調用時執(zhí)行,允許開發(fā)者在參數被綁定到 SQL 語句之前和之后執(zhí)行自定義代碼。

ResultSetHandler:負責處理 JDBC 返回的 ResultSet 結果集,攔截 ResultSetHandler 可以在結果集處理前后添加自定義邏輯。

ResultSetHandler 的工作原理

當 MyBatis 執(zhí)行查詢操作后,會得到一個 ResultSet,ResultSetHandler 的職責就是遍歷這個 ResultSet,并將其行轉換為 Java 對象。MyBatis 中默認的 ResultSetHandler 實現是 DefaultResultSetHandler。

源碼解析

下面是 DefaultResultSetHandler 處理結果集的一個簡化版示例,幫助理解其工作機制:

public class DefaultResultSetHandler implements ResultSetHandler {

    private final MappedStatement mappedStatement;
    private final RowBounds rowBounds;

    public DefaultResultSetHandler(MappedStatement mappedStatement, RowBounds rowBounds) {
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
    }

    @Override
    public <E> List<E> handleResultSets(Statement stmt) throws SQLException {
        ResultSet rs = stmt.getResultSet();
        List<E> resultList = new ArrayList<>();
        while (rs.next()) {
            E resultObject = getRowValue(rs);
            resultList.add(resultObject);
        }
        return resultList;
    }

    private <E> E getRowValue(ResultSet rs) throws SQLException {
        // 實際的結果對象映射邏輯
        // 這里通常會涉及到 ResultMap 的處理
        return ...;
    }
}

在這個簡化的例子中,handleResultSets 方法遍歷 ResultSet,對每一行調用 getRowValue 方法將其轉換為一個 Java 對象,最終返回一個對象列表。

攔截器的應用

通過攔截 ResultSetHandlerhandleResultSets 方法,開發(fā)者可以在結果集被處理成 Java 對象前后插入自定義邏輯。這可以用于額外的結果處理,比如對查詢結果的后處理或審計日志記錄等。

@Intercepts({
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = Statement.class)
})
public class ExampleResultSetHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 在結果集處理前的邏輯
        System.out.println("Before handling result sets");

        // 執(zhí)行原有邏輯
        Object result = invocation.proceed();

        // 在結果集處理后的邏輯
        System.out.println("After handling result sets");

        return result;
    }
}

在這個示例中,intercept 方法在 handleResultSets 被調用時執(zhí)行,允許在結果集轉換為 Java 對象的前后執(zhí)行自定義代碼。

StatementHandler:負責對 JDBC Statement 的操作,通過攔截 StatementHandler,可以在 SQL 語句被執(zhí)行前后添加自定義邏輯。

StatementHandler 的工作原理

StatementHandler 主要有三個實現類:SimpleStatementHandler、PreparedStatementHandlerCallableStatementHandler,分別對應于 JDBC 的 Statement、PreparedStatementCallableStatement。這些類處理 SQL 的不同執(zhí)行方式,其中 PreparedStatementHandler 是最常用的,因為它支持參數化的 SQL 語句,有助于提高性能和安全性。

源碼解析

以下是 StatementHandler 接口的一個簡化版實現(PreparedStatementHandler),演示了 MyBatis 如何準備和執(zhí)行 SQL 語句:

public class PreparedStatementHandler implements StatementHandler {

    private final MappedStatement mappedStatement;
    private final Object parameterObject;
    private final BoundSql boundSql;
    private final ParameterHandler parameterHandler;

    public PreparedStatementHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        this.mappedStatement = mappedStatement;
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
        this.parameterHandler = mappedStatement.getConfiguration().newParameterHandler(mappedStatement, parameterObject, boundSql);
    }

    @Override
    public PreparedStatement prepare(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        PreparedStatement pstmt = connection.prepareStatement(sql);
        parameterHandler.setParameters(pstmt);
        return pstmt;
    }

    @Override
    public int update(PreparedStatement pstmt) throws SQLException {
        pstmt.execute();
        return pstmt.getUpdateCount();
    }

    @Override
    public <E> List<E> query(PreparedStatement pstmt, ResultHandler resultHandler) throws SQLException {
        pstmt.execute();
        return resultSetHandler.handleResultSets(pstmt);
    }
}

在這個簡化的實現中,prepare 方法負責創(chuàng)建 PreparedStatement 并通過 ParameterHandler 設置參數。updatequery 方法則用于執(zhí)行 SQL 語句并處理執(zhí)行結果。

攔截器的應用

通過攔截 StatementHandler 的方法,可以在 SQL 語句執(zhí)行的關鍵節(jié)點加入自定義邏輯。例如,可以攔截 prepare 方法,在 SQL 語句被準備之前或之后添加邏輯:

@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class ExampleStatementHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 在 SQL 語句準備前的邏輯
        System.out.println("Before preparing the SQL statement");

        // 執(zhí)行原有邏輯
        Object result = invocation.proceed();

        // 在 SQL 語句準備后的邏輯
        System.out.println("After preparing the SQL statement");

        return result;
    }
}

在這個示例中,intercept 方法會在 prepare 方法執(zhí)行時被調用,允許開發(fā)者在 SQL 語句被準備和執(zhí)行前后插入自定義代碼。

實際案例

讓我們在一個 Spring Boot 項目中結合 MyBatis 的 Executor、ParameterHandlerResultSetHandler、和 StatementHandler 構建一個實際案例。我們將創(chuàng)建一個簡單的用戶管理系統(tǒng),其中包含用戶的添加、查詢功能,并通過攔截器在關鍵節(jié)點添加日志記錄、性能監(jiān)控等自定義邏輯。

步驟 1: 定義實體和映射文件

首先,定義一個 User 實體:

public class User {
    private Integer id;
    private String name;
    private String email;

    // Getters and setters...
}

然后,創(chuàng)建一個 MyBatis 映射文件 UserMapper.xml

<mapper namespace="com.example.mybatisdemo.mapper.UserMapper">
    <insert id="insertUser" parameterType="User">
        INSERT INTO users (name, email) VALUES (#{name}, #{email})
    </insert>
    
    <select id="getUserById" parameterType="int" resultType="User">
        SELECT * FROM users WHERE id = #{id}
    </select>
</mapper>

步驟 2: 定義 Mapper 接口

@Mapper
public interface UserMapper {
    void insertUser(User user);
    User getUserById(int id);
}

步驟 3: 定義攔截器

  • Executor 攔截器 - 記錄執(zhí)行時間:
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class ExecutorInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = invocation.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("Execution time: " + (endTime - startTime) + "ms");
        return result;
    }
}
  • ParameterHandler 攔截器 - 記錄參數信息:
@Intercepts({
    @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
public class ParameterHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        System.out.println("Parameters: " + parameterHandler.getParameterObject());
        return invocation.proceed();
    }
}
  • ResultSetHandler 攔截器 - 在結果集后打印日志:
@Intercepts({
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class ResultSetHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        System.out.println("Result: " + result);
        return result;
    }
}
  • StatementHandler 攔截器 - 修改 SQL 語句:
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class StatementHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        System.out.println("Original SQL: " + sql);
        
        // 此處可根據需要修改 sql
        // String modifiedSql = sql.replace(...);

        return invocation.proceed();
    }
}

步驟 4: 注冊攔截器

在 Spring Boot 配置類中注冊攔截器:

@Configuration
public class MyBatisConfig {
    
    @Bean
    public ExecutorInterceptor executorInterceptor() {
        return new ExecutorInterceptor();
    }

    @Bean
    public ParameterHandlerInterceptor parameterHandlerInterceptor() {
        return new ParameterHandlerInterceptor();
    }

    @Bean
    public ResultSetHandlerInterceptor resultSetHandlerInterceptor() {
        return new ResultSetHandlerInterceptor();
    }

    @Bean
    public StatementHandlerInterceptor statementHandlerInterceptor() {
        return new StatementHandlerInterceptor();
    }
}

步驟 5: 使用 Mapper 進行操作

在你的服務層或控制器中,使用 UserMapper 來執(zhí)行

數據庫操作:

@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;

    public void createUser(User user) {
        userMapper.insertUser(user);
    }

    public User getUser(int id) {
        return userMapper.getUserById(id);
    }
}

當你執(zhí)行 createUser 或 getUser 方法時,你的攔截器將會被觸發(fā),你可以看到控制臺上打印的相關信息,以及 MyBatis 如何處理 SQL 操作以及如何通過攔截器介入這些過程。

以上就是詳解如何使用Mybatis的攔截器的詳細內容,更多關于Mybatis攔截器使用的資料請關注腳本之家其它相關文章!

相關文章

  • 詳解java NIO之Channel(通道)

    詳解java NIO之Channel(通道)

    這篇文章主要介紹了詳解java NIO之Channel(通道)的相關資料,文中講解非常詳細,示例代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • Java用文件流下載網絡文件示例代碼

    Java用文件流下載網絡文件示例代碼

    這篇文章主要介紹了Java用文件流的方式下載網絡文件,大家參考使用吧
    2013-11-11
  • 基于spring mvc請求controller訪問方式

    基于spring mvc請求controller訪問方式

    這篇文章主要介紹了spring mvc請求controller訪問方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 解決SpringBoot2多線程無法注入的問題

    解決SpringBoot2多線程無法注入的問題

    這篇文章主要介紹了解決SpringBoot2多線程無法注入的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 快速學會Dubbo的配置環(huán)境及相關配置

    快速學會Dubbo的配置環(huán)境及相關配置

    本文主要講解Dubbo的環(huán)境與配置,文中運用大量代碼和圖片講解的非常詳細,需要學習或用到相關知識的小伙伴可以參考這篇文章
    2021-09-09
  • JavaWeb實現文件上傳與下載的方法

    JavaWeb實現文件上傳與下載的方法

    這篇文章主要介紹了JavaWeb實現文件上傳與下載的方法的相關資料,需要的朋友可以參考下
    2016-01-01
  • spring cloud 配置中心native配置方式

    spring cloud 配置中心native配置方式

    這篇文章主要介紹了spring cloud 配置中心native配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java接口冪等性設計原理解析

    Java接口冪等性設計原理解析

    這篇文章主要介紹了Java接口冪等性設計原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05
  • 詳解java設計模式中的門面模式

    詳解java設計模式中的門面模式

    門面模式又叫外觀模式(Facade?Pattern),主要用于隱藏系統(tǒng)的復雜性,并向客戶端提供了一個客戶端可以訪問系統(tǒng)的接口,本文通過實例代碼給大家介紹下java門面模式的相關知識,感興趣的朋友一起看看吧
    2022-02-02
  • Java中的Comparable和Comparator接口

    Java中的Comparable和Comparator接口

    這篇文章主要介紹了Java中的Comparable和Comparator接口,文章圍繞主題展開詳細的內容戒殺,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09

最新評論