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

news2024/12/24 22:01:15

事务管理

事务的定义

什么是事务

  • 事务是由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>() {
        @Override
        public 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;
    }

    @Override
    public 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 mapper
        PUBLIC "-//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 comment
        where entity_type = #{entityType} and entity_id = #{entityId}
        order by create_time desc
    </select>

    <select id="selectCommentCount" resultType="int">
        select count(id)
        from comment
        where entity_type = #{entityType}
          and entity_id = #{entityId}mapper >
    </select>

</mapper>

业务层

  1. 编写CommentService类
@Service
public class CommentService {
    @Autowired
    private 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){
                //评论Vo
                Map<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_post
        set 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 {
    @Autowired
    private CommentService commentService;

    @Autowired
    private 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.coloradmin.cn/o/1549795.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源大数据集群部署(十八)Hive 安装部署

作者&#xff1a;櫰木 1 创建hive Kerberos主体 bash /root/bigdata/getkeytabs.sh /etc/security/keytab/hive.keytab hive2 安装 在hd1.dtstack.com主机root权限下操作&#xff1a; 解压包 [roothd3.dtstack.com software]# tar -zxvf apache-hive-3.1.2-bin.tar.gz -C …

树与二叉树的应用试题解析

01&#xff0e;在有n个叶结点的哈夫曼树中&#xff0c;非叶结点的总数是( A ). A. n-1 B. n C. 2n-1 D.2n 02.给定整数集合{3,5,6,9,12}&#xff0c;与之对应的哈夫曼树是( D…

设计模式——行为型——策略模式Strategy

Q&#xff1a;策略模式的特点 A&#xff1a; 具体算法从具体的业务方法中独立出来策略模式是同行为的不同实现 Q&#xff1a;什么时候使用策略模式 A&#xff1a;多个if-else使用策略模式 收费对象类 public class CashContext {private CashStrategy cashStrategy;public…

iphoneX系统的参数

1. 2. 3. 4. 5.相关的网址信息 Apple iPhone X 規格、价格和评论 | Kalvo Apple iPhone X 規格、价格和评论 | Kalvo

Apache Hive的基本使用语法

一、数据库操作 创建数据库 create database if not exists myhive;查看数据库 use myhive; desc database myhive;创建数据库并指定hdfs存储 create database myhive2 location /myhive2;删除空数据库&#xff08;如果有表会报错&#xff09; drop database myhive;…

【二叉树】Leetcode 102. 二叉树的层序遍历【中等】

二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09; 示例1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 解题思路…

LabVIEW无人机大气数据智能测试系统

LabVIEW无人机大气数据智能测试系统 随着无人机技术的迅速发展&#xff0c;大气数据计算机作为重要的机载设备&#xff0c;在确保飞行安全性方面发挥着重要作用。设计了一套基于LabVIEW的无人机大气数据智能测试系统&#xff0c;通过高效、稳定的性能测试&#xff0c;及时发现…

Chakra UI:重塑React组件开发的未来

随着前端开发技术的不断演进&#xff0c;React已经成为了一个不可或缺的开源JavaScript库&#xff0c;用于构建用户界面。然而&#xff0c;虽然React提供了构建用户界面的强大工具&#xff0c;但在组件的可访问性、可重复使用性和可组合性方面仍存在挑战。Chakra UI正是一个为解…

数据处理库Pandas的数据结构Series

Series是一种一维数据结构&#xff0c;每个元素都带有一个索引&#xff0c;与一维数组的含义相似&#xff0c;其中索引可以为数字或字符串&#xff0c;如图3-1所示。 Series 对象包含两个主要的属性&#xff1a;index 和 values&#xff0c;分别为上例中的左右两列。因为传给构…

春秋云境CVE-2022-23880

简介 taoCMS v3.0.2 文件管理处存在任意文件上传漏洞&#xff0c;攻击者可执行任意代码 正文 首先进入靶场&#xff0c;我们发现在首页底部有个管理界面&#xff0c;那么就直接点进去进入后台。 找到后台 找到后台&#xff0c;尝试弱口令&#xff0c;发现不成功&#xff0c…

Zookeeper的系统架构

先看一张图&#xff1a; ZooKeeper 的架构图中我们需要了解和掌握的主要有&#xff1a; 1&#xff1a; ZooKeeper分为服务器端&#xff08;Server&#xff09; 和客户端&#xff08;Client&#xff09;&#xff0c;客户端可以连接到整个ZooKeeper服务的任意服务器上&#xff…

Jenkins安装配置部署

Jenkins安装配置部署 一、什么是CI/CD 1.CI(Continuous integration&#xff09; 中文意思是持续集成)是一种软件开发时间。持续集成强调开发人员提交了 新代码之后&#xff0c;立刻进行构建、&#xff08;单元&#xff09;测试。根据测试结果&#xff0c;我们可以确定新代码…

第3章:角色提示,强化Chatgpt输出新篇章!

角色提示技术 角色提示技术&#xff08;role prompting technique&#xff09;&#xff0c;是通过模型扮演特定角色来产出文本的一种方法。用户为模型设定一个明确的角色&#xff0c;它就能更精准地生成符合特定上下文或听众需求的内容。 比如&#xff0c;想生成客户服务的回复…

未来制造:机器人行业新质生产力提升策略

机器人行业新质生产力提升咨询方案 一、机器人行业目前发展现状及特点&#xff1a; 创新活跃、应用广泛、成长性强。 二、机器人企业发展新质生产力面临的痛点&#xff1a; 1、高端人才匮乏 2、核心技术受限 3、竞争日益国际化 4、成本控制挑战 5、用户体验提升需求 三…

Flink on Kubernetes (flink-operator) 部署Flink

flink on k8s 官网 https://nightlies.apache.org/flink/flink-kubernetes-operator-docs-release-1.1/docs/try-flink-kubernetes-operator/quick-start/ 我的部署脚本和官网不一样&#xff0c;有些地方官网不够详细 部署k8s集群 注意&#xff0c;按照默认配置至少有两台wo…

【C++】STL 标准模板库

前言 在前一章种我们介绍了C中的模板的使用&#xff0c;这是一种泛型编程&#xff0c;模板的使用能让我们减少大量的相似代码&#xff0c;减少我们的代码量与工作量&#xff0c;写出更加高效简洁的代码&#xff0c;模板如此好用&#xff0c;但还是要我们先出写一个泛型类或函数…

前端web移动端学习day04

移动 Web 第四天 01-vw适配方案 vw和vh基本使用 vw和vh是相对单位&#xff0c;相对视口尺寸计算结果&#xff0c;相对于屏幕的逻辑参数 vw&#xff1a;viewport width&#xff08;1vw 1/100视口宽度 &#xff09;vh&#xff1a;lviewport height ( 1vh 1/100视口高度 ) …

Stardew Valley(到达同一高度最少操作数)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例&#xff1a; 输入 5 1 2 3 1 2 输出 2 思路&#xff1a; 根据题意&#xff0c;要求选取一段区间 1 &#xff0c;使得序列单调递增。求最少操作数。 我们选取区间 1 是为了不超过前面的最大…

软考数据库

目录 分值分布1. 事务管理1.1 事物的基本概念1.2 数据库的并发控制1.2.1 事务调度概念1.2.2 并发操作带来的问题1.2.3 并发控制技术1.2.4 隔离级别&#xff1a; 1.3 数据库的备份和恢复1.3.1 故障种类1.3.2 备份方法1.3.3 日志文件1.3.4 恢复 SQL语言 分值分布 1. 事务管理 1.…

华为CLI实验-配置旁路检测时的安全策略

CLI举例&#xff1a;配置旁路检测时的安全策略 举例说明当FW作为旁路检测设备时&#xff0c;如何配置安全策略。 组网需求 如图1所示&#xff0c;企业内网通过路由器Router连接到Internet。FW作为旁路检测设备&#xff0c;对通过Router的流量进行内容安全检测。 图1 旁路检测…