MyBatis设计模式之装饰器、模版方法、策略模式

MyBatis设计模式深度解析(二)

一、MyBatis整体架构与设计模式续篇

在上一篇文章中,我们深入讲解了MyBatis中的构建者模式、工厂模式和代理模式。本文将继续探讨MyBatis中另外三种重要的设计模式:装饰器模式、模板方法模式和策略模式。这些设计模式的应用,使得MyBatis在功能扩展、性能优化和架构灵活性方面表现出色。

1.1 MyBatis整体架构回顾

MyBatis采用分层架构设计,从上到下包括:

  • 应用层:用户应用程序
  • 接口层:SqlSession、Mapper接口
  • 核心处理层:SQL解析、执行、结果映射
  • 基础支撑层:缓存、事务、类型处理、日志等
  • 数据层:数据源、连接池、JDBC驱动

在本文中,我们将重点探讨装饰器模式、模板方法模式和策略模式在这些层次中的应用。

1.2 本文涉及的设计模式

  1. 装饰器模式(Decorator Pattern):CachingExecutor、Cache装饰器链
  2. 模板方法模式(Template Method Pattern):BaseExecutor、BaseTypeHandler
  3. 策略模式(Strategy Pattern):RoutingStatementHandler、不同Executor策略

二、装饰器模式(Decorator Pattern)

装饰器模式是一种结构型设计模式,它允许你在不改变对象结构的情况下,动态地为对象添加新的行为。装饰器模式通过将对象包装在装饰器类中来实现功能的增强。

2.1 CachingExecutor

CachingExecutor是MyBatis中最典型的装饰器模式应用。它为Executor添加了二级缓存功能。

2.1.1 类定义
java 复制代码
public class CachingExecutor implements Executor {

    private final Executor delegate;  // 被装饰的Executor
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        // 确保被装饰的Executor类型是SIMPLE、REUSE或BATCH
        if (delegate instanceof CachingExecutor) {
            throw new IllegalArgumentException("Cannot wrap a CachingExecutor");
        }
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 1. 获取绑定SQL
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        // 2. 创建缓存Key
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        // 3. 执行查询(先查缓存,缓存没有则查数据库)
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        // 4. 获取MappedStatement的缓存配置
        Cache cache = ms.getCache();

        if (cache != null) {
            // 5. 刷新缓存(如果需要)
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                // 6. 确保参数不为null时才使用缓存
                ensureNoOutParams(ms, boundSql);
                // 7. 从事务缓存管理器获取缓存
                @SuppressWarnings("unchecked")
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    // 8. 缓存未命中,委托给被装饰的Executor执行查询
                    list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    // 9. 将查询结果放入缓存
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
            }
        }
        // 10. 没有配置缓存,直接委托给被装饰的Executor
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public int update(MappedStatement ms, Object parameterObject) throws SQLException {
        // 更新操作先刷新缓存
        flushCacheIfRequired(ms);
        // 委托给被装饰的Executor执行更新
        return delegate.update(ms, parameterObject);
    }

    private void flushCacheIfRequired(MappedStatement ms) {
        Cache cache = ms.getCache();
        if (cache != null && ms.isFlushCacheRequired()) {
            tcm.clear(cache);
        }
    }
}
2.1.2 装饰器模式的优势
  1. 不修改原始类:通过包装而非继承来扩展功能
  2. 动态组合:可以在运行时动态地添加或删除功能
  3. 职责单一:每个装饰器只负责一个功能
  4. 灵活扩展:可以多层嵌套,实现功能的灵活组合

2.2 Cache装饰器链

MyBatis的缓存系统也大量使用了装饰器模式。Cache接口有多个装饰器实现,形成了一个装饰器链。

2.2.1 Cache接口
java 复制代码
public interface Cache {
    String getId();
    void putObject(Object key, Object value);
    Object getObject(Object key);
    Object removeObject(Object key);
    void clear();
    int getSize();
    ReadWriteLock getReadWriteLock();
}
2.2.2 Cache装饰器实现
java 复制代码
// 1. PerpetualCache - 基础缓存实现
public class PerpetualCache implements Cache {
    private final String id;
    private Map<Object, Object> cache = new HashMap<>();

    public PerpetualCache(String id) {
        this.id = id;
    }

    @Override
    public void putObject(Object key, Object value) {
        cache.put(key, value);
    }

    @Override
    public Object getObject(Object key) {
        return cache.get(key);
    }

    @Override
    public Object removeObject(Object key) {
        return cache.remove(key);
    }

