马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
MyBatis 作为一款经典的恒久层框架,其计划精妙之处在于通过几个核心组件的协作,将 SQL 操作与 Java 对象优雅地结合起来。本文将深入剖析 MyBatis 的核心组件,包罗它们的作用、相互关系以及底层实现原理。
1.MyBatis 核心组件概览
MyBatis 的核心组件紧张包罗以下几个部分:
- SqlSessionFactoryBuilder:负责从 XML 设置文件或 Java 代码中构建 SqlSessionFactory。
- SqlSessionFactory:工厂模式的实现,负责创建 SqlSession 实例。
- SqlSession:提供了执行 SQL 下令的方法,是应用与 MyBatis 之间的紧张编程接口。
- Executor:SqlSession 内部使用 Executor 来执行 SQL 语句。
- Mapper 接口与映射文件:界说 SQL 语句与 Java 方法的映射关系。
- TypeHandler:负责 Java 类型与 JDBC 类型之间的转换。
- ParameterHandler:处理 SQL 参数。
- ResultSetHandler:处理 SQL 查询效果集。
这些组件相互协作,形成了 MyBatis 的核心架构。下面我们通过一个团体架构图来直观地了解它们之间的关系:
- +-------------------+ +-------------------+ +-------------------+
- | | | | | |
- | SqlSessionFactory |<--->| SqlSession |<--->| MapperProxy |
- | | | | | |
- +-------------------+ +-------------------+ +-------------------+
- ^ | ^ ^
- | | | |
- | v | |
- +-------------------+ +-------------------+ +-------------------+
- | | | | | |
- | Configuration | | Executor | | MapperRegistry |
- | | | | | |
- +-------------------+ +-------------------+ +-------------------+
- ^ | ^
- | | |
- | v |
- +-------------------+ +-------------------+
- | | | |
- | MappedStatement | | TypeHandler |
- | | | |
- +-------------------+ +-------------------+
复制代码 2.核心组件详解
1. SqlSessionFactoryBuilder
作用:SqlSessionFactoryBuilder 是 MyBatis 的入口点,负责剖析设置文件并构建 SqlSessionFactory 实例。
源码关键代码:
- public class SqlSessionFactoryBuilder {
- public SqlSessionFactory build(InputStream inputStream) {
- return build(inputStream, null, null);
- }
-
- public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
- try {
- // 解析 XML 配置文件
- XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
- // 构建 Configuration 对象
- return build(parser.parse());
- } catch (Exception e) {
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally {
- ErrorContext.instance().reset();
- try {
- inputStream.close();
- } catch (IOException e) {
- // Intentionally ignore. Prefer previous error.
- }
- }
- }
-
- public SqlSessionFactory build(Configuration config) {
- // 创建 DefaultSqlSessionFactory 实例
- return new DefaultSqlSessionFactory(config);
- }
- }
复制代码 使用示例:
- InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
复制代码 2. SqlSessionFactory
作用:SqlSessionFactory 是一个工厂接口,负责创建 SqlSession 实例。它是线程安全的,可以被多个线程共享。
核心方法:
- openSession():创建一个新的 SqlSession 实例。
- openSession(boolean autoCommit):创建一个带有主动提交功能的 SqlSession。
- openSession(ExecutorType execType):创建一个指定执行器类型的 SqlSession。
源码关键代码:
- public interface SqlSessionFactory {
- SqlSession openSession();
- SqlSession openSession(boolean autoCommit);
- SqlSession openSession(Connection connection);
- SqlSession openSession(TransactionIsolationLevel level);
- SqlSession openSession(ExecutorType execType);
- // 其他重载方法...
- }
- 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);
- }
-
- 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);
- // 创建 DefaultSqlSession 实例
- 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();
- }
- }
- }
复制代码 3. SqlSession
作用:SqlSession 是 MyBatis 的核心接口,提供了执行 SQL 下令的方法。它是线程不安全的,应该在方法内部使用,用完后实时关闭。
核心方法:
- selectOne(String statement, Object parameter):查询单个效果。
- selectList(String statement, Object parameter):查询多个效果。
- insert(String statement, Object parameter):插入数据。
- update(String statement, Object parameter):更新数据。
- delete(String statement, Object parameter):删除数据。
- commit():提交事件。
- rollback():回滚事件。
- getMapper(Class<T> type):获取 Mapper 接口的代理对象。
源码关键代码:
- public interface SqlSession extends Closeable {
- <T> T selectOne(String statement);
- <T> T selectOne(String statement, Object parameter);
- <E> List<E> selectList(String statement);
- <E> List<E> selectList(String statement, Object parameter);
- int insert(String statement);
- int insert(String statement, Object parameter);
- int update(String statement);
- int update(String statement, Object parameter);
- int delete(String statement);
- int delete(String statement, Object parameter);
- void commit();
- void commit(boolean force);
- void rollback();
- void rollback(boolean force);
- <T> T getMapper(Class<T> type);
- Configuration getConfiguration();
- Connection getConnection();
- }
- public class DefaultSqlSession implements SqlSession {
- private final Configuration configuration;
- private final Executor executor;
- private final boolean autoCommit;
- private boolean dirty;
-
- public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
- this.configuration = configuration;
- this.executor = executor;
- this.autoCommit = autoCommit;
- this.dirty = false;
- }
-
- @Override
- public <T> T selectOne(String statement, Object parameter) {
- // Popular vote was to return null on 0 results and throw exception on too many.
- List<T> list = this.selectList(statement, parameter);
- if (list.size() == 1) {
- return list.get(0);
- } else if (list.size() > 1) {
- throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
- } else {
- return null;
- }
- }
-
- @Override
- public int insert(String statement, Object parameter) {
- return update(statement, parameter);
- }
-
- @Override
- public int update(String statement, Object parameter) {
- try {
- dirty = true;
- // 通过执行器执行更新操作
- MappedStatement ms = configuration.getMappedStatement(statement);
- return executor.update(ms, wrapCollection(parameter));
- } catch (Exception e) {
- throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
- } finally {
- ErrorContext.instance().reset();
- }
- }
-
- @Override
- public <T> T getMapper(Class<T> type) {
- // 通过 Configuration 获取 Mapper 代理
- return configuration.getMapper(type, this);
- }
- }
复制代码 4. Executor
作用:Executor 是 MyBatis 的执行器,负责 SQL 语句的执行和缓存的维护。
紧张实现类:
- SimpleExecutor:简朴执行器,每次执行都会创建新的预处理语句。
- ReuseExecutor:可重用执行器,会重用预处理语句。
- BatchExecutor:批处理执行器,用于批量操作。
- CachingExecutor:缓存执行器,用于二级缓存的管理。
源码关键代码:
- public interface Executor {
- ResultHandler NO_RESULT_HANDLER = null;
-
- int update(MappedStatement ms, Object parameter) throws SQLException;
-
- <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
-
- <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
-
- List<BatchResult> flushStatements() throws SQLException;
-
- void commit(boolean required) throws SQLException;
-
- void rollback(boolean required) throws SQLException;
-
- CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
-
- boolean isCached(MappedStatement ms, CacheKey key);
-
- void clearLocalCache();
-
- void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
-
- Transaction getTransaction();
-
- void close(boolean forceRollback);
-
- boolean isClosed();
-
- void setExecutorWrapper(Executor executor);
- }
- public abstract class BaseExecutor implements Executor {
- // 实现 Executor 接口的方法
- // 包含事务管理、缓存管理等通用逻辑
- }
- public class SimpleExecutor extends BaseExecutor {
- @Override
- public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
- Statement stmt = null;
- try {
- Configuration configuration = ms.getConfiguration();
- StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
- // 准备语句
- stmt = prepareStatement(handler, ms.getStatementLog());
- // 执行更新
- return handler.update(stmt);
- } finally {
- closeStatement(stmt);
- }
- }
-
- @Override
- public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
- Statement stmt = null;
- try {
- Configuration configuration = ms.getConfiguration();
- StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
- // 准备语句
- stmt = prepareStatement(handler, ms.getStatementLog());
- // 执行查询
- return handler.query(stmt, resultHandler);
- } finally {
- closeStatement(stmt);
- }
- }
- }
复制代码 5. Mapper 接口与映射文件
作用:Mapper 接口界说了数据库操作的方法,映射文件(或注解)界说了这些方法对应的 SQL 语句。
映射文件示例:
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.example.mapper.UserMapper">
-
- <select id="getUserById" parameterType="int" resultType="com.example.entity.User">
- SELECT * FROM users WHERE id = #{id}
- </select>
-
- <insert id="insertUser" parameterType="com.example.entity.User">
- INSERT INTO users (username, email, age)
- VALUES (#{username}, #{email}, #{age})
- </insert>
-
- <!-- 其他 SQL 映射... -->
- </mapper>
复制代码 Mapper 接口示例:
- package com.example.mapper;
- import com.example.entity.User;
- import java.util.List;
- public interface UserMapper {
- User getUserById(int id);
- void insertUser(User user);
- void updateUser(User user);
- void deleteUser(int id);
- List<User> getAllUsers();
- }
复制代码 MapperProxy 实现:
MyBatis 使用动态代理实现 Mapper 接口:
- public class MapperProxy<T> implements InvocationHandler, Serializable {
- private static final long serialVersionUID = -642454039855972983L;
- private final SqlSession sqlSession;
- private final Class<T> mapperInterface;
- private final Map<Method, MapperMethodInvoker> methodCache;
-
- public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
- this.sqlSession = sqlSession;
- this.mapperInterface = mapperInterface;
- this.methodCache = methodCache;
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- if (Object.class.equals(method.getDeclaringClass())) {
- return method.invoke(this, args);
- } else {
- return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
- }
- } catch (Throwable t) {
- throw ExceptionUtil.unwrapThrowable(t);
- }
- }
-
- private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
- try {
- return methodCache.computeIfAbsent(method, m -> {
- if (m.isDefault()) {
- try {
- if (privateLookupInMethod == null) {
- return new DefaultMethodInvoker(getMethodHandleJava8(method));
- } else {
- return new DefaultMethodInvoker(getMethodHandleJava9(method));
- }
- } catch (IllegalAccessException | InstantiationException | InvocationTargetException
- | NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- } else {
- // 创建 MapperMethod 实例
- return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
- }
- });
- } catch (RuntimeException re) {
- Throwable cause = re.getCause();
- throw cause == null ? re : cause;
- }
- }
- }
复制代码 3.核心组件协作流程
下面通过一个查询操作的时序图,展示 MyBatis 核心组件的协作流程:
- Client SqlSession Executor MappedStatement JDBC
- | | | | |
- | getUserById(1) | | | |
- |--------------------->| | | |
- | | getMappedStatement("...") | |
- | |---------------------------->| |
- | | | | |
- | | query(ms, 1) | | |
- | |-------------->| | |
- | | | getBoundSql() | |
- | | |------------->| |
- | | | | |
- | | | prepareStatement() |
- | | |----------------------------->|
- | | | | Connection |
- | | | |<-------------|
- | | | | |
- | | | executeQuery() |
- | | |----------------------------->|
- | | | | ResultSet |
- | | |<-------------| |
- | | | | |
- | | | handleResultSets() |
- | | |<-------------| |
- | |<---------------| | |
- |<---------------------| | | |
复制代码 4.总结
通过深入剖析 MyBatis 的核心组件,我们可以看到其计划的精妙之处:
- 工厂模式:SqlSessionFactoryBuilder 构建 SqlSessionFactory,SqlSessionFactory 创建 SqlSession。
- 代理模式:MapperProxy 实现 Mapper 接口的动态代理,将方法调用转换为 SQL 执行。
- 计谋模式:Executor 提供多种执行计谋(SimpleExecutor、ReuseExecutor、BatchExecutor)。
- 模板方法模式:BaseExecutor 实现了通用的执行逻辑,具体实现由子类完成。
这种计划使得 MyBatis 既保持了机动性,又提供了简朴易用的 API。开辟者可以通过设置文件或注解界说 SQL 映射,然后通过 Mapper 接口进行数据库操作,无需编写繁琐的 JDBC 代码。
在实际开辟中,理解 MyBatis 的核心组件和工作原理,有助于我们更好地使用 MyBatis 进行开辟,也能够在遇到问题时更快地定位和办理问题。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|