手写Mybatis:第20章-Mybatis 框架源码10种设计模式分析

news2024/11/28 17:44:06

文章目录

  • 一、类型:创建型模式
    • 1.1 工厂模式
    • 1.2 单例模式
    • 1.3 建造者模式
  • 二、类型:结构型模式
    • 2.1 适配器模式
    • 2.2 代理模式
    • 2.3 组合模式
    • 2.4 装饰器模式
  • 三、类型:行为型模式
    • 3.1 模板模式
    • 3.2 策略模式
    • 3.3 迭代器模式

在这里插入图片描述

一、类型:创建型模式

1.1 工厂模式

SqlSessionFactory.java

package com.lino.mybatis.session;

/**
 * @description: 工厂模式接口,构建SqlSession的工厂
 */
public interface SqlSessionFactory {

    /**
     * 打开一个session
     *
     * @return SqlSession
     */
    SqlSession openSession();
}

DefaultSqlSessionFactory.java

package com.lino.mybatis.session.defaults;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.TransactionFactory;
import java.sql.SQLException;

/**
 * @description: 默认的SqlSessionFactory实现类
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        Transaction tx = null;
        try {
            final Environment environment = configuration.getEnvironment();
            TransactionFactory transactionFactory = environment.getTransactionFactory();
            tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
            // 创建执行器
            final Executor executor = configuration.newExecutor(tx);
            // 创建 DefaultSqlSession
            return new DefaultSqlSession(configuration, executor);
        } catch (Exception e) {
            try {
                assert tx != null;
                tx.close();
            } catch (SQLException ignore) {

            }
            throw new RuntimeException("Error opening session. Cause: " + e);
        }
    }
}

在这里插入图片描述

  • 工厂模式:简单工厂,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例对象的类型。
  • 场景介绍SqlSessionFactory 是获取会话的工厂,每次我们使用 Mybatis 操作数据库的时候,都会开启一个新的会话。
    • 在会话工厂的实现中负责获取数据源环境配置信息、构建事务工厂、创建操作 SQL 的执行器,并最终返回会话实现类。
  • 同类设计SqlSessionFactoryObjectFactoryMapperproxyFactoryDataSourceFactory

1.2 单例模式

Configuration.java

package com.lino.mybatis.session;

import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.cache.Cache;
import com.lino.mybatis.cache.decorators.FifoCache;
import com.lino.mybatis.cache.impl.PerpetualCache;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.CachingExecutor;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.parameter.ParameterHandler;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.plugin.Interceptor;
import com.lino.mybatis.plugin.InterceptorChain;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.scripting.LanguageDriverRegistry;
import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @description: 配置项
 */
public class Configuration {

    /**
     * 环境
     */
    protected Environment environment;
    /**
     * 是否使用自动生成键值对
     */
    protected boolean useGeneratedKeys = false;
    /**
     * 默认启用缓存,cacheEnabled = true/false
     */
    protected boolean cacheEnabled = true;
    /**
     * 缓存机制,默认不配置的情况是 SESSION
     */
    protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
    /**
     * 映射注册机
     */
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);
    /**
     * 映射的语句,存在Map里
     */
    protected final Map<String, MappedStatement> mappedStatements = new HashMap<>(16);
    /**
     * 缓存,存在Map里
     */
    protected final Map<String, Cache> caches = new HashMap<>(16);
    /**
     * 结果映射,存在Map里
     */
    protected final Map<String, ResultMap> resultMaps = new HashMap<>(16);
    /**
     * 键值生成器,存在Map里
     */
    protected final Map<String, KeyGenerator> keyGenerators = new HashMap<>(16);
    /**
     * 插件拦截器链
     */
    protected final InterceptorChain interceptorChain = new InterceptorChain();
    /**
     * 类型别名注册机
     */
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    /**
     * 脚本语言注册器
     */
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    /**
     * 类型处理器注册机
     */
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
    /**
     * 对象工厂
     */
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    /**
     * 对象包装工厂
     */
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    /**
     * 准备资源列表
     */
    protected final Set<String> loadedResources = new HashSet<>();
    /**
     * 数据库ID
     */
    protected String databaseId;

	//...

}

