磁力链接 网站怎么做的,wordpress主题--ux,东莞哪里有做网站的,wordpress关注公众号可见事务管理
事务的定义
什么是事务
事务是由N步数据库操作序列组成的逻辑执行单元#xff0c;这系列操作要么全执行#xff0c;要么全放弃执行。
事务的特性(ACID)
原子性(Atomicity):事务是应用中不可再分的最小执行体#xff08;事务中部分执行失败就会回滚 。一致性(C…事务管理
事务的定义
什么是事务
事务是由N步数据库操作序列组成的逻辑执行单元这系列操作要么全执行要么全放弃执行。
事务的特性(ACID)
原子性(Atomicity):事务是应用中不可再分的最小执行体事务中部分执行失败就会回滚 。一致性(Consistency):事务执行的结果须使数据从一个一致性状态变为另一个一致性状态。**隔离性(Isolation)*各个事务的执行互不干扰任何事务的内部操作对其他的事务都是隔离的。持久性(Durability):事务一旦提交对数据所做的任何改变都要记录到永久存储器中。
事务的隔离性
常见的并发异常
第一类丢失更新、第二类丢失更新。脏读、不可重复读、幻读。
常见的隔离级别 从低到高
Read Uncommitted读取未提交的数据。Read Committed读取已提交的数据。Repeatable Read可重复读。Serializable串行化。可以解决所有的问题但需要加锁降低数据库性能
第一类丢失更新 事务1的回滚导致事务2的数据更新失败
第二类丢失更新 事务1和事务2最终结果都是11事务2不能接受
脏读 实际上事务2读到的11实际上N已经是10了
不可重复读 事务2并没有对N变动但先后结果不一样查询单行数据导致不一致
幻读 查询多行数据导致不一致
不用的处理方式对数据安全的影响 一般中间两种比较适合
数据库保证事务的实现机制
悲观锁(数据库) 共享锁(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(742uu12qq.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 TransactionCallbackString() {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(742uu12qq.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
编写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 \ };}
}
定义CommentMapper接口
Mapper
public interface CommentMapper {ListComment selectCommentsByEntity(int entityType, int entityId, int offset,int limit);int selectCountByEntity(int entityType, int entityId);}编写comment-mapper.xml文件
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.newcoder.community.dao.CommentMappersql idselectFieldsid, user_id, entity_type, entity_id, target_id, content, status, create_time/sqlselect idselectCommentByEntity resultTypeCommentselectinclude refidselectFields/from commentwhere entity_type #{entityType} and entity_id #{entityId}order by create_time desc/selectselect idselectCommentCount resultTypeintselect count(id)from commentwhere entity_type #{entityType}and entity_id #{entityId}mapper /select/mapper业务层
编写CommentService类
Service
public class CommentService {Autowiredprivate CommentMapper commentMapper;public ListComment 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());//直接从帖子中取ListComment commentList commentService.findCommentsByEntity(ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());//遍历集合将每个评论的其他信息查出来这里嵌套是难点之后可以在面试上说ListMapString, Object commentVoList new ArrayList();if(commentList ! null){for(Comment comment : commentList){//评论VoMapString, Object commentVo new java.util.HashMap();//评论commentVo.put(comment,comment);//作者commentVo.put(user,userService.findUserById(comment.getUserId()));//回复列表评论的评论ListComment replyList commentService.findCommentsByEntity(ENTITY_TYPE_COMMENT,comment.getId(),0,Integer.MAX_VALUE);ListMapString,Object replyVoList new ArrayList();if(replyList ! null){for(Comment reply : replyList){MapString,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 修改discuss-detail.html
这里太复杂了直接把html附上
注意这里的分页可以复用首页的分页逻辑。
评论显示分页复用index.html中的th:fragment“pagination”
nav classmt-5 th:replaceindex::paginationul classpagination justify-content-centerli classpage-itema classpage-link href#首页/a/lili classpage-item disableda classpage-link href#上一页/a/lili classpage-item activea classpage-link href#1/a/lili classpage-itema classpage-link href#2/a/lili classpage-itema classpage-link href#3/a/lili classpage-itema classpage-link href#4/a/lili classpage-itema classpage-link href#5/a/lili classpage-itema classpage-link href#下一页/a/lili classpage-itema classpage-link href#末页/a/li/ul/nav 添加评论
数据层DAO
在CommentMapper中添加insert帖子接口 int insertComment(Comment comment);返回值为什么是int 在MyBatis中insert方法通常返回一个int类型的值这个值表示的是插入操作影响的行数。如果插入成功这个值应该是1因为插入一条数据影响一行如果插入失败这个值可能是0没有行被影响。这样开发者可以通过检查这个返回值来判断插入操作是否成功。 修改comment-Mapper修改sql语句 sql idinsertFieldsuser_id, entity_type, entity_id, target_id, content, status, create_time/sqlinsert idinsertComment parameterTypeCommentinsert into comment (include refidinsertFields/include)values (#{userId}, #{entityType}, #{entityId}, #{targetId}, #{content}, #{status}, #{createTime})/insert
修改postmapper更新评论数量
int updateCommentCount(int id, int commentCount);修改postmapper填写sql update idupdateCommentCountupdate discuss_postset comment_count #{commentCount}where id #{id}/update业务层
DiscussPostService:
public int updateCommentCount(int id, int commentCount) {return discussPostMapper.updateCommentCount(id, commentCount);}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(pathadd/{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
修改评论输入框
div classcontainer mt-3form classreplyform methodpost th:action{|/comment/add/${post.id}|}p classmt-3a namereplyform/atextarea placeholder在这里畅所欲言你的看法吧! namecontent/textareainput typehidden nameentityType value1/input typehidden nameentityId th:value${post.id}//pp classtext-rightbutton typesubmit classbtn btn-primary btn-smnbsp;nbsp;回nbsp;nbsp;帖nbsp;nbsp;/button/p/form
/div在您的CommentController中您使用了Comment对象来接收表单提交的数据。Spring MVC会自动将请求参数绑定到Comment对象的属性上这是通过参数名和Comment对象属性名的匹配来实现的。因此content表单元素的值会被自动绑定到Comment对象的content属性上。 修改楼中楼输入框(就是回复评论的框
li classpb-3 pt-3form methodpost th:action{|/comment/add/${post.id}|}divinput typetext classinput-size namecontent placeholder请输入你的观点/input typehidden nameentityType value2/input typehidden nameentityId th:value${cvo.comment.id}//divdiv classtext-right mt-2button typebutton classbtn btn-primary btn-sm onclick#nbsp;nbsp;回nbsp;nbsp;复nbsp;nbsp;/button/div/form
/li修改楼中楼中楼的框就是回复评论的评论的框
div th:id|huifu-${rvoStat.count}| classmt-4 collapseform methodpost th:action{|/comment/add/${post.id}|}divinput typetext classinput-size name content th:placeholder|回复${rvo.user.username}|/input typehidden nameentityType value2/input typehidden nameentityId th:value${cvo.comment.id}/input typehidden nametargetId th:value${rvo.user.id}//divdiv classtext-right mt-2button typesubmit classbtn btn-primary btn-sm onclick#nbsp;nbsp;回nbsp;nbsp;复nbsp;nbsp;/button/div/form
/div