您现在的位置是:首页 > 文章详情

Java实现DDD中UnitOfWork

日期:2020-05-19点击:396

Java实现DDD中UnitOfWork

背景
Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.
Unit of Work --Martin Fowler
Unit Of Work模式,由马丁大叔提出,是一种数据访问模式。UOW模式的作用是在业务用例的操作中跟踪对象的所有更改(增加、删除和更新),并将所有更改的对象保存在其维护的列表中。在业务用例的终点,通过事务,一次性提交所有更改,以确保数据的完整性和有效性。总而言之,UOW协调这些对象的持久化及并发问题。

Uow的本质
UOW跟踪变化
UOW维护了一个变更列表
UOW将跟踪到的已变更的对象保存到变更列表中
UOW借助事务一次性提交变更列表中的所有更改
UOW处理并发
对于以上这些点,在C#的EF框架中,DBContext已经实现。
而这里主要描述如何用java实现以上要点。

Repository
将仓储Repo作为聚合的范型类
在Repo中维护一个聚合与聚合状态的集合
在Repo中每次add/update/delete等操作时,将操作的聚合对象,和其最终状态存入集合中
在Repo中的retrieve方法,将聚合检索出来并存入Repo的集合中
代码如下:
public class RepositoryBase implements IRepository {

private HashMap<IBusinessKey, RepositoryComponent<T>> map = new HashMap<IBusinessKey, RepositoryComponent<T>>(); private Class<T> tClass; public RepositoryBase(Class<T> tClass) { RepositoryThreadLocalHelper.newInstance().put(tClass.getSimpleName(), this); this.tClass = tClass; } public void add(T t) { IBusinessKey bk = t.getBusinessKey(); if (map.containsKey(bk) && RepositoryComponentState.DELETED.equals(map.get(bk).getState())) { map.put(bk, new RepositoryComponent<T>(t, RepositoryComponentState.UNCHANGED)); } else { map.put(bk, new RepositoryComponent<T>(t, RepositoryComponentState.ADDED)); } } public void update(T t) { IBusinessKey bk = t.getBusinessKey(); if (map.containsKey(bk) && RepositoryComponentState.ADDED.equals(map.get(bk).getState())) { map.put(bk, new RepositoryComponent<T>(t, RepositoryComponentState.ADDED)); } else { map.put(bk, new RepositoryComponent<T>(t, RepositoryComponentState.MODIFIED)); } } public void delete(IBusinessKey bk) { if (map.containsKey(bk) && RepositoryComponentState.ADDED.equals(map.get(bk).getState())) { map.get(bk).setState(RepositoryComponentState.UNCHANGED); } else { map.put(bk, new RepositoryComponent<T>(retrieve(bk), RepositoryComponentState.DELETED)); } } public void delete(T t) { IBusinessKey bk = t.getBusinessKey(); if (map.containsKey(bk) && RepositoryComponentState.ADDED.equals(map.get(bk).getState())) { map.get(bk).setState(RepositoryComponentState.UNCHANGED); } else { map.put(bk, new RepositoryComponent<T>(t, RepositoryComponentState.DELETED)); } } public T retrieve(IBusinessKey bk) { if (map.containsKey(bk)) { return map.get(bk).getT(); } else { RepositoryBuilder<T> builder = new RepositoryBuilder<T>(); T t = builder.buildBo(tClass, bk); map.put(bk, new RepositoryComponent<T>(t, RepositoryComponentState.UNCHANGED)); return t; } }

}
RepositoryComponentState
聚合存在于内存中的状态,主要分为以下5个状态
Added 4
The entity is being tracked by the context but does not yet exist in the database.
Deleted 2
The entity is being tracked by the context and exists in the database. It has been marked for deletion from the database.
Detached 0
The entity is not being tracked by the context.
Modified 3
The entity is being tracked by the context and exists in the database. Some or all of its property values have been modified.
Unchanged 1
The entity is being tracked by the context and exists in the database. Its property values have not changed from the values in the database.
Uow
增加自定义注解UowTransaction
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UowTransaction {
}
增加注解切面,在所注解的方法上执行完毕后调用uowService.saveChange()
@Slf4j
@Component
public class UowService {

@Transaction public void saveChange() { SqlSessionFactory factory = (SqlSessionFactory) SpringContextUtil.getBean("sqlSessionFactory"); SqlSession session = factory.openSession(); Collection<IRepository> repos = RepositoryThreadLocalHelper.newInstance().getRepositories(); for (IRepository repo : repos) { repo.flushDb(session); } }

}
RepositoryFlush
flushDb时将内存中的聚合集合统一刷新入数据库
flushAdd:将聚合从聚合根到所有子节点依次入库
flushModify:更改数据库聚合时,先检索出数据库中原聚合originEntity,然后将原聚合与更改后聚合依次对比,子节点中根据对比内容做新增/删除/修改
flushDelete:将聚合从聚合根到所有子节点依次删除
具体参考代码如下:
@Slf4j
public class RepositoryFlush {

private SqlSession session; private RepositoryConfigHelper configHelper = new RepositoryConfigHelper(); RepositoryFlush(SqlSession session) { this.session = session; } void flushAdd(IBusinessEntity bo) { RepositoryConfig repoConfig = configHelper.getRepositoryConfig(bo); //bo->do Object dataObject = TypeConversion.unWrapPrimitiveType(bo, repoConfig.getEntityClass()); BaseMapper mapper = this.session.getMapper(repoConfig.getEntityMapperClass()); mapper.insert(dataObject); List<String> childNames = BusinessObjectManager.getEntityNamesByComposition( bo.getBusinessObjectRoot().getArtifactName(), bo.getArtifactName()); if (childNames.size() > 0) { for (String name : childNames) { List<IBusinessEntity> childBes = bo.getEntitiesByComposition(name); for (IBusinessEntity be : childBes) { flushAdd(be); } } } } void flushModify(IBusinessEntity originBo, IBusinessEntity bo) { RepositoryConfig repoConfig = configHelper.getRepositoryConfig(bo); // bo->do Object dataObject = TypeConversion.unWrapPrimitiveType(bo, repoConfig.getEntityClass()); BaseMapper mapper = this.session.getMapper(repoConfig.getEntityMapperClass()); //1. gdt->基本类型 //2. bk:驼峰形式改为下划线形式 Map<String, String> bkMap = RepositoryUtil.MapCamelCaseToUnderscore( UnWrapMapUtil.getGdtValue( bo.getBusinessKey().getBusinessKeyMap())); // update root UpdateWrapper updateWrapper = new UpdateWrapper(); updateWrapper.allEq(bkMap); mapper.update(dataObject, updateWrapper); // 遍历子be List<String> originChildNames = BusinessObjectManager.getEntityNamesByComposition( originBo.getBusinessObjectRoot().getArtifactName(), originBo.getArtifactName() ); if (originChildNames.size() > 0) { for (String name : originChildNames) { List<IBusinessEntity> originChildBes = originBo.getEntitiesByComposition(name); List<IBusinessEntity> childBes = bo.getEntitiesByComposition(name); for (IBusinessEntity be : childBes) { // be在数据库中,从originChildBes列表中删除,所有删除完剩下的->需要delete的 Optional<IBusinessEntity> optional = originChildBes.stream() .filter( x -> { // 判断bk是否相同 return x.getBusinessKey().equals(be.getBusinessKey()); } ).findFirst(); if (optional.isPresent()) { // 数据库中存在:修改 IBusinessEntity originBe = optional.get(); originChildBes.remove(originBe); flushModify(originBe, be); } else { // 数据库中不存在:新增 flushAdd(be); } } // 数据库中存在,但modifyBo中没有:删除 if (originChildBes.size() > 0) { for (IBusinessEntity originDeleteBe : originChildBes) { flushDelete(originDeleteBe); } } } } } void flushDelete(IBusinessEntity bo) { RepositoryConfig repoConfig = configHelper.getRepositoryConfig(bo); BaseMapper mapper = this.session.getMapper(repoConfig.getEntityMapperClass()); IBusinessKey bk = bo.getBusinessKey(); Map<String, String> bkMap = RepositoryUtil.MapCamelCaseToUnderscore( UnWrapMapUtil.getGdtValue( bo.getBusinessKey().getBusinessKeyMap())); mapper.deleteByMap(bkMap); List<String> childNames = BusinessObjectManager.getEntityNamesByComposition( bo.getBusinessObjectRoot().getArtifactName(), bo.getArtifactName() ); if (childNames.size() > 0) { for (String name : childNames) { List<IBusinessEntity> childBes = bo.getEntitiesByComposition(name); for (IBusinessEntity be : childBes) { flushDelete(be); } } } }

}
在repo中增加flushDb方法,如下:
public void flushDb(SqlSession session) {

RepositoryFlush flush = new RepositoryFlush(session); for (Map.Entry<IBusinessKey, RepositoryComponent<T>> entry : map.entrySet()) { RepositoryComponentState state = entry.getValue().getState(); T t = entry.getValue().getT(); if (RepositoryComponentState.ADDED.equals(state)) { flush.flushAdd(t); } else if (RepositoryComponentState.MODIFIED.equals(state)) { RepositoryBuilder<T> builder = new RepositoryBuilder<T>(); T rootT = builder.buildBo(tClass, t.getBusinessKey()); flush.flushModify(rootT, t); } else if (RepositoryComponentState.DELETED.equals(state)) { flush.flushDelete(t); } }

}
Retrieve&&RepositoryBuilder
repo中提供retrieve方法用于检索聚合
若聚合在repo的集合中已存在则直接返回聚合,若无聚合,则通过RepoBuilder从数据库中捞取聚合
public class RepositoryBuilder {

private RepositoryConfigHelper configHelper = new RepositoryConfigHelper(); private Map<String, List<IBusinessEntity>> beValues = new HashMap<>(); T buildBo(Class<T> tClass, IBusinessKey businessKey) { SqlSessionFactory factory = (SqlSessionFactory) SpringContextUtil.getBean("sqlSessionFactory"); SqlSession session = factory.openSession(); try { RepositoryConfig config = configHelper.getRepositoryConfig((IBusinessEntity) tClass.newInstance()); BaseMapper mapper = session.getMapper(config.getEntityMapperClass()); QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.allEq( RepositoryUtil.MapCamelCaseToUnderscore( UnWrapMapUtil.getGdtValue( businessKey.getBusinessKeyMap())) ); // 获取到当前do object Object object = mapper.selectOne(queryWrapper); if (object == null) { throw new RuntimeException("未找到数据库对应DO数据。"); } // build child IBusinessObjectRoot rootT = (IBusinessObjectRoot) TypeConversion.wrapPrimitiveType(object, tClass); buildChildBe(session, tClass, object, rootT); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } finally { session.close(); } // beValues->bo return (T) BusinessObjectManager.newInstance().createBusinessObjectInstance( tClass.getSimpleName(), beValues ); } /** * 1. do->beValue * 2. beValues.add(beValue) * 3. bo查子bo * 4. 返回 */ private void buildChildBe(SqlSession session, Class<T> tClass, Object object, IBusinessObjectRoot rootT) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { T t = TypeConversion.wrapPrimitiveType(object, tClass); if (t == null) { throw new RuntimeException("DO数据类型转BE异常"); } // 加入beValues if (beValues.containsKey(t.getArtifactName())) { beValues.get(t.getArtifactName()).add(t); } else { beValues.put(t.getArtifactName(), new ArrayList<IBusinessEntity>() {{ add(t); }}); } // IBusinessKey bk = t.getBusinessKey(); List<String> childClassNames = BusinessObjectManager.getEntityNamesByComposition( rootT.getArtifactName(), t.getArtifactName() ); if (childClassNames.size() > 0) { for (String childClassName : childClassNames) { Class childClass = Class.forName(childClassName); // 构造函数:包含父be(无结构构造函数) IBusinessEntity nullChildBe = (IBusinessEntity) childClass .getConstructor() .newInstance(); RepositoryConfig childConfig = configHelper.getRepositoryConfig(rootT, nullChildBe); BaseMapper childMapper = session.getMapper(childConfig.getEntityMapperClass()); List dbList = childMapper.selectByMap( RepositoryUtil.MapCamelCaseToUnderscore( UnwrapMapUtil.getGdtValue(bk.getBusinessKeyMap()) ) ); for (Object dbObject : dbList) { buildChildBe(session, childClass, dbObject, rootT); } } } }

}
最后
以上代码只包含Uow、Repo等关键代码,完整代码使用还需要配合聚合的建模,全局统一类型的使用
代码仅供学习,以后有机会会上传到github中
资料参考
EntityFrameworkCore
UnitOfWork知多少

原文地址https://www.cnblogs.com/llicat/p/12924591.html

原文链接:https://yq.aliyun.com/articles/761790
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章