在这里插入图片描述

  • 单例模式:是一种创建型模式,让你能够保证一个类只有一个实例,并提供一个访问该实例的全局节点。
  • 场景介绍Configuration 就像狗皮膏药一样大单例,贯穿整个会话的生命周期,所有的配置对象。
    • 映射、缓存、入参、出参、拦截器、注册机、对象工厂等,都在 Configuration 配置项中初始化。
    • 并随着 SqlSessionFactorybuilder 构建阶段完成实例化操作。
  • 同类场景ErrorContextLogFactoryConfigutation

1.3 建造者模式

ResultMap.java

package com.lino.mybatis.mapping;

import com.lino.mybatis.session.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * @description: 结果映射
 */
public class ResultMap {

    private String id;
    private Class<?> type;
    private List<ResultMapping> resultMappings;
    private Set<String> mappedColumns;

    public ResultMap() {
    }

    public static class Builder {

        private ResultMap resultMap = new ResultMap();

        public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
            resultMap.id = id;
            resultMap.type = type;
            resultMap.resultMappings = resultMappings;
        }

        public ResultMap build() {
            resultMap.mappedColumns = new HashSet<>();
            // 添加 mappedColums 字段
            for (ResultMapping resultMapping : resultMap.resultMappings) {
                final String column = resultMapping.getColumn();
                if (column != null) {
                    resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
                }
            }
            return resultMap;
        }
    }

    //...
}

在这里插入图片描述

  • 建造者模式:使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 场景介绍:关于建造者模式在 Mybatis 框架里的使用,到处都是 XxxBuilder,所有关于 XML 文件的解析到各类对象的封装,都使用建造者以及建造者助手来完成对象的封装。
    • 它的核心目的就是不希望把过多的关于对象的属性设置,写到其他业务流程中,而是用创建者的方式提供最佳的边界隔离。
  • 同类场景SqlSesiionFactoryBuilderXMLConfigBuilderXMLMapperBuilderXMLStatementBuilderCacheBuilder

二、类型:结构型模式

2.1 适配器模式

Log.java

package com.lino.mybatis.logging;

/**
 * @description: 日志接口
 */
public interface Log {

    boolean isDebugEnabled();

    boolean isTraceEnabled();

    void error(String s, Throwable e);

    void error(String s);

    void debug(String s);

    void trace(String s);

    void warn(String s);
}

Slf4jImpl.java

package com.lino.mybatis.logging.slf4j;

import com.lino.mybatis.logging.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @description: slf4j log 设计模式类,不做实现
 */
public class Slf4jImpl implements Log {

    public Slf4jImpl(String clazz) {
        Logger logger = LoggerFactory.getLogger(clazz);
    }

    @Override
    public boolean isDebugEnabled() {
        return false;
    }

    @Override
    public boolean isTraceEnabled() {
        return false;
    }

    @Override
    public void error(String s, Throwable e) {

    }

    @Override
    public void error(String s) {

    }

    @Override
    public void debug(String s) {

    }

    @Override
    public void trace(String s) {

    }

    @Override
    public void warn(String s) {

    }
}

在这里插入图片描述

  • 适配器模式:是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。
  • 场景介绍:正是因为有太多的日志框架,包括:Log4j、Log4j2、Slf4J 等等,而这些日志框架的使用接口又都各有差异,为了统一这些日志工具的接口,Mybatis 定义了一套统一的日志接口,为所有的其他日志工具接口做相应的适配操作。
  • 同类场景:主要集中在对日志的适配上,Log 和 对应的实现类,以及在 LogFactory 工厂方法中进行使用。

2.2 代理模式

MapperProxy.java

package com.lino.mybatis.binding;

import com.lino.mybatis.session.SqlSession;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @description: 映射器代理类
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;

    private SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            final MapperMethod mapperMethod = cacheMapperMethod(method);
            return mapperMethod.execute(sqlSession, args);
        }
    }

    /**
     * 去缓存中找MapperMethod
     */
    private MapperMethod cacheMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }
}

