You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

现代Java实现DAO模式:如何为mapObjectModel添加跨调用状态支持

最优实现方案

核心思路

首先排除用DAO类成员变量存储状态的方案:DAO通常是单例Bean,成员变量会引发多线程并发下的数据错乱问题。我们采用上下文对象作为单次查询执行周期内的状态容器,仅修改抽象层兼容逻辑,所有现有DAO无需改动,仅针对有状态需求的特定DAO做定制实现即可。

具体实现步骤

1. 扩展DomainMapper接口,新增带上下文的重载方法(兼容旧实现)

interface DomainMapper<S, T> {
    // 原有方法保持不变,现有DAO的实现完全不受影响
    T mapObjectModel(S source);

    // 新增带上下文的重载方法,默认直接调用原有方法,旧DAO无需任何适配
    default T mapObjectModel(S source, Map<Object, Object> context) {
        return mapObjectModel(source);
    }
}

2. 修改AbstractDao抽象类,新增支持上下文的执行逻辑

public abstract class AbstractDao<Source, Target> implements DomainMapper<Source, Target> {
    // 原有execute方法保持不变,旧DAO调用逻辑完全兼容
    protected List<T> execute(SqlParameter params...) {
        // 内部直接调用带上下文的重载方法,传入空上下文
        return execute(params, new HashMap<>());
    }

    // 新增支持自定义上下文的execute方法,供有状态需求的DAO调用
    protected List<T> execute(SqlParameter params, Map<Object, Object> context) {
        // 原有查询执行逻辑保持不变
        public S mapRow(ResultSet rs, int rowNum) {
            S source = super.mapRow(rs, rowNum);
            // 调用带上下文的映射方法
            return mapObjectModel(source, context);
        }
        // 原有返回逻辑保持不变
    }
}

3. 仅改造需要状态传递的SpecificDAO

public class SpecificDAO extends AbstractDAO<Source, Target> {
    // 仅重写带上下文的映射方法即可
    @Override
    public Target mapObjectModel(Source s, Map<Object, Object> context) {
        Long parentId = s.getParentId();
        // 从上下文获取已创建的父对象
        Parent parent = (Parent) context.get(parentId);
        if (parent == null) {
            parent = new Parent();
            parent.setId(parentId);
            parent.setChildren(new ArrayList<>());
            context.put(parentId, parent);
        }
        // 构造当前行的子对象,追加到父对象的子列表中
        Child child = new Child();
        child.setId(s.getChildId());
        parent.getChildren().add(child);
        return parent;
    }

    // 对外提供聚合查询接口
    public Parent getParentWithChildren(Long parentId) {
        SqlParameter params = buildQueryParams(parentId);
        Map<Object, Object> context = new HashMap<>();
        // 执行查询,上下文会自动在逐行映射时传递状态
        execute(params, context);
        // 直接从上下文获取聚合完成的父对象即可
        return (Parent) context.get(parentId);
    }
}

用到的设计模式

该方案核心采用上下文模式(Context Pattern),将单次查询的状态存储在和执行周期绑定的上下文对象中,隔离不同调用的状态,天然线程安全。如果后续有多表聚合场景需要复用已创建的对象,也可以结合享元模式优化对象创建逻辑。

方案优势

  • 侵入性极低:已有的20个DAO不需要做任何修改,完全兼容原有逻辑
  • 线程安全:上下文是execute方法内的局部变量,不同调用的上下文完全隔离,不存在并发竞争问题
  • 扩展灵活:上下文可存储任意类型的状态,后续其他DAO有状态传递需求时可以直接复用该能力
  • 实现简单:仅需修改接口、抽象类各加两个重载方法,开发成本极低

内容的提问来源于stack exchange,提问作者RookieDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 19:45:05