实现原理
1、功能描述:一个用户对同一文章只能点赞一次,第二次就是取消赞
2、建立一个点赞表great,字段有文章ID(aid),点赞用户ID(uid)
3、当有用户进行点赞行为时,使用aid和uid搜索点赞表。

若有该记录,则表示用户已经点过赞,本次点击是取消点赞行为,故删除great表中的该条记录,同时将该文章的点赞数减1。
若无该记录,则表示用户是要点赞,故在great表中添加该记录,同时该文章的点赞数加1。
核心代码分析
核心控制器BaseController:

@Controller
public class BaseController {
    private final GreatRepository greatRepository;
    private final ArticleRepository articleRepository;
    //Spring团队推荐的做法是用构造器来引入依赖,而不是直接使用@Autowired引入
    @Autowired
    public BaseController(GreatRepository greatRepository,
                          ArticleRepository articleRepository) {
        this.greatRepository = greatRepository;
        this.articleRepository=articleRepository;
    }

@RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }

//添加事务支持
    @Transactional
    @RequestMapping("/great")
    public String great(@Param("aid") int aid, @Param("uid") int uid, Model model){
        //查询是否有该用户对该文章的点赞记录
        List<Great> list=greatRepository.findByAidAndUid(aid,uid);
        if (list!=null && list.size()>0){
            //如果找到了这条记录,则删除该记录,同时文章的点赞数减1
            Great great=list.get(0);
            //删除记录
            greatRepository.delete(great.getId());
            //文章点赞数减1,查询时使用Mysql行级锁解决并发覆盖问题
            Article article=articleRepository.findByIdForUpdate(aid);
            article.setGreatNum(article.getGreatNum()-1);
            articleRepository.saveAndFlush(article);
        }else {
            //如果没有找到这条记录,则添加这条记录,同时文章点赞数加1
            Great great=new Great();
            great.setAid(aid);
            great.setUid(uid);
            //添加记录
            greatRepository.saveAndFlush(great);
            //文章点赞数加1,查询时使用Mysql行级锁解决并发覆盖问题
            Article article=articleRepository.findByIdForUpdate(aid);
            article.setGreatNum(article.getGreatNum()+1);
            articleRepository.saveAndFlush(article);
        }
        model.addAttribute("details",articleRepository.findAll());
        return "detail";
    }

}

Aritcle实体的持久化仓库ArticleRepository

@Repository
public interface ArticleRepository extends JpaRepository<Article,Integer>{
    @Lock(value = LockModeType.PESSIMISTIC_WRITE)
    Article findByIdForUpdate(Integer id);
}

注意其中使用了@Lock注解以及 LockModeType.PESSIMISTIC_WRITE来让JPA使用数据库层的行级锁,在事务中,该行级锁能解决对同一条记录的并发修改问题。

代码中已经附有详细注解

完整实例的相关信息
为了突出重点,项目前端较为简陋,功能已经通过测试。
项目采用的框架:
1、容器框架:SpringBoot
2、持久层框架:Spring Data JPA
3、渲染框架:Thymeleaf
4、版本控制:Git
5、依赖:Maven
6、数据库:Mysql
数据库建表文件Schema.sql:

DROP TABLE IF EXISTS `article`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `num` int(11) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `article`
--