在这里插入图片描述

  • 代理模式:是一种结构型模式,让你能够提供对象的替代品或其占位符。代理控制着对原对象的访问,并允许在将请求提交给对象前进行一些处理。
  • 场景介绍:没有代理模式,就不会有各类的框架存在。就像 Mybatis 中的 MybatisProxy 映射器代理实现类,它所实现的功能就是帮助我们完成 DAO 接口的具体实现类的方法操作,你的任何一个配置的 DAO 接口所调用的 CRUD 方法,都会被 MapperProxy 接管,调用到方法执行器等一系列操作,并返回最终的数据的数据库执行结果。
  • 同类场景DriverProxyPluginInvokerMapperProxy

2.3 组合模式

SqlNode.java

package com.lino.mybatis.scripting.xmltags;

/**
 * @description: SQL 节点
 */
public interface SqlNode {

    /**
     * 应用动态上下文
     *
     * @param context 动态上下文
     * @return boolean
     */
    boolean apply(DynamicContext context);
}

IfSqlNode.java

package com.lino.mybatis.scripting.xmltags;

/**
 * @description: IF SQL 节点
 */
public class IfSqlNode implements SqlNode {

    private ExpressionEvaluator evaluator;
    private String test;
    private SqlNode contents;

    public IfSqlNode(SqlNode contents, String test) {
        this.test = test;
        this.contents = contents;
        this.evaluator = new ExpressionEvaluator();
    }

    @Override
    public boolean apply(DynamicContext context) {
        // 如果满足条件,则apply,并返回true
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
            contents.apply(context);
            return true;
        }
        return false;
    }
}

XMLScriptBuilder.java

package com.lino.mybatis.scripting.xmltags;

import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.defaults.RawSqlSource;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;
import org.dom4j.Node;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description: XML脚本构建器
 */
public class XMLScriptBuilder extends BaseBuilder {

    private Element element;
    private boolean isDynamic;
    private Class<?> parameterType;
    private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();

    public XMLScriptBuilder(Configuration configuration, Element element, Class<?> parameterType) {
        super(configuration);
        this.element = element;
        this.parameterType = parameterType;
        initNodeHandlerMap();
    }

    private void initNodeHandlerMap() {
        // 9种,实现其中2种 trim/where/set/foreach/if/choose/when/otherwise/bind
        nodeHandlerMap.put("trim", new TrimHandler());
        nodeHandlerMap.put("if", new IfHandler());
    }

    public SqlSource parseScriptNode() {
        List<SqlNode> contents = parseDynamicTags(element);
        MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
        SqlSource sqlSource = null;
        if (isDynamic) {
            sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
        } else {
            sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
        }
        return sqlSource;
    }

    private List<SqlNode> parseDynamicTags(Element element) {
        List<SqlNode> contents = new ArrayList<>();
        List<Node> children = element.content();
        for (Node child : children) {
            if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {
                String data = child.getText();
                TextSqlNode textSqlNode = new TextSqlNode(data);
                if (textSqlNode.isDynamic()) {
                    contents.add(textSqlNode);
                    isDynamic = true;
                } else {
                    contents.add(new StaticTextSqlNode(data));
                }
            } else if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getName();
                NodeHandler handler = nodeHandlerMap.get(nodeName);
                if (handler == null) {
                    throw new RuntimeException("Unknown element <" + nodeName + "> in SQL statement.");
                }
                handler.handleNode(element.element(child.getName()), contents);
                isDynamic = true;
            }
        }
        return contents;
    }

    private interface NodeHandler {
        void handleNode(Element nodeToHandle, List<SqlNode> targetContents);
    }

    private class TrimHandler implements NodeHandler {
        @Override
        public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
            List<SqlNode> contents = parseDynamicTags(nodeToHandle);
            MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
            String prefix = nodeToHandle.attributeValue("prefix");
            String prefixOverrides = nodeToHandle.attributeValue("prefixOverrides");
            String suffix = nodeToHandle.attributeValue("suffix");
            String suffixOverrides = nodeToHandle.attributeValue("suffixOverrides");
            TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
            targetContents.add(trim);
        }
    }

    private class IfHandler implements NodeHandler {
        @Override
        public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
            List<SqlNode> contents = parseDynamicTags(nodeToHandle);
            MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
            String test = nodeToHandle.attributeValue("test");
            IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
            targetContents.add(ifSqlNode);
        }
    }
}