    @Override
    public void clear() {
        cache.clear();
    }
}

// 2. LruCache - LRU淘汰策略装饰器
public class LruCache implements Cache {
    private final Cache delegate;
    private Map<Object, Object> keyMap;
    private Object eldestKey;

    public LruCache(Cache delegate) {
        this.delegate = delegate;
        setSize(1024);
    }

    @Override
    public void putObject(Object key, Object value) {
        delegate.putObject(key, value);
        cycleKeyList(key);
    }

    @Override
    public Object getObject(Object key) {
        keyMap.get(key); // 触发LRU
        return delegate.getObject(key);
    }

    private void cycleKeyList(Object key) {
        keyMap.put(key, key);
        if (eldestKey != null) {
            delegate.removeObject(eldestKey);
            eldestKey = null;
        }
    }
}

// 3. FifoCache - FIFO淘汰策略装饰器
public class FifoCache implements Cache {
    private final Cache delegate;
    private final Deque<Object> keyList;
    private int size;

    public FifoCache(Cache delegate) {
        this.delegate = delegate;
        this.keyList = new LinkedList<>();
        this.size = 1024;
    }

    @Override
    public void putObject(Object key, Object value) {
        cycleKeyList(key);
        delegate.putObject(key, value);
    }

    private void cycleKeyList(Object key) {
        keyList.addLast(key);
        if (keyList.size() > size) {
            Object oldestKey = keyList.removeFirst();
            delegate.removeObject(oldestKey);
        }
    }
}

// 4. SoftCache/WeakCache - 软引用/弱引用装饰器
public class SoftCache implements Cache {
    private final Cache delegate;
    private final Map<Object, Object> hardLinksToAvoidGarbageCollection;

    @Override
    public void putObject(Object key, Object value) {
        delegate.putObject(key, new SoftEntry(value));
    }

    @Override
    public Object getObject(Object key) {
        Object value = delegate.getObject(key);
        if (value instanceof SoftEntry) {
            return ((SoftEntry) value).get();
        }
        return value;
    }
}

// 5. BlockingCache - 阻塞装饰器(防止缓存击穿)
public class BlockingCache implements Cache {
    private final Cache delegate;
    private final ConcurrentHashMap<Object, CountDownLatch> locks;

    @Override
    public void putObject(Object key, Object value) {
        try {
            delegate.putObject(key, value);
        } finally {
            releaseLock(key);
        }
    }

    @Override
    public Object getObject(Object key) {
        acquireLock(key);
        Object value = delegate.getObject(key);
        if (value != null) {
            releaseLock(key);
        }
        return value;
    }

    private void acquireLock(Object key) {
        CountDownLatch latch = new CountDownLatch(1);
        CountDownLatch existing = locks.putIfAbsent(key, latch);
        if (existing != null) {
            try {
                existing.await();
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        }
    }
}

// 6. LoggingCache - 日志装饰器
public class LoggingCache implements Cache {
    private final Cache delegate;
    private int hits = 0;
    private int requests = 0;

    @Override
    public void putObject(Object key, Object value) {
        delegate.putObject(key, value);
    }

    @Override
    public Object getObject(Object key) {
        requests++;
        final Object value = delegate.getObject(key);
        if (value != null) {
            hits++;
        }
        if (log.isDebugEnabled()) {
            log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
        }
        return value;
    }

    private double getHitRatio() {
        return (double) hits / (double) requests;
    }
}

// 7. ScheduledCache - 定时清理装饰器
public class ScheduledCache implements Cache {
    private final Cache delegate;
    private long clearInterval;
    private long lastClear;

    @Override
    public void putObject(Object key, Object value) {
        clearWhenStale();
        delegate.putObject(key, value);
    }

    @Override
    public Object getObject(Object key) {
        return clearWhenStale() ? null : delegate.getObject(key);
    }

    private boolean clearWhenStale() {
        if (System.currentTimeMillis() - lastClear > clearInterval) {
            clear();
            return true;
        }
        return false;
    }
}

// 8. SerializedCache - 序列化装饰器
public class SerializedCache implements Cache {
    private final Cache delegate;

    @Override
    public void putObject(Object key, Object value) {
        if (value != null && !(value instanceof Serializable)) {
            throw new CacheException("Cacheed object is not serializable: " + value);
        }
        delegate.putObject(key, serialize(value));
    }

