什么是事务
事务的特性(ACID)
常见的并发异常
常见的隔离级别
Read Uncommitted
:读取未提交的数据。Read Committed
:读取已提交的数据。Repeatable Read
:可重复读。Serializable
:串行化。
悲观锁(数据库)
乐观锁(自定义)
声明式事务
编程式事务
数据层
业务层
表现层
在 CommunityConstant
接口中添加两个字段:帖子和评论,代码如下:
public interface CommunityConstant {
/**
* 激活成功
*/
int ACTIVATION_SUCCESS = 0;
/**
* 重复激活
*/
int ACTIVATION_REPEAT = 1;
/**
* 激活失败
*/
int ACTIVATION_FAILURE = 2;
/**
* 默认状态的登录凭证的超时时间
*/
int DEFAULT_EXPIRED_SECONDS = 3600 * 12;
/**
* 记住状态的登录凭证超时时间
*/
int REMEMBER_EXPIRED_SECONDS = 3600 * 24 * 100;
/**
* 实体类型: 帖子
*/
int ENTITY_TYPE_POST = 1;
/**
* 实体类型: 评论
*/
int ENTITY_TYPE_COMMENT = 2;
}
在 util
包下创建 Comment
类,与数据库表 Comment
中的字段互相对应,代码如下:
public class Comment {
private int id;
private int userId; // 评论人的id
private int entityType; // 针对谁进行评论 1:帖子 2:评论
private int entityId; // 评论对象的id 针对帖子的评论:帖子的id 针对评论的评论:评论的id
private int targetId; // a回复b,针对评论的评论,评论对象b的user_id,方便前端快速展示b的用户名
private String content;
private int status;
private Date 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 int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date 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 +
'}';
}
}
在 dao
包先创建 CommentMapper
类,代码如下:
@Mapper
public interface CommentMapper {
List selectCommentsByEntity(int entityType, int entityId, int offset, int limit);
int selectCountByEntity(int entityType, int entityId);
}
对应的 comment-mapper.xml
代码如下:
id, user_id, entity_type, entity_id, target_id, content, status, create_time
在 service
包下创建 CommentService
类,代码如下:
@Service
public class CommentService {
@Autowired
private CommentMapper commentMapper;
public List 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);
}
}
在原有的 DiscussPostController
类中添加 getDiscussPost
方法,代码如下:
@Controller
@RequestMapping("/discuss")
public class DiscussPostController implements CommunityConstant {
@Autowired
private DiscussPostService discussPostService;
@Autowired
private HostHolder hostHolder;
@Autowired
private UserService userService;
@Autowired
private CommentService commentService;
@RequestMapping(path = "/add", method = RequestMethod.POST)
@ResponseBody
public String addDiscussPost(String title, String content) {
User user = hostHolder.getUser();
if (user == null) {
return CommunityUtil.getJSONString(403,"你还没有登录哦!");
}
DiscussPost post = new DiscussPost();
post.setUserId(user.getId());
post.setTitle(title);
post.setContent(content);
post.setCreateTime(new Date());
discussPostService.addDiscussPost(post);
// 报错的情况,会统一处理
return CommunityUtil.getJSONString(0, "发布成功!");
}
@RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
public String getDiscussPost(@PathVariable("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 commentList = commentService.findCommentsByEntity(
ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());
// 评论VO列表
List
index.html
:
- 赞 11
li class="d-inline ml-2">|
discuss-detail.html
:
由于帖子详情页面的修改比较混杂,将在代码整合的时候展示这部分修改的部分。
数据层
业务层
表现层
在 CommentMapper
类中添加 insertComment
方法,用于增加评论数据,代码如下:
@Mapper
public interface CommentMapper {
List selectCommentsByEntity(int entityType, int entityId, int offset, int limit);
int selectCountByEntity(int entityType, int entityId);
int insertComment(Comment comment);
}
对应的 comment-mapper.xml
代码如下:
id, user_id, entity_type, entity_id, target_id, content, status, create_time
user_id, entity_type, entity_id, target_id, content, status, create_time
insert into comment( )
values(#{userId},#{entityType},#{entityId},#{targetId},#{content},#{status},#{createTime})
在 DiscussPostMapper
类中添加 updateCommentCount
方法,用于修改帖子的评论数量,代码如下:
@Mapper
public interface DiscussPostMapper {
List selectDiscussPosts(int userId, int offset, int limit);
// @Param注解用于给参数取别名
// 如果只有一个参数,并且在里使用,则必须加别名
// 根据userId查询帖子数量
int selectDiscussPostRows(@Param("userId") int userId);
int insertDiscussPost(DiscussPost discussPost);
DiscussPost selectDiscussPostById(int id);
int updateCommentCount(int id, int commentCount);
}
对应的 discusspost-mapper.xml
代码如下:
id, user_id, title, content, type, status, create_time, comment_count, score
user_id, title, content, type, status, create_time, comment_count, score
insert into discuss_post( )
values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
update discuss_post set comment_count = #{commentCount} where id = #{id}
在 CommentService
类中添加 addComment
方法,此方法用到了事务注解,涉及到两张表的操作,代码如下:
@Service
public class CommentService implements CommunityConstant {
@Autowired
private CommentMapper commentMapper;
@Autowired
private SensitiveFilter sensitiveFilter;
@Autowired
private DiscussPostService discussPostService;
public List 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);
}
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public int addComment(Comment comment) {
if (comment == null) {
throw new IllegalArgumentException("参数不能为空!");
}
// 添加评论
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;
}
在 DiscussPostService
中添加 updateCommentCoun
方法,代码如下:
@Service
public class DiscussPostService {
@Autowired
private DiscussPostMapper discussPostMapper;
@Autowired
private SensitiveFilter sensitiveFilter;
public List findDiscussPosts(int userId, int offset, int limit) {
return discussPostMapper.selectDiscussPosts(userId, offset, limit);
}
public int findDiscussPostRows(int userId) {
return discussPostMapper.selectDiscussPostRows(userId);
}
public int addDiscussPost(DiscussPost post) {
if (post == null) {
throw new IllegalArgumentException("参数不能为空!");
}
// 转义 HTML 标记
post.setTitle(HtmlUtils.htmlEscape(post.getTitle()));
post.setContent(HtmlUtils.htmlEscape(post.getContent()));
// 过滤敏感词
post.setTitle(sensitiveFilter.filter(post.getTitle()));
post.setContent(sensitiveFilter.filter(post.getContent()));
return discussPostMapper.insertDiscussPost(post);
}
public DiscussPost findDiscussPostById(int id) {
return discussPostMapper.selectDiscussPostById(id);
}
public int updateCommentCount(int id, int commentCount) {
return discussPostMapper.updateCommentCount(id, commentCount);
}
创建 CommentController
类,代码如下:
@RequestMapping("/comment")
public class CommentController {
@Autowired
private CommentService commentService;
@Autowired
private HostHolder hostHolder;
@RequestMapping(value = "/add/{discussPostId}", method = RequestMethod.POST)
public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) {
comment.setUserId(hostHolder.getUser().getId());
comment.setStatus(0);
comment.setCreateTime(new Date());
commentService.addComment(comment);
return "redirect:/discuss/detail/" + discussPostId;
}
}