Activity_Mapper.xml

<select id="queryActivityById" parameterType="com.lino.mybatis.test.po.Activity" resultMap="activityMap">
    SELECT activity_id, activity_name, activity_desc, create_time, update_time
    FROM activity
    <trim prefix="where" prefixOverrides="AND | OR" suffixOverrides="and">
        <if test="null != activityId">
            activity_id = #{activityId}
        </if>
    </trim>
</select>

在这里插入图片描述

  • 组合模式:是一种结构型设计模式,你可以使用它将对象组合成树状结构,并且能独立使用对象一样使用它们。
  • 场景介绍:在 Mybatis XML 动态的 SQL 配置中,共提供了9种(trim/where/set/foreach/if/choose/when/otherwise/bind)标签的使用,让使用者可以组合出各类场景的 SQL 语句。而 SqlNode 接口的实现就是每一个组合结构种的规则节点,通过规则节点的组装完成一颗规则树组合模式的使用。
  • 同类场景:主要体现在对各类 SQL 标签的解析上,以实现 SqlNode 接口的各个子类为主。

2.4 装饰器模式

Congiguration.java

package com.lino.mybatis.session;

import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.cache.Cache;
import com.lino.mybatis.cache.decorators.FifoCache;
import com.lino.mybatis.cache.impl.PerpetualCache;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.CachingExecutor;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.parameter.ParameterHandler;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.plugin.Interceptor;
import com.lino.mybatis.plugin.InterceptorChain;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.scripting.LanguageDriverRegistry;
import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @description: 配置项
 */
public class Configuration {

    /**
     * 生产执行器
     *
     * @param transaction 事务
     * @return 执行器
     */
    public Executor newExecutor(Transaction transaction) {
        Executor executor = new SimpleExecutor(this, transaction);
        // 配置开启缓存,创建 CachingExecutor(默认就是有缓存)装饰着模式
        if (cacheEnabled) {
            executor = new CachingExecutor(executor);
        }
        return executor;
    }

}

在这里插入图片描述

  • 装饰器模式:是一种结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
  • 场景介绍Mybatis 的所有 SQL 操作,都是经过 SqlSession 会话调用 SimpleExecutor 简单实现的执行器完成的,而一级缓存的操作也是在简单执行器中处理。那么这里二级缓存因为是基于二级缓存刷新操作的,所以在实现上,通过创建一个缓存执行器,包装简单执行器的处理逻辑,实现二级缓存操作。那么这里用到的就是装饰器模式,也叫俄罗斯套娃模式。
  • 同类场景:主要提前在 Cache 缓存接口的实现和 CachingExecutor 执行器中。

三、类型:行为型模式

3.1 模板模式

BaseExecutor.java

package com.lino.mybatis.executor;

import com.lino.mybatis.cache.CacheKey;
import com.lino.mybatis.cache.impl.PerpetualCache;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ParameterMapping;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.LocalCacheScope;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.type.TypeHandlerRegistry;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 执行器抽象基类
 */
public abstract class BaseExecutor implements Executor {

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        if (closed) {
            throw new RuntimeException("Executor was closed.");
        }
        // 清理局部缓存,查询堆栈为0则清理。queryStack 避免递归调用清理
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }
        List<E> list;
        try {
            queryStack++;
            // 根据cacheKey从localCache中查询数据
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            if (list == null) {
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            queryStack--;
        }
        if (queryStack == 0) {
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                clearLocalCache();
            }
        }
        return list;
    }

}

SimpleExecutor.java

package com.lino.mybatis.executor;

import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import com.lino.mybatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 简单执行器
 */
public class SimpleExecutor extends BaseExecutor {

    public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
    }

    @Override
    protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            // 新建一个 StatementHandler
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
            // 准备语句
            stmt = prepareStatement(handler);
            // StatementHandler.update
            return handler.update(stmt);
        } finally {
            closeStatement(stmt);
        }
    }

    @Override
    protected <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
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
            // 准备语句
            stmt = prepareStatement(handler);
            // 返回结果
            return handler.query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

    private Statement prepareStatement(StatementHandler handler) throws SQLException {
        Statement stmt;
        Connection connection = transaction.getConnection();
        // 准备语句
        stmt = handler.prepare(connection);
        handler.parameterize(stmt);
        return stmt;
    }
}

