切換語言為:簡體

使用 Mybatis 時,呼叫 DAO介面時是怎麼呼叫到 SQL 的?

  • 爱糖宝
  • 2024-07-01
  • 2063
  • 0
  • 0

Mybatis 是一個流行的 Java 持久層框架,它提供了一種半自動的 SQL 對映方式,允許開發者在 Java 程式碼中以一種更加直觀和靈活的方式來運算元據庫。當你使用 Mybatis 呼叫 DAO 介面時,背後的工作流程大致如下:

  1. 介面定義:首先,你需要定義一個 DAO 介面,這個介面中會包含一些方法,這些方法對應於你想要執行的資料庫操作。

  2. Mapper XML:對於 DAO 介面中的每一個方法,你需要在 Mybatis 的對映檔案(通常是一個 XML 檔案)中定義一個 <mapper> 標籤,裡面包含一個 <select><insert><update><delete> 標籤,對應於你想要執行的 SQL 語句。

  3. 配置檔案:在 Mybatis 的配置檔案(通常是 mybatis-config.xml)中,你需要指定你的 Mapper XML 檔案的位置,這樣 Mybatis 才能找到並載入它們。

  4. SqlSessionFactory:Mybatis 使用 SqlSessionFactory 來建立 SqlSession 物件。SqlSessionFactory 是透過配置檔案和對映檔案構建的,它包含了所有必要的資訊來執行 SQL 語句。

  5. SqlSession:SqlSession 是 Mybatis 中執行 SQL 語句的主要物件。它提供了執行 SQL 語句的方法,例如 selectOne、selectList、insert、update 和 delete 等。

  6. 呼叫 DAO 介面:當你呼叫 DAO 介面中的方法時,實際上是 Mybatis 的動態代理機制在起作用。Mybatis 會為 DAO 介面建立一個代理物件,當呼叫介面中的方法時,代理物件會攔截這些呼叫,然後根據方法名找到對應的 SQL 對映語句,並執行。

  7. 執行 SQL:Mybatis 透過代理物件,使用 SqlSession 來執行對應的 SQL 語句。執行完成後,SqlSession 會返回結果給呼叫者。

  8. 關閉 SqlSession:執行完畢後,應該關閉 SqlSession 以釋放資料庫連線資源。

這個過程涉及到了 Mybatis 的核心元件和工作流程,確保了 SQL 語句的執行和結果的返回。使用 Mybatis 的好處之一就是它允許開發者以一種宣告式的方式來編寫 SQL,同時還能保持程式碼的清晰和易於維護。

Mybatis 的內部實現細節非常豐富,下面我將透過一些關鍵類的原始碼片段來具體展示 Mybatis 的工作原理。

  1. 配置解析(XMLConfigBuilder.java)

Mybatis 使用 XMLConfigBuilder 來解析 mybatis-config.xml 檔案:

public Configuration parse() {
    parseConfiguration(parser.eval(Configuration.class));
    return configuration;
}

這裏,parseConfiguration 方法會填充 Configuration 物件的屬性,比如資料庫連線資訊、型別別名、型別處理器等。 2. 建立 SqlSessionFactory(SqlSessionFactoryBuilder.java)

使用 XMLConfigBuilder 解析配置後,SqlSessionFactoryBuilder 會建立 SqlSessionFactory:

public SqlSessionFactory build(InputStream inputStream) {
    try {
        XMLConfigBuilder xmlParser = new XMLConfigBuilder(inputStream, environment, reporter);
        return build(xmlParser.parse());
    } catch (Exception e) {
        throw new BuilderException("Error building SqlSession.", e);
    }
}

  1. SqlSession 管理(DefaultSqlSessionFactory.java)

DefaultSqlSessionFactory 提供了建立 SqlSession 的方法:

public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getEnvironment().getDataSource(), null, false);
}

  1. Executor 執行器(BaseExecutor.java)

BaseExecutor 是 Executor 介面的抽象實現,提供了事務和快取管理的框架:

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
    ErrorContext.instance().resource(ms.getResource()).activity("querying").object(ms.getId());
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
    }
    List<E> list;
    try {
        queryStack++;
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        if (list != null && resultHandler != null) {
            resultHandler.handleRows(list);
        }
        if (list == null) {
            list = doQuery(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    } finally {
        queryStack--;
    }
    return list;
}

  1. 動態代理(MapperProxy.java)

MapperProxy 使用 JDK 動態代理來攔截 Mapper 介面方法的呼叫:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
        try {
            return method.invoke(this, args);
        } catch (Throwable t) {
            throw new RuntimeException("Error when attempting to invoke method '" +
                                      method.getName() + "' on Mapper Proxy.", t);
        }
    } else {
        return mapperMethod.execute(sqlSession, args);
    }
}

  1. 對映器 XML 解析(XMLMapperBuilder.java)

XMLMapperBuilder 負責解析 Mapper 的 XML 檔案:

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
        configurationElement(parser.evalNode("mapper"));
        configuration.addLoadedResource(resource);
        bindMapperForNamespace();
    }
}

  1. 快取機制(PerpetualCache.java)

PerpetualCache 是 Mybatis 快取的基本實現:

public V get(Object key, CacheProvider provider) {
    V value = (V) cache.get(key);
    if (value == null) {
        value = provider.apply(key);
        cache.put(key, value);
    }
    return value;
}

  1. TypeHandler 和 ParameterHandler

TypeHandler 介面定義了 Java 型別和 JDBC 型別之間的轉換邏輯:

public interface TypeHandler<T> {
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    T getResult(ResultSet rs, int columnIndex) throws SQLException;
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

ParameterHandler 負責將方法引數對映到 SQL 語句的引數上:

public void setParameters(PreparedStatement ps) throws SQLException {
    for (ParameterMapping parameter : boundSql.getParameterMappings()) {
        if (parameter.getMode() != ParameterMode.OUT) {
            Object value = parameter.getObject(value);
            TypeHandler typeHandler = parameter.getTypeHandler();
            typeHandler.setParameter(ps, parameter.getI(), value, parameter.getJdbcType());
        }
    }
}

  1. 結果對映(DefaultResultSetHandler.java)

DefaultResultSetHandler 負責將 ResultSet 中的資料對映到 Java 物件:

public <E> List<E> handleRowValues(ResultSet rs, List<E> resultList, RowBounds rowBounds) throws SQLException {
    if (hasResultSetData(rs)) {
        int offset = rowBounds == null || rowBounds.getOffset() == RowBounds.NO_ROW_OFFSET ? 0 : rowBounds.getOffset();
        int limit = rowBounds == null || rowBounds.getLimit() == RowBounds.NO_ROW_LIMIT ? Integer.MAX_VALUE : rowBounds.getLimit();
        int rowNumber = 0;
        while (rs.next()) {
            if (offset > 0) {
                offset--;
                continue;
            }
            if (limit > 0) {
                limit--;
                if (limit == 0) {
                    break;
                }
            }
            Object rowValue = getRowValue(rs, null);
            resultList.add((E) rowValue);
            rowNumber++;
        }
    }
    return resultList;
}

這些程式碼片段展示了 Mybatis 核心元件的工作原理。然而,由於 Mybatis 的複雜性,這裏只提供了部分關鍵程式碼的簡要概述。要完全理解 Mybatis 的內部實現,需要深入研究每個類和介面的實現細節,以及它們之間的互動。關注威哥愛程式設計,一起向全棧出發。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.