现代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
相关产品推荐
相关产品推荐

