<2021SC@SDUSC>山东大学软件工程应用与实践JPress代码分析(三)

2021SC@SDUSC

目录

1.接口类SinglePageCommentService.java

2.实现类SinglePageCommentServiceProvider.java

2.1 注解

2.1.1 @Bean——Jboot注解

2.1.2 @Inject——JFinal注解

2.2 实现类的代码解析

2.2.1 join()方法

3.线程PageViewsCountUpdateTask.java

3.1 @FixedRate注解

3.2 run()方法

4.总结


上篇文章分析了代码模块module-page的两个子模块module-page-service、model-page-service-provider,本篇文章将继续分析这两个子模块的其他部分。

1.接口类SinglePageCommentService.java

  • 该接口为页面的留言的接口,定义了需要调用的方法。
  • 与SinglePageService不同,因为没有对于多个表的操作,他没有继承JbootServiceJoiner。
  • 因为一个Model为数据库的一条数据,所以本接口类用的是model.Columns这个包中的方法。
import com.jfinal.plugin.activerecord.Page;
import io.jpress.module.page.model.SinglePageComment;
import io.jboot.db.model.Columns;
import java.util.List;
  • SinglePageCommentService使用model、columns的服务,定义对数据的增删改查的函数,比如根据提交查询数据、根据id批量删除等,包中方法示例如下:
/**
     * 根据提交查询数据量
     *
     * @param columns
     * @return
     */
    long findCountByColumns(Columns columns);
/**
     * 根据 多个 id 批量删除
     *
     * @param ids
     * @return
     */
    boolean batchDeleteByIds(Object... ids);

boolean doChangeStatus(Long id, String status);

boolean batchChangeStatusByIds(String statusNormal, Object... toArray);
  • 在以上方法中我们可以看到,参数列表中使用Object... ids来声明参数长度的可变性。在舒勇这个函数的时候,我们可以传入多个参数,在后续controller层面的调用中,使用了JFinal的相关函数对字符串进行拼接,这会在我之后的代码分析博客中讲到。

2.实现类SinglePageCommentServiceProvider.java

  • SinglePageCommentServiceProvider继承自JbootServiceBase,使用其中的方法实现SinglePageCommentService。
  • SinglePageCommentServiceProvider实现SinglePageCommentService接口,具体化之中的函数。

2.1 注解

2.1.1 @Bean——Jboot注解

//使用
import io.jboot.aop.annotation.Bean;
@Bean
public class SinglePageCommentServiceProvider extends JbootServiceBase<SinglePageComment> implements SinglePageCommentService

//Jboot的Bean注解代码
package io.jboot.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface Bean {
    String name() default "";
}
  • 由代码段得知,Jboot的Bean注解是由java.lang.annotation的很多个注解共同构成的。
  • @Bean 注解在类上,这样在Jboot启动的时候,会自动扫描到对应的class,并通过 JbootAopFactory 把对应的service和serviceprovider添加上关联关系。
  • @Inherited:如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解。
  • @Retention(RetentionPolicy.RUNTIME):注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。
  • @Target:注解的作用目标,接口、类、枚举、注解、字段、枚举的常量、方法。

2.1.2 @Inject——JFinal注解

//使用
import com.jfinal.aop.Inject;
@Inject
    private SinglePageService pageService;

//JFinal的Inject注解代码
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Inject {
    Class<?> value() default Void.class;
}
  • 由代码段可知,JFinal的Inject注解是由java.lang.annotation的很多个注解共同构成的。
  • @Inject:由JSR-330提供,注入bean的注解。
  • @Inherited、@Retention、@Target在上文已经分析过了。
  • @Documented:默认情况下,javadoc是不包括注解的。 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中。

2.2 实现类的代码解析

以下会介绍每个函数的用途,重要的函数会贴详细代码展示。

通过评论的id找到该评论

@Override
public SinglePageComment findById(Object id) {
    SinglePageComment comment = super.findById(id);
    pageService.join(comment, "page_id", "page");
    userService.join(comment, "user_id");
    return comment;
}

通过status属性找到count值

@Override
public long findCountByStatus(String status)

通过status属性给数据进行分页,该函数在pageController中被调用。

@Override
public Page<SinglePageComment> _paginateByStatus(int page, int pagesize, Long articleId, String keyword, String status) {
    Columns columns = Columns.create("article_id", articleId)
            .eq("status", status)
            .likeAppendPercent("content", keyword);

    Page<SinglePageComment> p = DAO.paginateByColumns(page,
            pagesize,
            columns,
            "id desc");

    userService.join(p, "user_id");
    pageService.join(p, "page_id", "page");
    return p;
}

在status的值为normal的情况下,根据页面的id进行分页

@Override
public Page<SinglePageComment> paginateByPageIdInNormal

更新页面一级评论的子评论数

@Override
public void doIncCommentReplyCount(long commentId)

改变页面的status值