    @Override
    public Object getObject(Object key) {
        Object object = delegate.getObject(key);
        return object == null ? null : deserialize((byte[]) object);
    }
}

// 9. SynchronizedCache - 同步装饰器
public class SynchronizedCache implements Cache {
    private final Cache delegate;

    @Override
    public synchronized void putObject(Object key, Object value) {
        delegate.putObject(key, value);
    }

    @Override
    public synchronized Object getObject(Object key) {
        return delegate.getObject(key);
    }

    @Override
    public synchronized void clear() {
        delegate.clear();
    }
}

// 10. TransactionalCache - 事务缓存装饰器
public class TransactionalCache implements Cache {
    private final Cache delegate;
    private boolean clearOnCommit;
    private Map<Object, Object> entriesToAddOnCommit;

    @Override
    public void putObject(Object key, Object value) {
        entriesToAddOnCommit.put(key, value);
    }

    @Override
    public Object getObject(Object key) {
        if (clearOnCommit) {
            return null;
        }
        return delegate.getObject(key);
    }

    public void commit() {
        if (clearOnCommit) {
            delegate.clear();
        }
        for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
            delegate.putObject(entry.getKey(), entry.getValue());
        }
        reset();
    }
}
2.2.3 Cache装饰器链的构建
java 复制代码
public class CacheBuilder {
    private String id;
    private Class<? extends Cache> implementation;
    private List<Class<? extends Cache>> decorators;

    public Cache build() {
        setDefaultImplementations();
        Cache cache = newBaseCacheInstance(implementation, id);
        setCacheProperties(cache);

        // 应用装饰器
        for (Class<? extends Cache> decorator : decorators) {
            cache = newCacheDecoratorInstance(decorator, cache);
            setCacheProperties(cache);
        }
        return cache;
    }
}

2.3 装饰器模式的应用场景

在MyBatis中,装饰器模式主要应用于:

  1. Executor增强:CachingExecutor为Executor添加二级缓存功能
  2. Cache功能扩展:通过各种Cache装饰器添加LRU、FIFO、日志等功能
  3. StatementHandler增强:通过RoutingStatementHandler路由到不同的实现

2.4 装饰器模式 vs 继承

装饰器模式相比继承的优势:

对比项 装饰器模式 继承
扩展方式 动态组合 静态继承
灵活性
代码复用 一般
类数量 可能增加 可能爆炸
复杂度 中等 简单

三、模板方法模式(Template Method Pattern)

模板方法模式是一种行为型设计模式,它在父类中定义了一个算法的框架,允许子类在不改变算法结构的情况下重写算法的特定步骤。

3.1 BaseExecutor

BaseExecutor是MyBatis中模板方法模式的典型应用,它定义了SQL执行的基本流程,将具体的执行逻辑留给子类实现。

3.1.1 BaseExecutor核心代码
java 复制代码
public abstract class BaseExecutor implements Executor {

    protected Transaction transaction;
    protected Executor wrapper;

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 1. 获取绑定SQL
        BoundSql boundSql = ms.getBoundSql(parameter);
        // 2. 创建缓存Key
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        // 3. 调用模板方法
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    // 模板方法 - 定义算法骨架
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());

        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }

        List<E> list;
        try {
            queryStack++;
            // 4. 检查一级缓存
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            if (list != null) {
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                // 5. 缓存未命中,调用抽象方法查询数据库
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            queryStack--;
        }

        if (queryStack == 0) {
            deferLoadIfNeeded();
        }
        return list;
    }

    // 从数据库查询 - 也是模板方法
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            // 6. 调用抽象方法,由子类实现具体查询逻辑
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            localCache.removeObject(key);
        }
        localCache.putObject(key, list);
        return list;
    }

    // 抽象方法 - 由子类实现具体的查询逻辑
    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter,
                                           RowBounds rowBounds, ResultHandler resultHandler,
                                           BoundSql boundSql) throws SQLException;

    // 抽象方法 - 由子类实现具体的更新逻辑
    protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

    // 其他抽象方法...
}
3.1.2 BaseExecutor的子类实现
java 复制代码
// 1. SimpleExecutor - 简单执行器
public class SimpleExecutor extends BaseExecutor {

    @Override
    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            // 1. 创建Statement
            stmt = createStatement(ms, parameter, rowBounds);
            // 2. 设置参数
            setStatementParameters(ms, stmt, parameter, rowBounds, boundSql);
            // 3. 执行查询
            return handler.query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

    @Override
    protected 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);
        }
    }
}

// 2. ReuseExecutor - 重用执行器
public class ReuseExecutor extends BaseExecutor {
    private final Map<String, Statement> statementMap = new HashMap<>();

