JavaWeb中点赞功能的实现及完整实例
实现原理
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中点赞功能的实现及完整实例的更多相关文章
- JavaWeb中登陆功能
首先我们要JavaWeb登陆的基本流程:JSP页面发送请求-->Servlet-->Servlet通过调用方法从数据库中得到数据并将结果返回页面 我们先建立三个jsp页面,包括login. ...
- JavaWeb中的文件上传和下载功能的实现
导入相关支持jar包:commons-fileupload.jar,commons-io.jar 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上 ...
- 全栈项目|小书架|服务器端-NodeJS+Koa2 实现点赞功能
效果图 接口分析 通过上面的效果图可以看出,点赞入口主要是在书籍的详情页面. 而书籍详情页面,有以下几个功能是和点赞有关的: 获取点赞状态 点赞 取消点赞 所以项目中理论上与点赞相关的接口就以上三个. ...
- Qt调用dll中的功能函数
声明: 事先我已经自己动手写了一个简单的dll文件(myDLL.dll),C版接口的.并且用我前两篇有关DLL文章里面的方法,从dll中导出了导入库(.lib)文件,dll中有两个函数,原型如下: ...
- 在Javaweb中使用Scala
Java 是一门比较优秀的编程语言, 其最大功劳是建立非常繁荣的JVM平台生态.不过 Java 语法比较麻烦,写过 C, Python 的人总是想使用简洁的语法,又希望利用上 Java 平台的强大,因 ...
- 在javaweb中通过servlet类和普通类读取资源文件
javaweb有两种方式读取资源文件 在Servlet中读取,可以使用servletContext,servletContext可以拿到web所有的资源文件,然后随便读,但是这种方法不常用,尽量少在S ...
- JavaScript实现评论点赞功能
通过分析评论功能的逻辑关系,学会如何使用JavaScript实现评论.回复.点赞等各种功能 1.学会JavaScript处理日期和时间. 2.掌握Dom操作中的添加/删除子节点方法. 3.使用setT ...
- JavaScript cookie操作实现点赞功能
JavaScript cookie操作实现点赞功能 参考实现原理,但是代码不够简洁,简洁代码参考:js操作cookie 实现一个点赞功能十分简单,主要问题在于不能重复点赞. 若是一个有用户的网站,可 ...
- JavaWeb中监听器+过滤器+拦截器区别、配置和实际应用
JavaWeb中监听器+过滤器+拦截器区别.配置和实际应用 1.前沿上一篇文章提到在web.xml中各个元素的执行顺序是这样的,context-param-->listener-->fil ...
随机推荐
- Python—实现ssl认证
https://blog.csdn.net/vip97yigang/article/details/84721027 https://www.cnblogs.com/lsdb/p/9397530.ht ...
- cpu 乱序执行与问题【转】
转自:https://blog.csdn.net/lizhihaoweiwei/article/details/50562732 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议 ...
- bootstrap多级下拉菜单
只需为下拉菜单的任意 <li> 元素添加 .dropdown-submenu 的类,并在该 <li> 元素下添加 .dropdown-menu 类的列表,就可以为该菜单项添加一 ...
- shellnet运行train_val_seg.py
1.semantic3d数据集准备:prepare_semantic3d_data.py 11个测试数据集(.txt文件): 假如运行的是室外点云数据集“seg_semantic3d”,可能需要做以下 ...
- CF1278C-Berry Jam-(前缀和)
https://vjudge.net/problem/CodeForces-1278C 题意:有2n瓶果酱,中间有一个楼梯隔开,从中间往左或右两边清空果酱,使得两种果酱的数量相等,最少要清空多少瓶 思 ...
- go 通过select实现超时
package main import ( "fmt" "time" ) func main() { ch := make(chan int) quit := ...
- H5开发 连接蓝牙打印机 打印标签(斑马ZR628)
1.连接蓝牙打印机(先用手机自带蓝牙进行配对),然后绑定出已配对的蓝牙设备(用来选择/切换打印机之用),代码如下 已配对蓝牙设备,中显示的就是已连接的,点击一下即可 代码: <!DOCTYPE ...
- docker 部署 HFish(集群部署)
主节点部署: docker run -d --name hfish-master -p : -p : -p : -p : -p : -p : -p : -p : -p : -p : -p : -p : ...
- bootstrap-editable 中关于onEditableSave 回调
问题描述 在对bootstrap-editable 进行编辑时,有两种使用方法:一种直接在每一个column中进行编辑保存,例如:{ title:'标题', field:'title', width: ...
- JAVA基础系列:Object类
1. 万物皆对象 1. JVM在编译源代码时,在遇到没有继承Object的对象的时候,编译器会默认指定一个默认的父类Object 2. Object 和接口的关系,接口是否继承Object?接口没有继 ...