在这里插入图片描述

  • 模板模式:是一种行为设计模式,它在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。
  • 场景介绍:只要存在一系列可被标准定义的流程,在流程的步骤大部分是通用逻辑,只有一少部分是需要子类实现的,那么通常会采用模板模式来定义出这个标准的流程。就像 MybatisBaseExecutor 就是一个用于定义模板模式的抽象类,在这个类中把查询、修改的操作都定义出了一套标准的流程。
  • 同类场景BaseExecutorSimpleExecutorBaseTypeHandler

3.2 策略模式

TypeHandler.java

package com.lino.mybatis.type;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @description: 类型处理器
 */
public interface TypeHandler<T> {

    /**
     * 设置参数
     *
     * @param ps        预处理语言
     * @param i         次数
     * @param parameter 参数对象
     * @param jdbcType  JDBC类型
     * @throws SQLException SQL异常
     */
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

    /**
     * 获取结果
     *
     * @param rs         结果列表
     * @param columnName 列名
     * @return T 结果
     * @throws SQLException
     */
    T getResult(ResultSet rs, String columnName) throws SQLException;

    /**
     * 获取结果
     *
     * @param rs          结果列表
     * @param columnIndex 列的索引
     * @return T 结果
     * @throws SQLException
     */
    T getResult(ResultSet rs, int columnIndex) throws SQLException;
}

LongTypeHandler.java

package com.lino.mybatis.type;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @description: Long类型处理器
 * @author: lingjian
 * @createDate: 2022/11/11 13:59
 */
public class LongTypeHandler extends BaseTypeHandler<Long> {
    @Override
    protected void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
        ps.setLong(i, parameter);
    }

    @Override
    protected Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getLong(columnName);
    }

    @Override
    protected Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getLong(columnIndex);
    }
}

在这里插入图片描述

  • 策略模式:是一种行为设计模式,它能定义一系列算法,并将每种算法分别放入独立的类中,以便算法的对象能够互相替换。
  • 场景介绍:在 Mybatis 处理 JDBC 执行后返回的结果时,需要按照不同的类型获取对应的值,这样就可以避免大量的 if 判断。所以这里基于 TypeHandler 接口对每个参数类型分别做了自己的策略实现。
  • 同类场景PooledDataSource\UnpooledDataSourceBatchExecutor\ResuseExecutor\SimpleExecutor\CachingExecutorLongTypeHandler\StringTypeHandler\DateTypeHandler

3.3 迭代器模式

PropertyTokenizer.java

package com.lino.mybatis.reflection.property;

import java.util.Iterator;

/**
 * @description: 属性分解标记
 */
public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> {

    // 例子:班级[0].学生.成绩
    /**
     * 属性名称:班级
     */
    private String name;
    /**
     * 属性对象名称:班级[0]
     */
    private String indexedName;

    /**
     * 属性索引:0
     */
    private String index;

    /**
     * 子属性:学生
     */
    private String children;

    public PropertyTokenizer(String fullName) {
        // 班级[0].学生.成绩:找.
        int delim = fullName.indexOf(".");
        if (delim > -1) {
            name = fullName.substring(0, delim);
            children = fullName.substring(delim + 1);
        } else {
            // 找不到.的话,取全部部分
            name = fullName;
            children = null;
        }
        indexedName = name;
        // 把中括号里的数字解析出来
        delim = name.indexOf("[");
        if (delim > -1) {
            index = name.substring(delim + 1, name.length() - 1);
            name = name.substring(0, delim);
        }
    }

    @Override
    public Iterator<PropertyTokenizer> iterator() {
        return this;
    }

    @Override
    public boolean hasNext() {
        return children != null;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
    }

    @Override
    public PropertyTokenizer next() {
        return new PropertyTokenizer(children);
    }

    public String getName() {
        return name;
    }

    public String getIndexedName() {
        return indexedName;
    }

    public String getIndex() {
        return index;
    }

    public String getChildren() {
        return children;
    }
}