LOCK TABLES `article` WRITE;
/*!40000 ALTER TABLE `article` DISABLE KEYS */;
INSERT INTO `article` VALUES (1,1),(2,0);
/*!40000 ALTER TABLE `article` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `great`
--

DROP TABLE IF EXISTS `great`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `great` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `aid` int(11) NOT NULL,
  `uid` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `great`
--

LOCK TABLES `great` WRITE;
/*!40000 ALTER TABLE `great` DISABLE KEYS */;
INSERT INTO `great` VALUES (5,1,1);
/*!40000 ALTER TABLE `great` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

特别说明:本文章的目的只是单纯向大家说明点赞这个功能的实现思路。为了保证逻辑尽量清晰和简单,因而并没有涉及到性能优化。示例代码中的锁机制能保证并发访问下的安全性,但会对系统并发性能产生一定的影响,但在一般系统中,由于大量用户同时对同一文章集中点赞的情况并不常见,因此性能损耗扔在可以接受的范围内。

如果大家在使用过程中确实有高并发的需要,那么可以考虑使用Redis这类缓存数据库来替代mysql。Redis是高性能的内存KV数据库,且是单线程运行的,因此性能和安全性问题都能得到完美的解决。

关于JPA中如何使用行级锁,可以参考这篇文章:https://blog.csdn.net/fengyuxue11011/article/details/47039765

JavaWeb中点赞功能的实现及完整实例的更多相关文章

  1. JavaWeb中登陆功能

    首先我们要JavaWeb登陆的基本流程:JSP页面发送请求-->Servlet-->Servlet通过调用方法从数据库中得到数据并将结果返回页面 我们先建立三个jsp页面,包括login. ...

  2. JavaWeb中的文件上传和下载功能的实现

    导入相关支持jar包:commons-fileupload.jar,commons-io.jar 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上 ...

  3. 全栈项目|小书架|服务器端-NodeJS+Koa2 实现点赞功能

    效果图 接口分析 通过上面的效果图可以看出,点赞入口主要是在书籍的详情页面. 而书籍详情页面,有以下几个功能是和点赞有关的: 获取点赞状态 点赞 取消点赞 所以项目中理论上与点赞相关的接口就以上三个. ...

  4. Qt调用dll中的功能函数

    声明: 事先我已经自己动手写了一个简单的dll文件(myDLL.dll),C版接口的.并且用我前两篇有关DLL文章里面的方法,从dll中导出了导入库(.lib)文件,dll中有两个函数,原型如下:   ...

  5. 在Javaweb中使用Scala

    Java 是一门比较优秀的编程语言, 其最大功劳是建立非常繁荣的JVM平台生态.不过 Java 语法比较麻烦,写过 C, Python 的人总是想使用简洁的语法,又希望利用上 Java 平台的强大,因 ...

  6. 在javaweb中通过servlet类和普通类读取资源文件

    javaweb有两种方式读取资源文件 在Servlet中读取,可以使用servletContext,servletContext可以拿到web所有的资源文件,然后随便读,但是这种方法不常用,尽量少在S ...

  7. JavaScript实现评论点赞功能

    通过分析评论功能的逻辑关系,学会如何使用JavaScript实现评论.回复.点赞等各种功能 1.学会JavaScript处理日期和时间. 2.掌握Dom操作中的添加/删除子节点方法. 3.使用setT ...

  8. JavaScript cookie操作实现点赞功能

    JavaScript cookie操作实现点赞功能 参考实现原理,但是代码不够简洁,简洁代码参考:js操作cookie 实现一个点赞功能十分简单,主要问题在于不能重复点赞.  若是一个有用户的网站,可 ...

  9. JavaWeb中监听器+过滤器+拦截器区别、配置和实际应用

    JavaWeb中监听器+过滤器+拦截器区别.配置和实际应用 1.前沿上一篇文章提到在web.xml中各个元素的执行顺序是这样的,context-param-->listener-->fil ...

随机推荐

  1. arm-linux-gcc-4.5.1安装方法

    写在前面 之前写了一篇arm-linux-gcc-5.4.0的安装方法,但是后来发现5.4.0这个版本可能有些太新了,所以又找了这个4.5.1版本(低版本),由FriendlyARM(友善之臂)提供, ...

  2. 父组件调用子组件中的方法- this.$refs.xxx.子组件方法();

    子组件中有一个说的方法 在父组件中去调用当你点击的时候 去调用子组件中的方法 fu.vue 在父组件的方法中调用子组件的方法,很重要 this.$refs.mychild.parentHandlecl ...

  3. Jenkins显示reportng测试报告

    在搭建Jenkins自动化测试平台后,我们点击立即构建就可以运行测试用例了,但没有地方查看测试报告,现在写这篇博客总结怎么在Jenkins显示reportng测试报告. 在maven项目配置repor ...

  4. 201871010111-刘佳华《面向对象程序设计(java)》第十五周学习总结

    201871010111-刘佳华<面向对象程序设计(java)>第十五周学习总结 实验十三  Swing图形界面组件(二) 实验时间 2019-12-6 第一部分:理论知识总结 5> ...

  5. 1【西北师大-2108Java】第一次作业成绩汇总

    [西北师大-2108Java]第一次作业成绩汇总 经过本次作业的练习,了解了Java最基本的知识和Java的发展:了解了Java到底是一门怎样的语言,也知道了学习Java的乐趣,懂得了去选择Java开 ...

  6. 解决问题:Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

    Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean. 注释掉, ...

  7. OC方法交换swizzle详细介绍——不再有盲点

    原文链接:https://www.cnblogs.com/mddblog/p/11105450.html 如果对方法交换已经比较熟悉,可以跳过整体介绍,直接看常见问题部分 整体介绍 方法交换是runt ...

  8. [C2W1] Improving Deep Neural Networks : Practical aspects of Deep Learning

    第一周:深度学习的实用层面(Practical aspects of Deep Learning) 训练,验证,测试集(Train / Dev / Test sets) 本周,我们将继续学习如何有效运 ...

  9. Python str & repr

    Python str & repr repr 更多是用来配合 eval 的 (<- 点击查看),str 更多是用来转换成字符串格式的 str() & repr() str() 和 ...

  10. Python tempfile (临时文件)

    Python tempfile 大量临时数据放在内存中会占用大量资源,可以使用临时文件来进行储存 临时文件不用命名,且使用后会被自动删除 TemporaryFile 使用 TemporaryFile ...