博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌
Java知识图谱点击链接:体系化学习Java(Java面试专题)
💕💕 感兴趣的同学可以收藏关注下 ,不然下次找不到哟💕💕
文章目录
- 1、Mybatis 的四大组件
- 2、SqlSessionFactoryBuilder 源码分析
- 2.1、XMLConfigBuilder
- 2.2、Configuration
- 3、SqlSessionFactory 源码分析
- 4、SqlSession 源码分析
- 5、Mapper 源码分析
- 5.1、Mapper 接口
- 5.2、MapperProxyFactory 类
- 5.3、MapperProxy 类
- 5.4、MapperMethod 类
1、Mybatis 的四大组件
Mybatis 的四大组件包括:
-
SqlSessionFactoryBuilder :SqlSessionFactoryBuilder 是 Mybatis 的核心组件之一。SqlSessionFactoryBuilder 负责创建 SqlSessionFactory 对象,它是 SqlSessionFactory 的构建器。SqlSessionFactoryBuilder 通过解析 Mybatis 的配置文件创建 SqlSessionFactory 对象,从而实现对 SqlSession 的创建和管理。在 Mybatis 中,SqlSessionFactoryBuilder 通常是通过静态方法调用进行使用,如 SqlSessionFactoryBuilder.build()。
-
SqlSessionFactory:SqlSessionFactory 是 Mybatis 的工厂类,用于创建 SqlSession 对象。SqlSession 是 Mybatis 中用于执行 SQL 语句的对象。
-
SqlSession:SqlSession 是 Mybatis 中用于执行 SQL 语句的对象。SqlSession 提供了一系列的方法,用于执行 SQL 语句、获取 Mapper 接口、提交事务、关闭连接等操作。
-
Mapper:Mapper 是 Mybatis 中用于描述 SQL 语句与 Java 方法之间映射关系的接口。Mapper 接口中定义了 SQL 语句及其参数类型、返回值类型等信息。
源码大家可以去 GitHub 下载 https://github.com/pydlove/mybatis-3-mybatis-3.5.11
2、SqlSessionFactoryBuilder 源码分析
以下是 SqlSessionFactoryBuilder 的源码分析举例代码:
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
try {
// 创建 XMLConfigBuilder 对象,用于解析 Mybatis 的配置文件
XMLConfigBuilder parser = new XMLConfigBuilder(reader);
// 创建 Configuration 对象,用于管理 Mybatis 的所有配置信息
Configuration config = parser.parse();
// 创建 SqlSessionFactory 对象,并返回该对象
return build(config);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
以上代码中,SqlSessionFactoryBuilder 类的 build() 方法接收一个 Reader 对象作为参数,用于读取 Mybatis 的配置文件。在方法内部,首先创建一个 XMLConfigBuilder 对象,用于解析 Mybatis 的配置文件。然后,调用 XMLConfigBuilder 对象的 parse() 方法,对 Mybatis 的配置文件进行解析。接着,创建一个 Configuration 对象,用于管理 Mybatis 的所有配置信息。然后,调用 XMLConfigBuilder 对象的 parseConfiguration() 方法,将解析出来的配置信息设置到 Configuration 对象中。最后,调用 Configuration 对象的 buildSessionFactory() 方法,创建 SqlSessionFactory 对象,并返回该对象。
可以看出,SqlSessionFactoryBuilder 类的 build() 方法非常简洁明了,通过调用其他组件的方法,实现了对 SqlSessionFactory 对象的创建和管理。
2.1、XMLConfigBuilder
XMLConfigBuilder parser = new XMLConfigBuilder(reader);
a)、什么是 XMLConfigBuilder ?
XMLConfigBuilder 是 MyBatis 中用于解析 XML 配置文件的类。它继承自 BaseBuilder 类,负责将 XML 配置文件中的内容解析成 Configuration 对象,并完成 MyBatis 的初始化工作。
b)、XMLConfigBuilder 的主要方法 parse
XMLConfigBuilder 中的主要方法是 parse() 方法,它接受一个 InputStream 对象作为参数,用于读取 XML 文件中的内容。在解析 XML 文件时,XMLConfigBuilder 会创建一个 XPathParser 对象,用于对 XML 文件进行解析。同时,XMLConfigBuilder 也会创建一个 Configuration 对象,用于存储解析结果。
在解析 XML 文件时,XMLConfigBuilder 会根据 XML 文件中的标签和属性,对 Configuration 对象中的各个属性进行设置。例如,当解析到 dataSource 标签时,XMLConfigBuilder 会根据标签中的属性设置数据库连接信息;当解析到 mappers 标签时,XMLConfigBuilder 会根据标签中的属性扫描指定的包,并自动注册 Mapper 接口。
XMLConfigBuilder 是 MyBatis 中非常重要的一个类,它负责将 XML 配置文件解析成 Configuration 对象,并完成 MyBatis 的初始化工作。
2.2、Configuration
Configuration 是 MyBatis 框架中的一个重要组件,它是 MyBatis 的核心配置类,用于存储 MyBatis 的配置信息,包括数据库连接信息、映射器信息、类型处理器信息等。
Configuration 类的主要作用是:
- 解析 MyBatis 的配置文件,将配置信息加载到内存中。
- 存储 MyBatis 的配置信息,包括数据库连接信息、映射器信息、类型处理器信息等。
- 提供获取和设置配置信息的方法,如获取 MapperRegistry 对象、获取 TypeHandlerRegistry 对象等。
- 提供创建 SqlSession 对象、Executor 对象、MappedStatement 对象等的方法。
Configuration 类的主要成员变量包括:
- Properties:存储 MyBatis 的全局配置信息。
- TypeAliasRegistry:存储别名信息。
- TypeHandlerRegistry:存储类型处理器信息。
- MapperRegistry:存储映射器信息。
- MappedStatement:存储 SQL 语句的映射信息。
- SqlSourceBuilder:用于解析 SQL 语句的 SQL 解析器。
- LanguageDriverRegistry:存储语言驱动器信息。
Configuration 类的主要方法包括:
- addMapper(Class type):向 MapperRegistry 中添加映射器。
- getMapper(Class type, SqlSession sqlSession):通过 MapperRegistry 获取映射器。
- getMappedStatement(String id):通过 MappedStatement 获取 SQL 语句的映射信息。
- addMappedStatement(MappedStatement ms):向 MappedStatement 中添加 SQL 语句的映射信息。
- newExecutor(Transaction transaction, ExecutorType executorType):创建 Executor 对象。
- newSqlSession(ExecutorType executorType, boolean autoCommit):创建 SqlSession 对象。
Configuration 是 MyBatis 框架中的一个重要组件,它负责存储和管理 MyBatis 的配置信息,提供了创建 SqlSession 对象、Executor 对象、MappedStatement 对象等的方法,是 MyBatis 框架的核心之一。
如下面这个简单的 MyBatis 配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>
3、SqlSessionFactory 源码分析
SqlSessionFactory 是 MyBatis 中用于创建 SqlSession 对象的工厂类。SqlSession 是 MyBatis 中用于执行 SQL 语句的核心类,它提供了许多执行 SQL 语句的方法,例如 selectOne()、selectList()、insert()、update()、delete() 等。
SqlSessionFactory 的主要作用是创建 SqlSession 对象,并为其配置必要的参数。在创建 SqlSession 对象时,SqlSessionFactory 会为其设置数据库连接信息、事务管理器、执行器类型等参数,以便 SqlSession 能够正确地执行 SQL 语句。
SqlSessionFactory 的创建通常是在应用程序启动时完成的。在创建 SqlSessionFactory 时,我们需要提供一个 Configuration 对象,该对象包含了 MyBatis 的配置信息。SqlSessionFactory 会根据 Configuration 对象中的配置信息,创建一个 SqlSession 对象,并将其返回给调用者。
SqlSessionFactory 的创建通常是比较耗时的,因此在应用程序中应该尽量避免频繁地创建 SqlSessionFactory 对象。通常情况下,我们会将 SqlSessionFactory 对象创建成单例,并在整个应用程序中共享使用。
SqlSessionFactory 有一个实现类 DefaultSqlSessionFactory,如下:
/*
* Copyright 2009-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.session.defaults;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
/**
* @author Clinton Begin
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
@Override
public SqlSession openSession(TransactionIsolationLevel level) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
return openSessionFromDataSource(execType, level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
return openSessionFromDataSource(execType, null, autoCommit);
}
@Override
public SqlSession openSession(Connection connection) {
return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}
@Override
public SqlSession openSession(ExecutorType execType, Connection connection) {
return openSessionFromConnection(execType, connection);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
private void closeTransaction(Transaction tx) {
if (tx != null) {
try {
tx.close();
} catch (SQLException ignore) {
// Intentionally ignore. Prefer previous error.
}
}
}
}
DefaultSqlSessionFactory 是 MyBatis 中 SqlSessionFactory 接口的默认实现类。它实现了 SqlSessionFactory 接口中的方法,并负责创建 SqlSession 对象。
在创建 DefaultSqlSessionFactory 对象时,我们需要提供一个 Configuration 对象作为参数。DefaultSqlSessionFactory 会使用这个 Configuration 对象来创建 SqlSession 对象,并为其设置必要的参数,例如数据库连接信息、事务管理器、执行器类型等。
DefaultSqlSessionFactory 中最重要的方法是 openSession(),它用于创建一个新的 SqlSession 对象。在创建 SqlSession 对象时,DefaultSqlSessionFactory 会为其设置必要的参数,并将其返回给调用者。SqlSession 对象创建完成后,我们就可以使用它来执行 SQL 语句了。openSession 内部的具体调用的方法 openSessionFromDataSource、openSessionFromConnection 这两个私有方法,可以读一下,主要是用来获取 SqlSession 的。
需要注意的是,DefaultSqlSessionFactory 对象的创建通常是比较耗时的,因此在应用程序中应该尽量避免频繁地创建 DefaultSqlSessionFactory 对象。通常情况下,我们会将 DefaultSqlSessionFactory 对象创建成单例,并在整个应用程序中共享使用。
4、SqlSession 源码分析
SqlSession 是 MyBatis 中用于执行 SQL 语句的核心类,它提供了许多执行 SQL 语句的方法,例如 selectOne()、selectList()、insert()、update()、delete() 等。下面我们来分析一下 SqlSession 的源码实现。
1. SqlSession 接口
SqlSession 接口定义了 MyBatis 中用于执行 SQL 语句的方法,包括 selectOne()、selectList()、insert()、update()、delete() 等。它的源码如下:
/*
* Copyright 2009-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.session;
import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;
/**
* The primary Java interface for working with MyBatis.
* Through this interface you can execute commands, get mappers and manage transactions.
*
* @author Clinton Begin
*/
public interface SqlSession extends Closeable {
/**
* Retrieve a single row mapped from the statement key.
* @param <T> the returned object type
* @param statement
* the statement
* @return Mapped object
*/
<T> T selectOne(String statement);
/**
* Retrieve a single row mapped from the statement key and parameter.
* @param <T> the returned object type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return Mapped object
*/
<T> T selectOne(String statement, Object parameter);
/**
* Retrieve a list of mapped objects from the statement key.
* @param <E> the returned list element type
* @param statement Unique identifier matching the statement to use.
* @return List of mapped object
*/
<E> List<E> selectList(String statement);
/**
* Retrieve a list of mapped objects from the statement key and parameter.
* @param <E> the returned list element type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return List of mapped object
*/
<E> List<E> selectList(String statement, Object parameter);
/**
* Retrieve a list of mapped objects from the statement key and parameter,
* within the specified row bounds.
* @param <E> the returned list element type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @param rowBounds Bounds to limit object retrieval
* @return List of mapped object
*/
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
/**
* The selectMap is a special case in that it is designed to convert a list
* of results into a Map based on one of the properties in the resulting
* objects.
* Eg. Return a of Map[Integer,Author] for selectMap("selectAuthors","id")
* @param <K> the returned Map keys type
* @param <V> the returned Map values type
* @param statement Unique identifier matching the statement to use.
* @param mapKey The property to use as key for each value in the list.
* @return Map containing key pair data.
*/
<K, V> Map<K, V> selectMap(String statement, String mapKey);
/**
* The selectMap is a special case in that it is designed to convert a list
* of results into a Map based on one of the properties in the resulting
* objects.
* @param <K> the returned Map keys type
* @param <V> the returned Map values type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @param mapKey The property to use as key for each value in the list.
* @return Map containing key pair data.
*/
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
/**
* The selectMap is a special case in that it is designed to convert a list
* of results into a Map based on one of the properties in the resulting
* objects.
* @param <K> the returned Map keys type
* @param <V> the returned Map values type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @param mapKey The property to use as key for each value in the list.
* @param rowBounds Bounds to limit object retrieval
* @return Map containing key pair data.
*/
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
/**
* A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
* @param <T> the returned cursor element type.
* @param statement Unique identifier matching the statement to use.
* @return Cursor of mapped objects
*/
<T> Cursor<T> selectCursor(String statement);
/**
* A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
* @param <T> the returned cursor element type.
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return Cursor of mapped objects
*/
<T> Cursor<T> selectCursor(String statement, Object parameter);
/**
* A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
* @param <T> the returned cursor element type.
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @param rowBounds Bounds to limit object retrieval
* @return Cursor of mapped objects
*/
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
/**
* Retrieve a single row mapped from the statement key and parameter
* using a {@code ResultHandler}.
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @param handler ResultHandler that will handle each retrieved row
*/
void select(String statement, Object parameter, ResultHandler handler);
/**
* Retrieve a single row mapped from the statement
* using a {@code ResultHandler}.
* @param statement Unique identifier matching the statement to use.
* @param handler ResultHandler that will handle each retrieved row
*/
void select(String statement, ResultHandler handler);
/**
* Retrieve a single row mapped from the statement key and parameter using a {@code ResultHandler} and
* {@code RowBounds}.
*
* @param statement
* Unique identifier matching the statement to use.
* @param parameter
* the parameter
* @param rowBounds
* RowBound instance to limit the query results
* @param handler
* ResultHandler that will handle each retrieved row
*/
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
/**
* Execute an insert statement.
* @param statement Unique identifier matching the statement to execute.
* @return int The number of rows affected by the insert.
*/
int insert(String statement);
/**
* Execute an insert statement with the given parameter object. Any generated
* autoincrement values or selectKey entries will modify the given parameter
* object properties. Only the number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @param parameter A parameter object to pass to the statement.
* @return int The number of rows affected by the insert.
*/
int insert(String statement, Object parameter);
/**
* Execute an update statement. The number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @return int The number of rows affected by the update.
*/
int update(String statement);
/**
* Execute an update statement. The number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @param parameter A parameter object to pass to the statement.
* @return int The number of rows affected by the update.
*/
int update(String statement, Object parameter);
/**
* Execute a delete statement. The number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @return int The number of rows affected by the delete.
*/
int delete(String statement);
/**
* Execute a delete statement. The number of rows affected will be returned.
* @param statement Unique identifier matching the statement to execute.
* @param parameter A parameter object to pass to the statement.
* @return int The number of rows affected by the delete.
*/
int delete(String statement, Object parameter);
/**
* Flushes batch statements and commits database connection.
* Note that database connection will not be committed if no updates/deletes/inserts were called.
* To force the commit call {@link SqlSession#commit(boolean)}
*/
void commit();
/**
* Flushes batch statements and commits database connection.
* @param force forces connection commit
*/
void commit(boolean force);
/**
* Discards pending batch statements and rolls database connection back.
* Note that database connection will not be rolled back if no updates/deletes/inserts were called.
* To force the rollback call {@link SqlSession#rollback(boolean)}
*/
void rollback();
/**
* Discards pending batch statements and rolls database connection back.
* Note that database connection will not be rolled back if no updates/deletes/inserts were called.
* @param force forces connection rollback
*/
void rollback(boolean force);
/**
* Flushes batch statements.
* @return BatchResult list of updated records
* @since 3.0.6
*/
List<BatchResult> flushStatements();
/**
* Closes the session.
*/
@Override
void close();
/**
* Clears local session cache.
*/
void clearCache();
/**
* Retrieves current configuration.
* @return Configuration
*/
Configuration getConfiguration();
/**
* Retrieves a mapper.
* @param <T> the mapper type
* @param type Mapper interface class
* @return a mapper bound to this SqlSession
*/
<T> T getMapper(Class<T> type);
/**
* Retrieves inner database connection.
* @return Connection
*/
Connection getConnection();
}
SqlSession 接口中定义了许多执行 SQL 语句的方法,包括 selectOne()、selectList()、insert()、update()、delete() 等。这些方法的实现会使用 MyBatis 中的 Executor 对象来执行 SQL 语句。
2. DefaultSqlSession 类
DefaultSqlSession 是 SqlSession 接口的默认实现类。它实现了 SqlSession 接口中的所有方法,并负责创建 Executor 对象和事务对象。下面我们来分析一下 DefaultSqlSession 的源码实现。
DefaultSqlSession 类有三个构造方法,分别是:
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
DefaultSqlSession 类中的大部分方法都是用来执行 SQL 语句的。这些方法会使用 Executor 对象来执行 SQL 语句。下面以 selectOne() 方法为例,来看一下它的源码实现:
public <T> T selectOne(String statement, Object parameter) {
return executor.query(statement, wrapCollection(parameter), RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
}
selectOne() 方法接受两个参数,分别是 SQL 语句的 ID 和参数对象。它会将参数对象转换为一个集合对象,并将其传递给 Executor 对象的 query() 方法来执行 SQL 语句。query() 方法接受四个参数,分别是 SQL 语句的 ID、参数对象、分页信息和结果处理器。在这里,我们传递了 RowBounds.DEFAULT 和 Executor.NO_RESULT_HANDLER 作为分页信息和结果处理器,表示不需要分页和结果处理器。
DefaultSqlSession 类中还包含了一些用于事务管理的方法,例如 commit()、rollback() 等。这些方法会使用 Transaction 对象来管理事务。下面以 commit() 方法为例,来看一下它的源码实现:
public void commit() {
commit(false);
}
public void commit(boolean force) {
executor.commit(isCommitOrRollbackRequired(force));
clearCache();
}
private boolean isCommitOrRollbackRequired(boolean force) {
return (!autoCommit && dirty) || force;
}
commit() 方法有两个重载版本,分别是带参数和不带参数的。带参数的版本表示是否强制提交事务。在 commit() 方法中,我们首先调用了 isCommitOrRollbackRequired() 方法来判断是否需要提交事务。isCommitOrRollbackRequired() 方法会根据自动提交标志和 dirty 标志来判断是否需要提交事务。如果需要提交事务,就调用 Executor 对象的 commit() 方法来提交事务,并清空缓存。
5、Mapper 源码分析
Mapper 是 MyBatis 中用于定义 SQL 映射关系的接口,它通过注解或 XML 文件来定义 SQL 语句和参数映射。下面我们来分析一下 Mapper 的源码实现。
5.1、Mapper 接口
Mapper 接口是 MyBatis 中用于定义 SQL 映射关系的接口,它定义了 SQL 语句和参数映射的方法。例如:
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(int id);
}
在这个例子中,我们使用 @Select 注解来定义了一个查询语句,并将参数 id 映射到 SQL 语句中的 #{id} 占位符。Mapper 接口中还可以使用其他注解,例如 @Insert、@Update、@Delete 等,用于定义插入、更新、删除等操作。
5.2、MapperProxyFactory 类
MapperProxyFactory 类是 MyBatis 中用于创建 Mapper 接口代理对象的工厂类。它会根据 Mapper 接口的定义,动态创建一个代理对象,并将 SQL 语句和参数映射到代理方法中。下面我们来分析一下 MapperProxyFactory 的源码实现。
MapperProxyFactory 类有一个构造方法,它接受一个 Class 类型的参数,表示要创建代理对象的 Mapper 接口类。在构造方法中,它会将这个参数保存到类的成员变量中。
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
MapperProxyFactory 类中最重要的方法是 newInstance() 方法,它用于创建 Mapper 接口的代理对象。下面是 newInstance() 方法的源码实现:
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
在 newInstance() 方法中,我们首先创建了一个 MapperProxy 对象,它接受三个参数,分别是 SqlSession 对象、Mapper 接口类和方法缓存。然后,我们调用了另一个重载版本在newInstance() 方法,它接受一个 MapperProxy 对象作为参数,用于创建代理对象。在 newInstance() 方法中,我们使用 Java 动态代理机制,创建了一个代理对象,并将 Mapper 接口类和 MapperProxy 对象传递给代理对象的构造方法。这样,当代理对象调用 Mapper 接口方法时,就会执行 MapperProxy 对象中的逻辑,将 SQL 语句和参数映射到实际的 SQL 语句中,并通过 SqlSession 对象执行 SQL 语句。
5.3、MapperProxy 类
MapperProxy 类是 Mapper 接口的代理类,它会将 Mapper 接口中定义的方法映射到实际的 SQL 语句中,并通过 SqlSession 对象执行 SQL 语句。下面我们来分析一下 MapperProxy 的源码实现。
MapperProxy 类有一个构造方法,它接受三个参数,分别是 SqlSession 对象、Mapper 接口类和方法缓存。在构造方法中,它会将这些参数保存到类的成员变量中,并创建一个 MethodResolver 对象来解析 Mapper 接口中定义的方法。
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
this.methodResolver = new MethodResolver(mapperInterface);
}
MapperProxy 类中最重要的方法是 invoke() 方法,它用于调用 Mapper 接口中定义的方法。下面是 invoke() 方法的源码实现:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
在 invoke() 方法中,我们首先判断要调用的方法是否是 Object 类中的方法,如果是,则直接调用该方法。否则,我们判断要调用的方法是否是默认方法(即带有 default 关键字的方法),如果是,则调用 invokeDefaultMethod() 方法来执行默认方法。最后,我们从方法缓存中获取 MapperMethod 对象,并调用它的 execute() 方法来执行 SQL 语句。
5.4、MapperMethod 类
MapperMethod 类是 MyBatis 中用于封装 Mapper 接口方法的类,它会将 Mapper 接口方法中定义的 SQL 语句和参数映射信息封装到一个对象中,方便后续执行 SQL 语句。下面我们来分析一下 MapperMethod 的源码实现。
MapperMethod 类有两个构造方法,它们分别用于从注解和 XML 文件中解析 SQL 语句和参数映射信息。在构造方法中,它们会将 SQL 语句和参数映射信息保存到类的成员变量中。
public MapperMethod(Class<?> mapperInterface, Method method, SqlSource sqlSource, SqlCommandType sqlCommandType) {
this.mapperInterface = mapperInterface;
this.method = method;
this.sqlSource = sqlSource;
this.sqlCommandType = sqlCommandType;
this.returnType = method.getReturnType();
this.parameterTypes = method.getParameterTypes();
this.hasRowBounds = hasRowBounds();
this.hasResultHandler = hasResultHandler();
this.hasMapResultHandler = hasMapResultHandler();
this.resultSetType = getResultSetType(method);
this.flushCache = isFlushCacheRequired(method);
this.useCache = isUseCacheEnabled(method);
this.keyGenerator = getKeyGenerator(method);
this.keyProperty = getKeyProperty(method);
this.keyColumn = getKeyColumn(method);
this.databaseId = getDatabaseId(method);
this.lang = getLang(method);
this.resultOrdered = method.getAnnotation(ResultOrder.class) != null;
this.resultSets = getResultSets(method);
}
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.mapperInterface = mapperInterface;
this.method = method;
this.sqlCommandType = getSqlCommandType(method);
this.sqlSource = new XMLLanguageDriver().createSqlSource(config, method.getAnnotation(MappedStatement.class).sql(), method.getReturnType());
this.returnType = method.getReturnType();
this.parameterTypes = method.getParameterTypes();
this.hasRowBounds = hasRowBounds();
this.hasResultHandler = hasResultHandler();
this.hasMapResultHandler = hasMapResultHandler();
this.resultSetType = getResultSetType(method);
this.flushCache = isFlushCacheRequired(method);
this.useCache = isUseCacheEnabled(method);
this.keyGenerator = getKeyGenerator(method);
this.keyProperty = getKeyProperty(method);
this.keyColumn = getKeyColumn(method);
this.databaseId = getDatabaseId(method);
this.lang = getLang(method);
this.resultOrdered = method.getAnnotation(ResultOrder.class) != null;
this.resultSets = getResultSets(method);
}
在构造方法中,我们使用反射机制获取 Mapper 接口方法的返回类型、参数类型、SQL 语句类型、是否需要刷新缓存、是否使用缓存、是否需要生成主键等信息,并根据这些信息创建一个 MapperMethod 对象。
MapperMethod 类中最重要的方法是 execute() 方法,它用于执行 SQL 语句,并将结果映射到 Java 对象中。下面是 execute() 方法的源码实现:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (sqlCommandType) {
case INSERT: {
Object param = getParam(args);
result = executeInsert(sqlSession, param);
break;
}
case UPDATE: {
Object param = getParam(args);
result = executeUpdate(sqlSession, param);
break;
}
case DELETE: {
Object param = getParam(args);
result = executeDelete(sqlSession, param);
break;
}
case SELECT:
default: {
Object param = getParam(args);
result = executeSelect(sqlSession, param);
break;
}
}
if (result == null && returnType.isPrimitive() && !void.class.equals(returnType)) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + returnType + ").");
}
return result;
}
在 execute() 方法中,我们首先根据 SQL 语句类型,调用不同的方法来执行 SQL 语句。例如,对于 INSERT、UPDATE 和 DELETE 操作,我们会调用 executeInsert()、executeUpdate() 和 executeDelete() 方法来执行 SQL 语句,而对于 SELECT 操作,则会调用 executeSelect() 方法来执行 SQL 语句。然后,我们根据方法的返回类型,将执行结果映射到 Java 对象中,并返回该对象。
Mapper 是 MyBatis 中的一个重要组件,它充分利用了 Java 接口的特性,可以让我们通过编写简单的接口方法来实现复杂的 SQL 操作。在 MyBatis 中,Mapper 接口方法会被解析成一个 MapperMethod 对象,该对象封装了 SQL 语句和参数映射信息,并提供了 execute() 方法来执行 SQL 语句。通过对 Mapper 的深入理解,我们可以更好地使用 MyBatis 来操作数据库。
💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