开发社区首页
一次请求流程
Dao层
1、先写entity中的实体类
- 依据数据库中表的字段建立相应的属性,并且生成其get和set方法,以及toString方法;
2、编写对应的Mapper接口,规定需要实现的方法
- selectDiscussPosts查询帖子,参数userid,offset-起始页,limit-每一页显示的数据
- userid==0表示首页查询,==其他值表示查询个人主页的帖子
- countDiscussPosts–表示查询一共有多少帖子,去除被拉黑的帖子,为了下一步的分页
@Mapper
public interface DiscussPostMapper {
//查询从offset开始每页limit条数据的帖子信息
List<DiscussPost> selectDiscussPosts(int userId,int offset,int limit);
//查询一共有多少条帖子
//@Param给参数取一个别名 当只有一个参数并且需要在<if>中使用就必须要加注解
int countDiscussPosts(@Param("userId") int userId);
}
3、编写Mapper对应的xml文件,编写sql语句
- 判断标签,如果test中值为false就不会拼接到sql中,否则拼接
<?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.js.community.dao.DiscussPostMapper">
<sql id="selectFields">
id, user_id, title, content, type, status, create_time,comment_count,score
</sql>
<select id="selectDiscussPosts" resultType="DiscussPost">
select <include refid="selectFields"></include>
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="countDiscussPosts" resultType="int">
select count(id)
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>
</mapper>
Service层
4、编写与Mapper对应的Service层,实现相应的方法
@Service
public class DiscussPostService {
@Autowired(required = false)
private DiscussPostMapper discussPostMapper;
//查询从offset开始每页limit条数据的帖子信息
public List<DiscussPost> findDiscussPosts(int userId, int offset, int limit)
{
return discussPostMapper.selectDiscussPosts(userId,offset,limit);
}
//查询一共有多少条帖子
//@Param给参数取一个别名 当只有一个参数并且需要在<if>中使用就必须要加注解
public int countDiscussPosts(@Param("userId") int userId)
{
return discussPostMapper.countDiscussPosts(userId);
}
}
- 为了把用户表和帖子表相关联,通过userid查询到username 可以有两种办法
- 1、在写sql查询的时候通过语句把查询到username返回;
- 2、在查询到帖子后通过userService层的方法查询到user,在返回其User对象,从而获得其它详细信息;一般使用这种方法,代码比较直观,使用redis缓存数据会更加方便
@Service
public class UserService {
@Autowired(required = false)
private UserMapper userMapper;
public User queryUserById(int userId)
{
return userMapper.queryById(userId);
}
}
导入前端资源
css,img,js等放在static下 网页放在templates下
、
Controller层
- 编写对应的Controller,调用Service层中的方法;
- 调用discussPostService找到帖子的前十条数据,并把其放到List集合中;
- 创建一个新的集合,遍历查询每一条帖子,通过userid找到对应的详细User信息,并且把它们放到map中,最后加入新集合中
- 把新集合加入到model模型中
- 返回主页面
@Controller
public class DiscussPostController {
@Autowired
private DiscussPostService discussPostService;
@Autowired
private UserService userService;
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String getIndexPage(Model model)
{
//获取帖子数据
List<DiscussPost> list = discussPostService.findDiscussPosts(0, 0, 10);
//创建新集合
List<Map<String,Object>> discussPosts=new ArrayList<>();
//遍历list
if(list!=null)
{
for(DiscussPost post:list)
{
Map<String,Object> map=new HashMap<>();
map.put("post",post);
User user = userService.queryUserById(post.getUserId());
map.put("user",user);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts",discussPosts);
return "/index";
}
}
Thymeleaf
-
声明Thymeleaf
-
绝对路径不需要修改,相对路径需要修改防止找不到资源 方法是,把相对路径用@{}包起来,这样就会寻找static下的路径
-
<link rel="stylesheet" th:href="@{/css/global.css}" />
-
使用utext会把转义字符进行相应的转义,text则不会
-
对li标签进行each循环
-
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
<a href="site/profile.html">
<img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;">
</a>
<div class="media-body">
<h6 class="mt-0 mb-3">
<a href="site/discuss-detail.html" th:utext="${map.post.title}"></a>
<span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}" >置顶</span>
<span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span>
</h6>
<div class="text-muted font-size-12">
<u class="mr-3" th:utext="${map.user.username}"></u> 发布于 <b th:text="${#dates.format(map.user.createTime,'yyyy-mm-dd HH:mm:ss')}"></b>
<ul class="d-inline float-right">
<li class="d-inline ml-2">赞 11</li>
<li class="d-inline ml-2">|</li>
<li class="d-inline ml-2">回帖 7</li>
</ul>
</div>
</div>
</li>
- Thymeleaf的日期格式化工具,dates.format
<u class="mr-3" th:utext="${map.user.username}"></u> 发布于 <b th:text="${#dates.format(map.user.createTime,'yyyy-mm-dd HH:mm:ss')}"></b>
分页组件开发
实现分页的思路:
浏览器和服务器之间:
- 从浏览器传回的参数得知当前页码和显示上限,即每一页最多显示几条数据;
- 要有查询路径,即点击分页跳转的页面路径;
跟数据库相关的操作
- 告诉数据库查询的offset起始行数,和limit
- offset通过当前页的页码计算得出 公式 offset=(current-1)*limit
页面显示
- 要计算总页数,需要先查询一共有多少的帖子数量,再把帖子数量/limit 就能够得到一共有多少页,如果%取余!=0,总页数等于结果加1;
- 显示从多少页到多少页
操作
1、建立page实体类
- 编写对应属性和方法
- 增加条件判断
public class Page {
//当前页码
private int current=1;
//每页显示数量,默认为10
private int limit=10;
//帖子总数量
private int rows;
//跳转路径
private String path;
}
2、增加其他方法
//数据库中offset起始行数计算
public int getOffset()
{
return (current-1)*limit;
}
//计算一共有多少页
public int pageCount()
{
if(rows%limit==0) {
return rows / limit;
}else {
return rows/limit+1;
}
}
//获取起始页码
public int fromPage()
{
//如果cur比1大返回cur否则返回1
int cur=current-2;
return cur>1?cur:1;
}
//获取结束页码
public int endPage()
{
int cur=current+2;
int totalPage=pageCount();
return cur>totalPage?totalPage:cur;
}
3、修改controller
- 增加page对象
- 设置rows和path 修改offset和limit使其为动态
- 不需要向model中添加page对象 因为方法调用前,SpringMvc会自动实例化Model和Page,并将Page注入Model 所以,在Thymeleaf中可以直接访问Page对象中的数据
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String getIndexPage(Model model, Page page)
{
//不需要向model中添加page对象 因为方法调用前,SpringMvc会自动实例化Model和Page,并将Page注入Model
// 所以,在Thymeleaf中可以直接访问Page对象中的数据
//获取总行数
page.setRows(discussPostService.countDiscussPosts(0));
page.setPath("/index");
//获取帖子数据
List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
//创建新集合
List<Map<String,Object>> discussPosts=new ArrayList<>();
//遍历list
if(list!=null)
{
for(DiscussPost post:list)
{
Map<String,Object> map=new HashMap<>();
map.put("post",post);
User user = userService.queryUserById(post.getUserId());
map.put("user",user);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts",discussPosts);
return "/index";
}
4、修改分页
- 首先判断rows即帖子数量是否大于0,小于则不显示分页
- 修改首页末页 修改current的值
- 修改上一页下一页,使current的值减一加一
- 循环每一页,使用Thymeleaf的numbers工具,生成一组连续的数字,遍历
- 当页数为第一页和最后一页的时候,把上一页和下一页的按钮变成灰色
- 判断当前页数,修改active点亮当前页面
<!-- 分页 -->
<nav class="mt-5" th:if="${page.rows>0}">
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link" th:href="@{${page.path}(current=1)}">首页</a>
</li>
<li th:class="|page-item ${page.current==1?'disabled':''}|">
<a class="page-link" th:href="@{${page.path}(current=${page.current-1})}">上一页</a>
</li>
<li th:class="|page-item ${page.current==i?'active':''}|" th:each="i:${#numbers.sequence(page.fromPage(),page.endPage())}">
<a class="page-link" th:href="@{${page.path}(current=${i})}" th:text="${i}">1</a>
</li>
<li th:class="|page-item ${page.current==page.pageCount()?'disabled':''}|">
<a class="page-link" th:href="@{${page.path}(current=${page.current+1})}">下一页</a>
</li>
<li class="page-item">
<a class="page-link" th:href="@{${page.path}(current=${page.pageCount()})}">末页</a>
</li>
</ul>
</nav>
debug:
1、org.apache.ibatis.binding.BindingException: Parameter not found,使用@Param注解与不使用@Param注解
因为在Mapper的xml的Sql语句中写了多余的参数limit #{offset},#{limit}
2、别忘记修改标签属性加上th:
th:class="|page-item ${page.current==1?'disabled':''}|"
tem">
末页
debug:
1、org.apache.ibatis.binding.BindingException: Parameter not found,使用@Param注解与不使用@Param注解
因为在Mapper的xml的Sql语句中写了多余的参数limit #{offset},#{limit}
2、别忘记修改标签属性加上th:
th:class="|page-item ${page.current==1?'disabled':''}|"