@Override
public boolean doChangeStatus(Long id, String status)
  • 在以上的重要方法中,我们发现join()方法、create()方法、eq()方法、likeAppendPercent()方法的使用频度很高。
  • create():新建一列。
  • eq():传入属性名和值,在数据库中会搜索属性名相同且值相同的数据。
  • likeAppendPercent():like模糊查询。
  • join()方法比较重要且多样,用以下一个具体例子来解释。

2.2.1 join()方法

是底层Jboot的函数。在 Jboot 中,提供了 Join 系列方法,我们在 Service 层可以直接使用 Join 进行 一对一、一对多、多对一、多对对 的查询操作。

以下例子来自jboot官方文档:http://jboot.io/docs/db.html#一对一、一对多、多对一、多对对

假设存在这样的关系:一篇文章只有一个作者,一个作者可以写多篇文章,一篇文章可以归属多个文章分类、一个文章分类有可以包含多篇文章。

那么,表结构设计如下:

文章表 article :

CREATE TABLE `article` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `author_id` int(11) unsigned DEFAULT NULL COMMENT '文章作者ID',
  `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文章标题',
  `content` text COLLATE utf8mb4_unicode_ci COMMENT '文章内容',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

文章作者表 author :

CREATE TABLE `author` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `nickname` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '作者昵称',
  `email` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '作者邮件',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

文章分类表 category :

CREATE TABLE `category` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '分类标题',
  `description` text COLLATE utf8mb4_unicode_ci COMMENT '分类描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

文章分类和分类的 多对对关系表: article_category:

CREATE TABLE `article_category` (
  `article_id` int(11) unsigned NOT NULL COMMENT '文章ID',
  `category_id` int(11) unsigned DEFAULT NULL COMMENT '分类ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

这样,在我们的代码里应该存在 3 个Service,分别是:

  • ArticleService 用于查询文章相关的服务
  • CategoryService 用于查询文章分类相关的服务
  • AuthorService 用于查询作者的服务

ArticleService 代码如下:

public class ArticleService extends JbootServiceBase<Article> {

    @Inject
    private AuthorService authorService;

    @Inject
    private CategoryService categoryService;

    public List<Article> findListWithAuthorAndCategories(){
        List<Article> articles = DAO.findAll();

       // 查询出每篇文章的作者
        authorService.join(articles,"author_id");

      // 查询文章的所属分类
        categoryService.joinManyByTable(articles,"article_category","article_id","category_id");

        return articles;
    }
}

AuthorService 代码如下:

public class AuthorService extends JbootServiceBase<Author> {

    @Inject
    private ArticleService articleService;

    public List<Author> findListWithArticles(){
        List<Author> authors = DAO.findAll();

      //查询每个作者的所有文章
        articleService.joinMany(authors,"author_id");

        return authors;
    }
}

CategoryService 代码如下:

public class CategoryService extends JbootServiceBase<Category> {

    @Inject
    private ArticleService articleService;

    public List<Category> findListWithArticles(){
        List<Category> categories = DAO.findAll();

      //查询每个分类的所有文章
        articleService.joinManyByTable(categories,"article_category","category_id","article_id");

        return categories;
    }
}

3.线程PageViewsCountUpdateTask.java

  • 这个类代码较短,主要作用是定期对页面的访问数量进行计算。
  • 实现Runnable接口,使用其run()方法。

3.1 @FixedRate注解

  • fixRate:按照一定的速率执行,是从上一次方法执行开始的时间算起,如果上一次方法阻塞住了,下一次也是不会执行,但是在阻塞这段时间内累计应该执行的次数,当不再阻塞时,一下子把这些全部执行掉,而后再按照固定速率继续执行.
  • initialDelay参数:在容器启动后,延迟10秒后再执行一次定时器。
  • 所以以下代码指的是定时器在容器启动后延迟一秒钟再执行定时器,且之后间隔一秒钟执行一次定时器。
@FixedRate(period = 60, initialDelay = 60)
public class PageViewsCountUpdateTask implements Runnable

3.2 run()方法

@Override
public void run() {
//如果在map中,该页面的访问值本身为空就看会
    if (countsMap.isEmpty()) {
        return;
    }
//用之前生成的countsMap,为页面的访问值和页面之间建立哈希map
    Map<Long, AtomicLong> pageViews = new HashMap<>(countsMap);
countsMap.clear();
//遍历,对对应的文章id找到文章,更新其页面访问值
    for (Map.Entry<Long, AtomicLong> entry : pageViews.entrySet()) {
        Db.update("update single_page set view_count = view_count + "
                + entry.getValue().get()
                + " where id = ? ", entry.getKey());
        Aop.get(SinglePageService.class).deleteCacheById(entry.getKey());
    }
}

4.总结

本篇博客通过分析页面评论处理的接口类he实现类,了解到部分Jboot、JFinal的注解的使用,并且加深了Jboot的join()函数的功能之强大,对上一篇博客的JbootServiceJoiner理解更加深刻。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值