    @Override
    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
        Statement stmt = prepareStatement(handler, ms.getStatementLog(), true);
        return handler.query(stmt, resultHandler);
    }

    private Statement prepareStatement(StatementHandler handler, Log statementLog, boolean isReusable) throws SQLException {
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        Statement stmt;
        if (isReusable) {
            // 尝试重用Statement
            stmt = statementMap.get(sql);
            if (stmt == null) {
                stmt = handler.prepare(statementLog.getConnection(), transaction.getTimeout());
                statementMap.put(sql, stmt);
            }
        } else {
            stmt = handler.prepare(statementLog.getConnection(), transaction.getTimeout());
        }
        handler.parameterize(stmt);
        return stmt;
    }
}

// 3. BatchExecutor - 批量执行器
public class BatchExecutor extends BaseExecutor {
    private final List<Statement> statementList = new ArrayList<>();
    private final List<BatchResult> batchResultList = new ArrayList<>();

    @Override
    protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
        Statement stmt = prepareStatement(handler, ms.getStatementLog());
        handler.parameterize(stmt);
        batchResultList.add(new BatchResult(ms.getStatement(), ms.getId(), parameter));
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
    }

    @Override
    public List<BatchResult> doFlushStatements(boolean isRollback) {
        List<BatchResult> results = new ArrayList<>();
        try {
            for (int i = 0, n = statementList.size(); i < n; i++) {
                Statement stmt = statementList.get(i);
                BatchResult batchResult = batchResultList.get(i);
                try {
                    int[] updateCounts = stmt.executeBatch();
                    batchResult.setUpdateCounts(updateCounts);
                    results.add(batchResult);
                } catch (BatchUpdateException e) {
                    StringBuilder message = new StringBuilder();
                    message.append(batchResult.getStatement()).append(" failed.");
                    if (updateCounts != null && updateCounts.length > 0) {
                        message.append(" Update counts: ");
                        for (int j = 0; j < updateCounts.length; j++) {
                            message.append(updateCounts[j]).append(";");
                        }
                    }
                    throw new BatchExecutorException(message.toString(), e, updateCounts, batchResult.getStatement());
                }
            }
            return results;
        } finally {
            clearLocalCache();
            statementList.clear();
            batchResultList.clear();
        }
    }
}

3.2 BaseTypeHandler

BaseTypeHandler也是模板方法模式的应用,它定义了类型处理的基本流程。

3.2.1 BaseTypeHandler核心代码
java 复制代码
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        // 模板方法:设置非空参数
        setParameter(ps, i, parameter, jdbcType);
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 模板方法:通过列名获取结果
        T result = getNullableResult(rs, columnName);
        return rs.wasNull() ? null : result;
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        // 模板方法:通过列索引获取结果
        T result = getNullableResult(rs, columnIndex);
        return rs.wasNull() ? null : result;
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        // 模板方法:从存储过程获取结果
        T result = getNullableResult(cs, columnIndex);
        return cs.wasNull() ? null : result;
    }

    // 抽象方法:由子类实现具体的参数设置逻辑
    public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

    // 抽象方法:由子类实现具体的结果获取逻辑
    public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

    public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

    public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}
3.2.2 具体的TypeHandler实现
java 复制代码
// StringTypeHandler
public class StringTypeHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

// IntegerTypeHandler
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter);
    }

    @Override
    public Integer getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getInt(columnName);
    }

    @Override
    public Integer getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getInt(columnIndex);
    }

    @Override
    public Integer getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getInt(columnIndex);
    }
}

3.3 模板方法模式的优势

  1. 代码复用:将公共逻辑提取到父类,避免重复代码
  2. 扩展性强:子类可以灵活实现特定步骤
  3. 算法稳定:算法框架固定,保证一致性
  4. 易于维护:修改算法只需修改父类

四、策略模式(Strategy Pattern)

策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户端。

4.1 RoutingStatementHandler

RoutingStatementHandler是MyBatis中策略模式的典型应用,它根据MappedStatement的配置,路由到不同的StatementHandler实现。

4.1.1 RoutingStatementHandler源码
java 复制代码
public class RoutingStatementHandler implements StatementHandler {