在这里插入图片描述

  • 迭代器模式:是一种行为设计模式,让你能在不暴露集合底层表现形式的情况下遍历集合中所有的元素。
  • 场景介绍PropertyTokenizer 是用于 Mybatis 框架 MetaObject 反射工具包下,用于解析对象关系的迭代操作。这个类在 Mybatis 框架中使用的非常频繁,包括解析数据源配置信息并填充到数据源类上,以及参数的解析、对象的设置都会使用到这个类。
  • 同类场景PropertyTokenizer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/975806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kolya and Movie Theatre

Recently, Kolya found out that a new movie theatre is going to be opened in his city soon, which will show a new movie every day for nn days. So, on the day with the number 1≤i≤n, the movie theatre will show the premiere of the ii-th movie. Also, Kolya f…

手写Mybatis:第17章-Plugin插件功能实现

文章目录 一、目标&#xff1a;Plugin插件二、设计&#xff1a;Plugin插件三、实现&#xff1a;Plugin插件3.1 工程结构3.2 Plugin插件代理模式类图3.3 自定义拦截注解3.3.1 方法签名3.3.2 拦截注解 3.4 拦截器接口定义3.4.1 调用信息3.4.2 拦截器接口 3.5 类代理包装操作3.5.1…

【计算机网络】http协议

目录 前言 认识URL URLEncode和URLDecode http协议格式 http方法 GET POST GET与POST的区别 http状态码 http常见header 简易的http服务器 前言 我们在序列化和反序列化这一章中&#xff0c;实现了一个网络版的计算器。这个里面设计到了对协议的分析与处…

