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