2021SC@SDUSC
前言我在上两篇文章中阐述了 module-article-service-provider 模块下的 ArticleServiceProvider 类,该类是整个 module-article 模块的核心 Service 类。本篇文章中,我将继续分析 module-article-service-provider 模块下的其它 Service 类。
在 module-article-service-provider 模块下一共有三个类,其中 ArticleServiceProvider 已经用了一篇半的篇幅做过介绍了,剩下的 ArticleCategoryServiceProvider 和 ArticleCommentServiceProvider 将在本篇文章进行介绍。
ArticleCategoryServiceProvider 基本结构首先介绍的是 ArticleCategoryServiceProvider,该类是对文章的类别进行操作的 Service 类,继承了 JbootServicebase 类,并实现了 ArticleCategoryService 接口。该类没有静态变量和成员变量。
public class ArticleCategoryServiceProvider extends JbootServicebaseCURDimplements ArticleCategoryService {}
@Override @Cacheable(name = "articleCategory") public ListfindAll() { return super.findAll(); } @Override @CachesEvict({ @CacheEvict(name = "articleCategory", key = "*"), @CacheEvict(name = "article-category", key = "*"), }) public void shouldUpdateCache(int action, Model model, Object id) { super.shouldUpdateCache(action, model, id); } @Override public List findListByType(String type) { return ModelUtil.copy(findListByTypeWithCache(type)); }
上述的代码为该类的 CURD 方法的一个缩影,与前面文章分析得无异,这里来看一下 ModelUtil 工具类。
上面代码中的 findListByType() 方法调用了 ModelUtil.copy() 的方法,后者位于 io.jboot.utils 包下。ModelUtil 类中一共有 5 个 copy() 方法,代码如下:
public static> List copy(List modelList) { if (modelList != null && !modelList.isEmpty()) { List list = modelList instanceof ArrayList ? new ArrayList(modelList.size()) : (List)newInstance(modelList.getClass()); Iterator var2 = modelList.iterator(); while(var2.hasNext()) { M m = (JbootModel)var2.next(); ((List)list).add(copy(m)); } return (List)list; } else { return modelList; } } public static > Set copy(Set modelSet) { if (modelSet != null && !modelSet.isEmpty()) { Set set = modelSet instanceof HashSet ? new HashSet(modelSet.size()) : (Set)newInstance(modelSet.getClass()); Iterator var2 = modelSet.iterator(); while(var2.hasNext()) { M m = (JbootModel)var2.next(); ((Set)set).add(copy(m)); } return (Set)set; } else { return modelSet; } } public static > M[] copy(M[] models) { if (models != null && models.length != 0) { M[] array = (JbootModel[])((JbootModel[])Array.newInstance(models.getClass().getComponentType(), models.length)); int i = 0; JbootModel[] var3 = models; int var4 = models.length; for(int var5 = 0; var5 < var4; ++var5) { M m = var3[var5]; array[i++] = copy(m); } return array; } else { return models; } } public static > Page copy(Page modelPage) { if (modelPage == null) { return null; } else { List modelList = modelPage.getList(); if (modelList != null && !modelList.isEmpty()) { modelPage.setList(copy(modelList)); return modelPage; } else { return modelPage; } } } public static > M copy(M model) { return model == null ? null : model.copy(); } private static T newInstance(Class clazz) { try { return clazz.newInstance(); } catch (Exception var2) { throw new JbootException("can not newInstance class:" + clazz + "n" + var2, var2); } }
这是 5 个重载方法,参数分别为 List
@Cacheable(name = "articleCategory", key = "type:#(type)") public ListfindListByTypeWithCache(String type) { return DAO.findListByColumns(Columns.create("type", type), "order_number asc,id desc"); }
故可得出,在这个 findListByType() 中调用的是参数为 List
@Override @Cacheable(name = "article-category", key = "#(articleId)", liveSeconds = 2 * CacheTime.HOUR, nullCacheEnable = true) public ListfindListByArticleId(long articleId) { List mappings = Db.find("select * from article_category_mapping where article_id = ?", articleId); if (mappings == null || mappings.isEmpty()) { return null; } return mappings .stream() .map(record -> DAO.findById(record.get("category_id"))) .filter(Objects::nonNull) .collect(Collectors.toList()); } @Override public List findListByArticleId(long articleId, String type) { List categoryList = findListByArticleId(articleId); if (categoryList == null || categoryList.isEmpty()) { return null; } return categoryList .stream() .filter(category -> type.equals(category.getType())) .collect(Collectors.toList()); }
Java 8 API 中添加了一个新的抽象 Stream,中文译名为流,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选、排序、聚合等。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。
在本例子中,两个重载方法 findListByArticleId() 分别通过文章类别 ID 和类型如分类、tag、专题来获得文章的 ID List。
通过标签创建或查询@Override public ListdoCreateOrFindByTagString(String[] tags) { if (tags == null || tags.length == 0) { return null; } boolean needClearCache = false; List articleCategories = new ArrayList<>(); for (String tag : tags) { if (StrUtil.isBlank(tag)) { continue; } //slug不能包含字符串点 " . ",否则url不能被访问 String slug = tag.contains(".") ? tag.replace(".", "_") : tag; Columns columns = Columns.create("type", ArticleCategory.TYPE_TAG); columns.add(Column.create("slug", slug)); ArticleCategory articleCategory = DAO.findFirstByColumns(columns); if (articleCategory == null) { articleCategory = new ArticleCategory(); articleCategory.setTitle(tag); articleCategory.setSlug(slug); articleCategory.setType(ArticleCategory.TYPE_TAG); articleCategory.save(); needClearCache = true; } articleCategories.add(articleCategory); } if (needClearCache) { AopCache.removeAll("articleCategory"); } return articleCategories; } @Override public List doCreateOrFindByCategoryString(String[] categories) { if (categories == null || categories.length == 0) { return null; } List articleCategories = new ArrayList<>(); for (String category : categories) { Columns columns = Columns.create("type", ArticleCategory.TYPE_CATEGORY); columns.add(Column.create("slug", category)); ArticleCategory articleCategory = DAO.findFirstByColumns(columns); if (articleCategory == null) { articleCategory = new ArticleCategory(); articleCategory.setTitle(category); articleCategory.setSlug(category); articleCategory.setType(ArticleCategory.TYPE_CATEGORY); articleCategory.save(); } articleCategories.add(articleCategory); } return articleCategories; }
这两个方法用于在创建、修改文章时,对文章类型进行创建或修改。由代码可以看出,在用户创建、修改文章时,通过遍历用户所选择的标签,然后逐个与数据库中的进行比对,若标签存在就直接保存,若不存在则先创建再保存。文章类别同理。
通过父 ID 查询子 ID这个应该属于 JPress 项目的特色内容,它是通过对一个递归方法 findChild(),将相同 ID 的找出来并返回,代码如下:
@Override
public Long[] findCategoryIdsByParentId(long parentId) {
List categories = findAll();
List findedList = new ArrayList<>();
findedList.add(findById(parentId));
findChild(parentId, categories, findedList);
return ArrayUtils.toObject(findedList.stream().mapToLong(category -> category.getId()).toArray());
}
public void findChild(Long parentId, List from, List to) {
for (ArticleCategory category : from) {
if (parentId.equals(category.getParentId())) {
to.add(category);
findChild(category.getId(), from, to);
}
}
}
结语
本文对 ArticleServiceCategoryProvider 类的进行了详细分析,后面将继续对 module-article-service-provider 模块进行分析