【力扣周赛】第 112 场双周赛(统计一个字符串的 k 子序列美丽值最大的数目(贪心+计数+组合数学)

文章目录 竞赛链接Q1&#xff1a;7021. 判断通过操作能否让字符串相等 IQ2&#xff1a;7005. 判断通过操作能否让字符串相等 II&#xff08;贪心&#xff09;Q3&#xff1a;2841. 几乎唯一子数组的最大和竞赛时代码——滑动窗口 Q4&#xff1a;8050. 统计一个字符串的 k 子序列…

电子邮件营销实例有哪些?如何做邮件营销?

可参考的电子邮件营销实例&#xff1f;营销邮件制作技巧有什么&#xff1f; 电子邮件营销是当今数字营销领域中的一个关键策略&#xff0c;旨在通过发送定制化的电子邮件与目标受众建立联系&#xff0c;提高品牌知名度、促进销售和培养客户关系。下面将介绍一些电子邮件营销的…

记录一次WMware网络问题

目录 ​编辑 一、问题描述 二、问题排查 2.1 指令ifconfig 查看ip信息 2.2 nmcli n 查看网卡状态 三、问题解决 3.1 启动 NetworkManager 网络管理器 3.2 ifup ens160 启动网卡 一、问题描述 我在我本地电脑上使用WMware虚拟机部署了k8s&#xff0c;有次正常关机后&am…

初试jsvmp加密

分析目标 目标网站 腾讯的点选验证码(我这边是本地环境&#xff0c;所以没有网址) 目标参数 cap_union_new_verify.collect 目标js文件 tdc.js?app_data 流程分析(分析算法) 我们打开我们要分析的网站&#xff0c;过一下点选验证码&#xff0c;抓一包&#xff0c;可以得到…

防雷工程中防雷接地网的应用方案

防雷接地是指在建筑物或其他设施中设置专门的接地装置&#xff0c;以防止雷电对人员、设备和建筑物造成危害的措施。防雷接地工程是防雷工程的重要组成部分&#xff0c;其主要目的是将雷电电流引入大地&#xff0c;消除雷电过电压&#xff0c;降低雷电危险。防雷接地工程应遵循…

BackgroudWork的详细用法,实例

一、什么是BackgroudWorker? 1、简言 backgroudworkd就是一个异步单线程&#xff0c;专门为入门级人员开发的。还可以显示进度条。操作简单实用,属于老技术。 注意&#xff1a;如果调用两次这个线程&#xff0c;将会出错。 2、backgroudwor…

centos7下docker设置新的下载镜像源并调整存放docker下载镜像的仓库位置

目录 1.设置镜像源 2.调整存放下载镜像的仓库位置 1.设置镜像源 在 /etc/docker下创建一个daemon.json文件。在json中下入 "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/"] 完成配置 加载配置 systemctl daemon-reload 重启docker sy…

医学影像工作站PACS系统源码,医院PACS系统源码

医学影像(PACS)系统主要进行病人信息和影像的获取、处理、存储、调阅、检索、管理&#xff0c;并通过网络向全院提供病人检查影像及诊断报告&#xff1b;各影像科室之间共享不同设备的病人检查影像及诊断报告&#xff1b;在诊断工作站上&#xff0c;调阅HIS中病人的其它信息&am…

企业架构LNMP学习笔记13

上线商城项目&#xff1a; 1&#xff09;上传项目文件到数据库&#xff1a; 入口文件位置的设计是为了让应用部署更安全&#xff0c;public目录为web可访问目录&#xff0c;其他的文件都可以放到非web访问目录下面。 nginx 默认访问index.html。没有index.html&#xff0c;就会…

软路由ip的优势与劣势:了解其适用场景和限制

在网络技术的快速发展中&#xff0c;软路由IP作为一种灵活且功能强大的网络设备&#xff0c;越来越受到人们的关注。然而&#xff0c;正如任何技术一样&#xff0c;软路由IP也有其优势和劣势。本文将深入探讨软路由IP的优势、劣势以及其适用场景和限制&#xff0c;帮助你更好地…

【回眸】牛客网刷刷刷!(八)——中断专题

目录 前言 1、在CortexM内核中&#xff0c;当系统响应一个中断时 2、用与非门和或非门可以实现其他基本门电路。进而实现任何逻辑电路 3、cpu interface提供了功能包含 4、以Cortex-M3内核为例&#xff0c;如果某个中断在得到响应之前&#xff0c;其请求信号以若干的脉冲的…

kafka-- 安装kafka manager及简单使用

一 、安装kafka manager 管控台&#xff1a; # 安装kafka manager 管控台&#xff1a; ## 上传 cd /usr/local/software ## 解压 unzip kafka-manager-2.0.0.2.zip -d /usr/local/ cd /usr/local/kafka-manager-2.0.0.2/conf vim /usr/local/kafka-manager-2.0.0.2/conf/appl…

MySql学习笔记03——DQL(数据查询)基本命令

DQL 导入数据 首先使用use database进入数据库中&#xff0c;然后使用命令 source D:\mysql_learning\mysql_learning\document\bjpowernode.sql注意文件名不能有双引号&#xff0c;命令结尾没有分号。 SQL脚本 .sql文件是SQL脚本文件&#xff0c;它里面的内容都是SQL语句…

个人炒伦敦银方法大公开

个人炒伦敦银的方法与机构投资者炒这个品种的方法是有不同的&#xff0c;但是双方可能会借鉴一些相同的分析工具&#xff0c;比方说有的机构可能也会使用技术分析&#xff0c;当然&#xff0c;个人投资者对技术分析这个词更是不会陌生。今天我们就从个人投资者的角度出发&#…

计算机竞赛 基于深度学习的人脸识别系统

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸识别系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…

8、异常机制---- 8.1 Error和Exception

8、异常机制 8.1 Error和Exception 什么是异常 在实际工作中&#xff0c;遇到的情况不可能是非常完美的。比如&#xff1a;你写的某个模块&#xff0c;用户输入不一定符合你的要求、你的程序要打开某个文件&#xff0c;这个文件可能不存在或者文件格式不对&#xff0c;你要读…

23. 带旋转的数独游戏

题目 Description 数独是一个基于逻辑的组合数字放置拼图&#xff0c;在世界各地都很受欢迎。 在这个问题上&#xff0c;让我们关注 网格的拼图&#xff0c;其中包含 个区域。 目标是用十六进制数字填充整个网格&#xff0c;即 &#xff0c;以便每列&#xff0c;每行和每个区…