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 */;

项目已经上传到Github,欢迎大家克隆学习。
项目地址:https://github.com/Dodozhou/greate_test
(若没有Github账户的同学,可以在评论区留言你的邮箱,我会将zip压缩包发到你的邮箱)(喜欢的请别忘了点赞哟,这是对我莫大的鼓励:-D)

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

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

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

  • 72
    点赞
  • 144
    收藏
    觉得还不错? 一键收藏
  • 132
    评论
实现 JavaWeb 评论功能的步骤如下: 1. 创建数据库表,包括评论内容、评论时间、评论人等字段。 2. 在页面中添加评论输入框和提交按钮。 3. 在后台处理程序中,获取提交的评论内容和评论人信息。 4. 将评论内容和评论人信息插入到数据库表中。 5. 在页面中显示最新的评论内容。 下面是一个简单的 JavaWeb 评论功能代码示例,仅供参考: 1. 创建数据库表 ``` CREATE TABLE comment ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(1000), author VARCHAR(50), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` 2. 添加评论输入框和提交按钮 ```html <form action="comment.do" method="post"> <textarea name="content" rows="3" cols="50"></textarea><br> <input type="text" name="author"><br> <input type="submit" value="提交"> </form> ``` 3. 处理程序中获取提交的评论信息并插入到数据库表中 ```java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String content = request.getParameter("content"); String author = request.getParameter("author"); // 将评论内容和评论人信息插入到数据库表中 Connection conn = null; PreparedStatement stmt = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password"); String sql = "INSERT INTO comment (content, author) VALUES (?, ?)"; stmt = conn.prepareStatement(sql); stmt.setString(1, content); stmt.setString(2, author); stmt.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } // 重定向到评论页面(实现页面刷新) response.sendRedirect("comment.jsp"); } ``` 4. 在页面中显示最新的评论内容 ```java <% Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password"); String sql = "SELECT * FROM comment ORDER BY create_time DESC LIMIT 10"; stmt = conn.prepareStatement(sql); rs = stmt.executeQuery(); while (rs.next()) { out.println("<p>" + rs.getString("content") + " - " + rs.getString("author") + "</p>"); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } %> ``` 以上是一个简单的 JavaWeb 评论功能实现示例,仅供参考。实际情况中还需要考虑安全性、性能等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值