    private final StatementHandler delegate;

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 根据Statement类型选择策略
        switch (ms.getStatementType()) {
            case STATEMENT:
                delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case PREPARED:
                delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case CALLABLE:
                delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            default:
                throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    }

    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        return delegate.prepare(connection, transactionTimeout);
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        delegate.parameterize(statement);
    }

    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return delegate.query(statement, resultHandler);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        return delegate.update(statement);
    }

    // 其他方法都委托给delegate...
}
4.1.2 三种StatementHandler策略
java 复制代码
// 1. SimpleStatementHandler - 简单SQL策略
public class SimpleStatementHandler extends BaseStatementHandler {

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        if (mappedStatement.getResultSetType() == ResultSetType.FORWARD_ONLY) {
            return connection.createStatement();
        } else {
            return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
    }

    @Override
    public int update(Statement statement) throws SQLException {
        String sql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        int rows;
        if (keyGenerator instanceof Jdbc3KeyGenerator) {
            statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
            rows = statement.getUpdateCount();
            keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        } else if (keyGenerator instanceof SelectKeyGenerator) {
            statement.execute(sql);
            rows = statement.getUpdateCount();
            keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        } else {
            statement.execute(sql);
            rows = statement.getUpdateCount();
        }
        return rows;
    }
}

// 2. PreparedStatementHandler - 预编译SQL策略
public class PreparedStatementHandler extends BaseStatementHandler {

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getResultSetType() == ResultSetType.FORWARD_ONLY) {
            return connection.prepareStatement(sql);
        } else {
            return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
        return rows;
    }
}

// 3. CallableStatementHandler - 存储过程策略
public class CallableStatementHandler extends BaseStatementHandler {

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getResultSetType() == ResultSetType.FORWARD_ONLY) {
            return connection.prepareCall(sql);
        } else {
            return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        registerOutputParameters((CallableStatement) statement);
        parameterHandler.setParameters((CallableStatement) statement);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement) statement;
        cs.execute();
        int rows = cs.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
        return rows;
    }
}

public class Configuration {

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        Executor executor;
        // 根据ExecutorType选择策略
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            executor = new SimpleExecutor(this, transaction);
        }
        // 如果启用缓存,装饰为CachingExecutor
        if (cacheEnabled) {
            executor = new CachingExecutor(executor);
        }
        return (Executor) interceptorChain.pluginAll(executor);
    }
}
4.2.2 三种Executor策略对比
Executor类型 特点 适用场景 性能
SimpleExecutor 每次执行创建新Statement 一般查询、单条操作 中等
ReuseExecutor 重用Statement 执行相同SQL多次 较好
BatchExecutor 批量执行SQL 批量插入、更新 最好

4.3 策略模式的优势

  1. 算法可替换:可以灵活切换不同的算法实现
  2. 代码解耦:算法的实现与使用分离
  3. 扩展方便:新增策略只需实现新类
  4. 简化测试:可以独立测试每个策略

4.4 策略模式 vs 模板方法模式

对比项 策略模式 模板方法模式
算法结构 可变 固定
实现方式 组合 继承
灵活性 中等
复杂度 较高 较低

五、设计模式的综合应用

在MyBatis中,多种设计模式往往协同工作,共同完成复杂的功能。

5.1 Executor的创建过程

java 复制代码
// 1. 模板方法模式:BaseExecutor定义执行流程
// 2. 策略模式:根据ExecutorType选择SimpleExecutor、ReuseExecutor或BatchExecutor
// 3. 装饰器模式:CachingExecutor为Executor添加缓存功能
// 4. 代理模式:Plugin为Executor添加拦截功能

Executor executor = configuration.newExecutor(transaction, ExecutorType.SIMPLE);

5.2 Cache的构建过程

java 复制代码
// 1. 装饰器模式:多层Cache装饰器嵌套
// 2. 模板方法模式:BaseExecutor定义查询流程(使用缓存)

Cache cache = new CacheBuilder("myCache")
    .implementation(PerpetualCache.class)
    .addDecorator(LruCache.class)
    .addDecorator(FifoCache.class)
    .addDecorator(ScheduledCache.class)
    .build();

5.3 StatementHandler的选择过程

java 复制代码
// 1. 策略模式:RoutingStatementHandler路由到不同的StatementHandler
// 2. 模板方法模式:BaseStatementHandler定义Statement处理流程
// 3. 代理模式:LoggerStatementHandler添加日志功能

StatementHandler handler = configuration.newStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);

六、最佳实践与建议

6.1 装饰器模式应用场景

当满足以下条件时,可以考虑使用装饰器模式:

  1. 需要动态地、透明地给对象添加功能
  2. 不希望通过继承来扩展功能(避免类爆炸)
  3. 需要组合多个功能

应用示例:

