Day23:事务管理、显示评论、添加评论

事务管理

事务的定义

什么是事务

  • 事务是由N步数据库操作序列组成的逻辑执行单元,这系列操作要么全执行,要么全放弃执行。

事务的特性(ACID)

  • 原子性(Atomicity):事务是应用中不可再分的最小执行体(事务中部分执行失败就会回滚 。
  • 一致性(Consistency):事务执行的结果,须使数据从一个一致性状态,变为另一个一致性状态。
  • **隔离性(Isolation)😗*各个事务的执行互不干扰,任何事务的内部操作对其他的事务都是隔离的。
  • 持久性(Durability):事务一旦提交,对数据所做的任何改变都要记录到永久存储器中。

事务的隔离性

常见的并发异常

  • 第一类丢失更新、第二类丢失更新。
  • 脏读、不可重复读、幻读。

常见的隔离级别 (从低到高)

  • Read Uncommitted:读取未提交的数据。
  • Read Committed:读取已提交的数据。
  • Repeatable Read:可重复读。
  • Serializable:串行化。(可以解决所有的问题,但需要加锁降低数据库性能)

第一类丢失更新

image

(事务1的回滚导致事务2的数据更新失败)

第二类丢失更新

image

(事务1和事务2最终结果都是11,事务2不能接受)

脏读

image

(实际上事务2读到的11,实际上N已经是10了)

不可重复读

image

(事务2并没有对N变动,但先后结果不一样,查询单行数据导致不一致)

幻读

image

(查询多行数据导致不一致)

不用的处理方式对数据安全的影响

image

(一般中间两种比较适合)

数据库保证事务的实现机制

  • 悲观锁(数据库)
    • 共享锁(S锁):事务A对某数据加了共享锁后,其他事务只能对该数据加共享锁,但不能加排他锁。
    • 排他锁(X锁):事务A对某数据加了排他锁后,其他事务对该数据既不能加共享锁,也不能加排他锁。
  • 乐观锁(自定义)
    • 版本号、时间戳等
    • 在更新数据前,检查版本号是否发生变化。若变化则取消本次更新,否则就更新数据(版本号+1)。

Spring事务管理

  • 声明式事务(简单,常用的项目设置)
    • 通过XML配置,声明某方法的事务特征。
    • 通过注解,声明某方法的事务特征。
  • 编程式事务(适合数据库中很多操作,只需要控制部分操作)
    • 通过 TransactionTemplate 管理事务,并通过它执行数据库的操作。

示例

  • 需求:一个用户自动注册完自动发送帖子
  • 如果存在事务,整个会原子执行,报错后会回滚,也就是用户和帖子不会被创建在数据库中

声明式事务(常用,简单)

@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public Object save1(){User user = new User();user.setUsername("test");user.setSalt("abc");user.setPassword(CommunityUtil.md5("123" + user.getSalt()));user.setEmail("742uu12@qq.com");user.setHeaderUrl("http://www.nowcoder.com/101.png");user.setCreateTime(new Date());userMapper.insertUser(user);//发布帖子DiscussPost post = new DiscussPost();post.setUserId(user.getId());post.setTitle("hello");post.setContent("新人报道");post.setCreateTime(new Date());discussPostMapper.insertDiscussPost(post);Integer.valueOf("abc");return "ok";
}

//A调B,两者都有事务

//(REQUIRED):B支持当前事务(外部事务A),如果不存在则创建新事务

//(REQUIRES_NEW):B创建一个新事务,并且暂停当前事务(外部事务A)

//(NESTED):B如果当前存在事务(外部事务A),则嵌套在该事务中执行(有独立的提交和回滚),否则和REQUIRED一样

  • 使用Transactional注解,isolation规定策略,propagation规定传播方式;
  • 故意写一个报错的句子 Integer.valueOf(“abc”);

使用TransactionTemplate

public String save2(){transactionTemplate.setIsolationLevel(TransactionTemplate.ISOLATION_READ_COMMITTED);transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);String result = transactionTemplate.execute(new TransactionCallback<String>() {@Overridepublic String doInTransaction(org.springframework.transaction.TransactionStatus transactionStatus) {User user = new User();user.setUsername("test");user.setSalt("abc");user.setPassword(CommunityUtil.md5("123" + user.getSalt()));user.setEmail("742uu12@qq.com");user.setHeaderUrl("http://www.nowcoder.com/101.png");user.setCreateTime(new Date());userMapper.insertUser(user);//发布帖子DiscussPost post = new DiscussPost();post.setUserId(user.getId());post.setTitle("hello");post.setContent("新人报道");post.setCreateTime(new Date());discussPostMapper.insertDiscussPost(post);return "ok";}});return result;
}

显示评论

  • 数据层
    • 根据实体查询一页评论数据。
    • 根据实体查询评论的数量。
  • 业务层
    • 处理查询评论的业务。
    • 处理查询评论数量的业务。
  • 表现层
    • 显示帖子详情数据时,
    • 同时显示该帖子所有的评论数据。

数据层DAO

  1. 编写Comment实体类:
package com.newcoder.community.entity;public class Comment {int id;int userId;int entityType;int entityId;int targetId;String content;String status;String createTime;public int getId() {return id;}public void setId(int id) {this.id = id;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public int getEntityType() {return entityType;}public void setEntityType(int entityType) {this.entityType = entityType;}public int getEntityId() {return entityId;}public void setEntityId(int entityId) {this.entityId = entityId;}public int getTargetId() {return targetId;}public void setTargetId(int targetId) {this.targetId = targetId;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public String getCreateTime() {return createTime;}public void setCreateTime(String createTime) {this.createTime = createTime;}@Overridepublic String toString() {return "Comment{" +"id=" + id +", userId=" + userId +", entityType=" + entityType +", entityId=" + entityId +", targetId=" + targetId +", content='" + content + '\'' +", status='" + status + '\'' +", createTime='" + createTime + '\'' +'}';}
}
  1. 定义CommentMapper接口
@Mapper
public interface CommentMapper {List<Comment> selectCommentsByEntity(int entityType, int entityId, int offset,int limit);int selectCountByEntity(int entityType, int entityId);}
  1. 编写comment-mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.newcoder.community.dao.CommentMapper"><sql id="selectFields">id, user_id, entity_type, entity_id, target_id, content, status, create_time</sql><select id="selectCommentByEntity" resultType="Comment">select<include refid="selectFields"/>from commentwhere entity_type = #{entityType} and entity_id = #{entityId}order by create_time desc</select><select id="selectCommentCount" resultType="int">select count(id)from commentwhere entity_type = #{entityType}and entity_id = #{entityId}mapper ></select></mapper>

业务层

  1. 编写CommentService类
@Service
public class CommentService {@Autowiredprivate CommentMapper commentMapper;public List<Comment> findCommentsByEntity(int entityType, int entityId, int offset, int limit){return commentMapper.selectCommentsByEntity(entityType,entityId,offset,limit);}public int findCommentCount(int entityType, int entityId){return commentMapper.selectCountByEntity(entityType,entityId);}}

Controller层

修改之前的getDiscussPost函数:

@RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)public String getDiscussPost(@PathVariable(name = "discussPostId") int discussPostId, Model model, Page page) {DiscussPost post = discussPostService.findDiscussPostById(discussPostId);model.addAttribute("post", post);//帖子的作者User user = userService.findUserById(post.getUserId());model.addAttribute("user", user);//评论分页信息page.setLimit(5);page.setPath("/discuss/detail/" + discussPostId);page.setRows(post.getCommentCount());//直接从帖子中取List<Comment> commentList = commentService.findCommentsByEntity(ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());//遍历集合,将每个评论的其他信息查出来(这里嵌套是难点,之后可以在面试上说)List<Map<String, Object>> commentVoList = new ArrayList<>();if(commentList != null){for(Comment comment : commentList){//评论VoMap<String, Object> commentVo = new java.util.HashMap<>();//评论commentVo.put("comment",comment);//作者commentVo.put("user",userService.findUserById(comment.getUserId()));//回复列表(评论的评论)List<Comment> replyList = commentService.findCommentsByEntity(ENTITY_TYPE_COMMENT,comment.getId(),0,Integer.MAX_VALUE);List<Map<String,Object>> replyVoList = new ArrayList<>();if(replyList != null){for(Comment reply : replyList){Map<String,Object> replyVo = new java.util.HashMap<>();//回复replyVo.put("reply",reply);//作者replyVo.put("user",userService.findUserById(reply.getUserId()));//回复目标User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());replyVo.put("target",target);replyVoList.add(replyVo);}}commentVo.put("replys",replyVoList);//回复数量int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT,comment.getId());commentVo.put("replyCount",replyCount);commentVoList.add(commentVo);}}//将评论Vo列表传给前端model.addAttribute("comments",commentVoList);return "/site/discuss-detail";}

方法有点长,从14行开始,首先设置分页信息(只有评论分页,评论的评论不分页)

然后查询所有评论,接着查询评论的评论,都加入hashmap中。

修改index.html

image

修改discuss-detail.html

这里太复杂了,直接把html附上:

注意这里的分页可以复用首页的分页逻辑。

  • 评论显示分页:复用index.html中的th:fragment=“pagination”
<nav class="mt-5" th:replace="index::pagination"><ul class="pagination justify-content-center"><li class="page-item"><a class="page-link" href="#">首页</a></li><li class="page-item disabled"><a class="page-link" href="#">上一页</a></li><li class="page-item active"><a class="page-link" href="#">1</a></li><li class="page-item"><a class="page-link" href="#">2</a></li><li class="page-item"><a class="page-link" href="#">3</a></li><li class="page-item"><a class="page-link" href="#">4</a></li><li class="page-item"><a class="page-link" href="#">5</a></li><li class="page-item"><a class="page-link" href="#">下一页</a></li><li class="page-item"><a class="page-link" href="#">末页</a></li></ul></nav>		

添加评论

数据层DAO

  1. 在CommentMapper中添加insert帖子接口:
    int insertComment(Comment comment);
  • 返回值为什么是int:

在MyBatis中,insert方法通常返回一个int类型的值,这个值表示的是插入操作影响的行数。如果插入成功,这个值应该是1(因为插入一条数据影响一行);如果插入失败,这个值可能是0(没有行被影响)。这样,开发者可以通过检查这个返回值来判断插入操作是否成功。

  1. 修改comment-Mapper,修改sql语句:
 <sql id="insertFields">user_id, entity_type, entity_id, target_id, content, status, create_time</sql><insert id="insertComment" parameterType="Comment">insert into comment (<include refid="insertFields"></include>)values (#{userId}, #{entityType}, #{entityId}, #{targetId}, #{content}, #{status}, #{createTime})</insert>
  1. 修改postmapper更新评论数量
int updateCommentCount(int id, int commentCount);
  1. 修改postmapper填写sql
    <update id="updateCommentCount">update discuss_postset comment_count = #{commentCount}where id = #{id}</update>

业务层

  1. DiscussPostService:
public int updateCommentCount(int id, int commentCount) {return discussPostMapper.updateCommentCount(id, commentCount);}
  1. CommentService(引入事务管理,重点!!!)
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
public int addComment(Comment comment){if(comment == null){throw new IllegalArgumentException("参数不能为空");}//转义HTML标记和过滤敏感词comment.setContent(HtmlUtils.htmlEscape(comment.getContent()));comment.setContent(sensitiveFilter.filter(comment.getContent()));int rows = commentMapper.insertComment(comment);//更新帖子评论数量(过滤楼中楼)if(comment.getEntityType() == ENTITY_TYPE_POST){int count = commentMapper.selectCountByEntity(comment.getEntityType(),comment.getEntityId());discussPostService.updateCommentCount(comment.getEntityId(), count);}return rows;
}

过滤敏感词、识别是帖子的评论而不是楼中楼,更新评论;

Controller层

添加一个新的CommentController:

@Controller
@RequestMapping("/comment")
public class CommentController {@Autowiredprivate CommentService commentService;@Autowiredprivate HostHolder hostHolder;@RequestMapping(path="add/{discussPostId}",method = RequestMethod.POST)public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment, Model model) {comment.setUserId(hostHolder.getUser().getId());comment.setStatus(0);comment.setCreateTime(new Date());commentService.addComment(comment);return "redirect:/discuss/detail/" + discussPostId;}
}
  • 想要重定向回原页面,故用@PathVariable取id好拼接url。

修改模板

修改的是site/discuss-post.html

  1. 修改评论输入框
<div class="container mt-3"><form class="replyform" method="post" th:action="@{|/comment/add/${post.id}|}"><p class="mt-3"><a name="replyform"></a><textarea placeholder="在这里畅所欲言你的看法吧!" name="content"></textarea><input type="hidden" name="entityType" value="1"/><input type="hidden" name="entityId" th:value="${post.id}"/></p><p class="text-right"><button type="submit" class="btn btn-primary btn-sm">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</button></p></form>
</div>

在您的CommentController中,您使用了Comment对象来接收表单提交的数据。Spring MVC会自动将请求参数绑定到Comment对象的属性上,这是通过参数名和Comment对象属性名的匹配来实现的。因此,content表单元素的值会被自动绑定到Comment对象的content属性上。

image

  1. 修改楼中楼输入框:(就是回复评论的框)
<li class="pb-3 pt-3"><form method="post" th:action="@{|/comment/add/${post.id}|}"><div><input type="text" class="input-size" name="content" placeholder="请输入你的观点"/><input type="hidden" name="entityType" value="2"/><input type="hidden" name="entityId" th:value="${cvo.comment.id}"/></div><div class="text-right mt-2"><button type="button" class="btn btn-primary btn-sm" onclick="#">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</button></div></form>
</li>

image

  1. 修改楼中楼中楼的框(就是回复评论的评论的框)
<div th:id="|huifu-${rvoStat.count}|" class="mt-4 collapse"><form method="post" th:action="@{|/comment/add/${post.id}|}"><div><input type="text" class="input-size" name = "content" th:placeholder="|回复${rvo.user.username}|"/><input type="hidden" name="entityType" value="2"/><input type="hidden" name="entityId" th:value="${cvo.comment.id}"/><input type="hidden" name="targetId" th:value="${rvo.user.id}"/></div><div class="text-right mt-2"><button type="submit" class="btn btn-primary btn-sm" onclick="#">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</button></div></form>
</div>

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/572518.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

教程2_图像的合并及融合

1、图像加法 您可以通过OpenCV函数 cv.add() 或仅通过 numpy 操作 res img1 img2 添加两个图像。两个图像应具有相同的深度和类型&#xff0c;或者第二个图像可以只是一个标量值。 注意: OpenCV加法和Numpy加法之间有区别。OpenCV加法是饱和运算&#xff0c;而Numpy加法是模运…

Multimodal Chain-of-Thought Reasoning in Language Models阅读笔记

论文&#xff08;2023年&#xff09;链接&#xff1a;https://arxiv.org/pdf/2302.00923.pdf GitHub项目链接&#xff1a;GitHub - amazon-science/mm-cot: Official implementation for "Multimodal Chain-of-Thought Reasoning in Language Models" (stay tuned a…

XSS学习(cookie远程登录演示)

1.HTTP特点&#xff1a; 1.请求应答模式。 2.灵活可扩展 3.可靠传输 4.无状态。 这里给大家举一个例子&#xff1a; HTTP是无状态的&#xff0c;所按理来说我每进行一次会话&#xff0c;比如我在CSDN发一个帖子&#xff0c;好像按理来以说我都要进行一次重新登陆&#xff0…

javaSwing坦克大战游戏

在游戏开发领域&#xff0c;坦克大战是一款经典的游戏&#xff0c;其简单而又耐玩的玩法吸引了无数玩家。而今&#xff0c;在Java编程技术的支持下&#xff0c;我们可以用Java Swing技术轻松实现这款经典游戏。本文将介绍如何使用Java Swing技术编写坦克大战游戏&#xff0c;并…

[flask]执行上下文的四个全局变量

flask上下文全局变量&#xff0c;程序上下文、请求上下文、上下文钩子 -- - 夏晓旭 - 博客园 (cnblogs.com) 执行上下文 执行上下文&#xff1a;即语境&#xff0c;语意&#xff0c;在程序中可以理解为在代码执行到某一行时&#xff0c;根据之前代码所做的操作以及下文即将要…

2024年腾讯云4核8G服务器多少钱一年?买1年送3个月

2024年腾讯云4核8G服务器租用优惠价格&#xff1a;轻量应用服务器4核8G12M带宽646元15个月&#xff0c;CVM云服务器S5实例优惠价格1437.24元买一年送3个月&#xff0c;腾讯云4核8G服务器活动页面 txybk.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器优惠价格 轻…

华为数通 HCIP-Datacom H12-831 题库补充(3/27)

2024年 HCIP-Datacom&#xff08;H12-831&#xff09;最新题库&#xff0c;完整题库请扫描上方二维码&#xff0c;持续更新。 如图所示&#xff0c;关于R4路由器通过IS-IS计算出来的IPv6路由&#xff0c;哪一选项的描述是错误的&#xff1f; A&#xff1a;R4通过IS—IS只学习到…

取消springboot中的Test类中的日志打印

使用SpringBootTest注解进行单元测试时, 打印东西的时候,总会伴随很多的无关紧要的日志信息&#xff0c;影响观感。去掉这些日志应该怎么做呢 ? 两个步骤: 第一步、修改application.properties logging.level.rootoff logging.level.org.springframeworkoff # 关闭baner信息…

SpringMvc执行流程源码解析

一、简介 Spring web Mvc是基于ServletApi构建的原始Web模块&#xff0c;从一开始就包含在Spring框架中&#xff1b; 从Servlet到SpringMvc 最典型的MVc就是JSPServletjavaBean的模式&#xff1b; 弊端&#xff1a; 1、xml下配置Servlet的映射非常麻烦&#xff0c;效率低&…

vue3+threejs新手从零开发卡牌游戏(十三):上场手牌添加攻击力文字

在utils/common.ts下新建渲染场上手牌文字方法&#xff1a; // 渲染场上手牌文字 const renderSiteCardText (mesh: any, font: any) > {return new Promise((resolve, reject) > {let pos mesh.positionconst geometry new TextGeometry( ATK ${mesh.userData._ATK}…

Windows直接运行python程序

Windows直接运行python程序 一、新建bat脚本二、新建vbs脚本 一、新建bat脚本 新建bat批处理脚本&#xff0c;写入以下内容 echo off call conda activate pytorch python app.pyecho off&#xff1a;在此语句后所有运行的命令都不显示命令行本身&#xff0c;但是本身的指令是…

ETIM -国际贸易的产品分类标准

ETIM 是除了XML 国际交流标准BMEcat之外的国际贸易的产品分类标准。 什么是ETIM &#xff1f; ETIM是一种基于分类识别共享和交换产品数据的格式。这种广泛使用的技术产品分类标准是为了构建 B2B 专业人员之间的信息流而制定的。 为什么选择ETIM&#xff1f; ETIM分类模型的开…