1、社区首页
1.1、功能分析
社区首页显示前10个帖子、支持内容分页、显示登录注册按钮。
1.2、功能实现
开发思路按照数据库-Dao-Service-Controller思路,首先新建数据库community,新建用户表user和帖子表discusspost;
前端页面展示采用thymeleaf模版引擎,实现将逻辑注入模板文件。具体sql文件和前端文件详见文章底部:源代码;
在community目录下创建entity包和dao包,entity包下编写User类和DiscussPost类,Page类用于控制页面分页显示。
@Data
public class User {
/**用户ID*/
private int id;
/**用户名称*/
private String username;
/**用户密码*/
private String password;
/**密码加盐*/
private String salt;
/**用户邮箱*/
private String email;
/**用户类型*/
private int type;
/**用户状态*/
private int status;
/**激活码*/
private String activationCode;
/**账号创建时间*/
private Date createTime;
/**用户头像路径ß*/
private String headerUrl;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
", email='" + email + '\'' +
", type=" + type +
", status=" + status +
", activationCode='" + activationCode + '\'' +
", createTime=" + createTime +
", headerUrl='" + headerUrl + '\'' +
'}';
}
}
@Data
public class DiscussPost {
/**评论ID*/
private int id;
/**用户id*/
private int userId;
/**评论ID*/
private String title;
/**评论内容*/
private String content;
/**评论类型*/
private int type;
/**评论状态*/
private int status;
/**评论创建时间*/
private Date createTime;
/**评论分数*/
private double score;
/**评论数*/
private int commentCount;
@Override
public String toString() {
return "DiscussPost{" +
"id=" + id +
", userId=" + userId +
", title='" + title + '\'' +
", content='" + content + '\'' +
", type=" + type +
", status=" + status +
", createTime=" + createTime +
", score=" + score +
", commentCount=" + commentCount +
'}';
}
}
public class Page {
/**
* 当前页码
*/
private int current = 1;
/**
* 显示上限
*/
private int limit = 10;
/**
* 数据总数(用于计算总页数)
*/
private int rows;
/**
* 查询路径(用于复用分页链接)
*/
private String path;
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
if (current >= 1) {
this.current = current;
}
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
if (limit >= 1 && limit <= 100) {
this.limit = limit;
}
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
if (rows >= 0) {
this.rows = rows;
}
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
/**
* 获取当前页的起始行
*/
public int getOffset() {
// current*limit - limit
return (current - 1) * limit;
}
/**
* 获取总页数
*/
public int getTotal() {
if (rows % limit == 0) {
return rows / limit;
} else {
return rows / limit + 1;
}
}
/**
* 获取起始页码
*/
public int getFrom() {
int from = current - 2;
return from < 1 ? 1 : from;
}
/**
* 获取结束页码
*/
public int getTo() {
int to = current + 2;
int total = getTotal();
return to > total ? total : to;
}
}
dao包下分别创建UseMapper接口和DiscussPostMapper接口文件
@Mapper
public interface UserMapper{
/**根据id查询用户*/
User selectById(int id);
/**根据用户名查询用户*/
User selectByName(String username);
/**根据邮箱查询用户*/
User selectByEmail(String email);
/**插入新用户*/
int insertUser(User user);
/**根据id修改状态*/
int updateStatus(int id, int status);
/**更新对应id的头像*/
int updateHeader(int id, String headerUrl);
/**更新用户密码*/
int updatePassword(int id, String password);
}
@Mapper
public interface DiscussPostMapper{
/**
* 根据用户id查询评论集合;或者查询所有评论集合
* @param userId 用户id
* @param offset 起始行行号
* @param limit 每页最多显示数量
* @return
*/
List<DiscussPost> selectDiscussPosts(int userId,int offset, int limit);
/**
* 查询总的评论数; @Param注释的作用给参数起别名; 当需要动态的拼接参数,并且参数只有一个的时候,必须有@Param注释
* 只有一个参数,并且在<if>里使用,则必须加别名
* @param userId
* @return
*/
int selectDiscussPostRows(@Param("userId") int userId);
}
在resources包下新建mapper包,在mapper包下新建UserMapper.xml文件和DiscussPostMapper.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.ahtoh.community.dao.UserMapper">
<select id="selectById" resultType="com.ahtoh.community.entity.User">
select *
from USER
where id = #{id};
</select>
<select id="selectByName" resultType="com.ahtoh.community.entity.User">
select *
from USER
where username = #{username};
</select>
<select id="selectByEmail" resultType="com.ahtoh.community.entity.User">
select *
from USER
where email = #{email};
</select>
<insert id="insertUser" parameterType="com.ahtoh.community.entity.User" keyProperty="id">
insert into user(username, password, salt, email, type, status, activation_code, header_url, create_time)
values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
</insert>
<update id = "updateStatus">
update user set status = #{status} where id = #{id}
</update>
<update id = "updateHeader">
update user set header_url = #{headerUrl} where id = #{id}
</update>
<update id = "updatePassword">
update user set password = #{password} where id = #{id}
</update>
</mapper>
<?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.ahtoh.community.dao.DiscussPostMapper">
<sql id="selectFields">
id,user_id,title,content,type,status,create_time,comment_count,score
</sql>
<select id="selectDiscussPosts" resultType="com.ahtoh.community.entity.DiscussPost">
select * from discuss_post
where status != 2
<if test="userId != 0">
and user_id = #{userId}
</if>
order by type desc, create_time desc
limit #{offset}, #{limit}
</select>
<select id="selectDiscussPostRows" resultType="int">
select count(id) from discuss_post
where status != 2
<if test="userId != 0">
and user_id = #{userId}
</if>
</select>
</mapper>
在community包下创建service包controller包,service包下新建UserService和DiscussPostService文件。
@Service
public class UserService{
@Autowired
private UserMapper userMapper;
/**
* 根据用户id查询用户
* @param userId
* @return
*/
public User findUserById(int userId){
return userMapper.selectById(userId);
}
/**
* 根据用户名称查询用户
* @param name
* @return
*/
public User findUserByName(String name){
return userMapper.selectByName(name);
}
}
@Service
public class DiscussPostService{
@Autowired
private DiscussPostMapper discussPostMapper;
/**
* 查询对应用户的所有评论
* @param userId 用户id
* @param offset 起始行数
* @param limit 最大显示数
* @return
*/
public List<DiscussPost> findDiscussPosts(int userId, int offset, int limit){
return discussPostMapper.selectDiscussPosts(userId,offset,limit);
}
/**
* 查询对应用户评论数
* @param userId 用户id
* @return
*/
public int findDiscussPostRows(int userId){
return discussPostMapper.selectDiscussPostRows(userId);
}
}
controller是负责业务调度的,所以在这一层应写一些业务的调度代码,是连接前端和后端的纽带。UserController 在本章需要实现的功能是接受浏览器发送过来的请求,然后去响应 thymeleaf 的模板页面 index.html。首页页面controller创建HomeController文件
@Controller
public class HomeController{
@Autowired
private UserService userService;
@Autowired
private DiscussPostService discussPostService;
/**
* 获取用户名和对应的评论
* @param model 前端返回模型
* @return
*/
@GetMapping("/index")
public String getIndexPages(Model model, Page page){
/**方法调用前,SpringMVC会自动实例化Model和Page,并将Page注入Model。
* 所以,在thymeleaf中可以直接访问Page对象中的数据*/
/**查询总行数*/
page.setRows(discussPostService.findDiscussPostRows(0));
/**复用页面路径*/
page.setPath("/index");
/**查询所有的评论*/
List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
/**创建用于存储用户对象和对应评论map*/
List<Map<String, Object>> discussPosts = new ArrayList<>();
/**判断查询是否为空*/
if(list != null) {
/**遍历查询到的评论对象*/
for (DiscussPost post : list) {
Map<String, Object> map = new HashMap<>();
/**根据用户id查询用户对象*/
User user = userService.findUserById(post.getUserId());
map.put("post",post);
map.put("user",user);
/**封存用户和对应的评论*/
discussPosts.add(map);
}
}
/**返回前端对象*/
model.addAttribute("discussPosts",discussPosts);
return "/index";
}
}
1.3、功能测试
启动 CommunityApplication类,在浏览器中输入地址http://localhost:8080/community/index
此时显示的是前端的首页:
注意:直接导入前端文件,可能会报属性不存在或者为空的错误,原因是后端没有传对应的属性,需要先注释掉前端对应报错的属性,即可正常测试。