java 复制代码
// 为服务添加缓存、日志、监控功能
Service service = new LoggingService(
    new MonitoringService(
        new CachingService(
            new BasicService()
        )
    )
);

6.2 模板方法模式应用场景

当满足以下条件时,可以考虑使用模板方法模式:

  1. 算法的整体结构固定,部分步骤可变
  2. 多个子类有共同的行为逻辑
  3. 需要控制子类的扩展

应用示例:

java 复制代码
public abstract class AbstractDataService {

    // 模板方法:定义数据处理流程
    public final void processData(String data) {
        validate(data);      // 验证
        data = transform(data); // 转换
        save(data);          // 保存
    }

    protected abstract void validate(String data);
    protected abstract String transform(String data);
    protected abstract void save(String data);
}

6.3 策略模式应用场景

当满足以下条件时,可以考虑使用策略模式:

  1. 有多种算法可以相互替换
  2. 算法的使用和实现分离
  3. 需要在运行时选择算法

应用示例:

java 复制代码
// 支付策略
public interface PaymentStrategy {
    void pay(int amount);
}

public class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        // 信用卡支付逻辑
    }
}

public class WeChatPayPayment implements PaymentStrategy {
    public void pay(int amount) {
        // 微信支付逻辑
    }
}

// 上下文
public class PaymentContext {
    private PaymentStrategy strategy;

    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void processPayment(int amount) {
        strategy.pay(amount);
    }
}

6.4 设计模式选择指南

场景 推荐模式 理由
需要动态添加功能 装饰器模式 灵活组合,避免继承
算法结构固定部分可变 模板方法模式 代码复用,结构清晰
算法需要相互替换 策略模式 解耦算法和使用
创建复杂对象 构建者模式 分步构建,代码清晰
创建对象族 工厂模式 解耦创建和使用
控制对象访问 代理模式 增强功能,不修改原类

6.5 避免过度设计

虽然设计模式很有用,但也要避免过度设计:

  1. 简单问题简单解决:不要为了使用模式而使用模式
  2. 按需设计:根据实际需求选择合适的模式
  3. 保持简洁:简单的设计往往比复杂的设计更好
  4. 重构时再引入:先实现功能,重构时再应用模式

七、总结

本文深入讲解了MyBatis中的装饰器模式、模板方法模式和策略模式,通过源码分析和示例代码,帮助读者理解这些设计模式在实际框架中的应用。

7.1 关键要点

装饰器模式:

  • CachingExecutor为Executor添加二级缓存功能
  • Cache装饰器链提供LRU、FIFO、日志等多种功能
  • 动态组合,灵活扩展

模板方法模式:

  • BaseExecutor定义SQL执行的基本流程
  • BaseTypeHandler定义类型处理的基本流程
  • 代码复用,算法稳定

策略模式:

  • RoutingStatementHandler根据配置路由到不同的StatementHandler
  • 不同Executor策略适用于不同的执行场景
  • 算法可替换,代码解耦

7.2 设计模式的价值

  1. 提高代码质量:使代码更易读、易维护、易扩展
  2. 促进架构设计:提供成熟的设计思想和解决方案
  3. 增强团队协作:统一的设计语言和思维方式
  4. 提升个人能力:深入理解设计模式是成为高级工程师的必经之路

相关推荐
高山上有一只小老虎1 天前
mybatisplus分页查询版本 3.5.8 以下和版本 3.5.9及以上的区别
java·spring boot·mybatis
人道领域1 天前
javaWeb从入门到进阶(MyBatis拓展)
java·tomcat·mybatis
J2虾虾1 天前
SpringBoot和mybatis Plus不兼容报错的问题
java·spring boot·mybatis
pp起床2 天前
【苍穹外卖】Day03 菜品管理
java·数据库·mybatis
九皇叔叔2 天前
【01】SpringBoot3 MybatisPlus 工程创建
java·mybatis·springboot3·mybatis plus
BD_Marathon2 天前
MyBatis逆向工程之清晰简洁版
mybatis
九皇叔叔2 天前
【02】SpringBoot3 MybatisPlus 加入日志功能
java·mysql·mybatis·日志·mybatisplus
齐 飞2 天前
MybatisPlus真正的批量新增
spring boot·mybatis
小北方城市网2 天前
Spring Cloud Gateway 生产问题排查与性能调优全攻略
redis·分布式·缓存·性能优化·mybatis
while(1){yan}2 天前
Spring事务
java·数据库·spring boot·后端·java-ee·mybatis