分页查询时怎么操作的,面试问过
1.配置分页功能实体类Page对象
分页的三个基本属性:这三个必须有,其余属性可以通过这3个值计算添加
- 1、每页几条记录limit,显示上限值,可以有默认值
- 2、当前第几页current,当前页数,可以有默认值
- 3、总数据数rows,不可以有默认值,必须从数据库中查到
page类
/**
* 封装分页相关的信息.
*/
public class Page {
// 当前页码,默认1
private int current = 1;
// 显示上限,本题默认10
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() {
// rows / limit [+1]
if (rows % limit == 0) {
return rows / limit;
} else {
return rows / limit + 1;
}
}
//获取起始页码,就是上一页和下一页中间的页数:每次当前页前后隔2页
public int getFrom() {
int from = current - 2;
return from < 1 ? 1 : from;
}
//获取结束页码,就是上一页和下一页中间的页数:每次当前页前后隔2页
public int getTo() {
int to = current + 2;
int total = getTotal();
return to > total ? total : to;
}
}
2. javabean实体类和数据库建表
实体类
@Data
public class DiscussPost {
private int id;
private int userId;
private String title;
private String content;
private int type;
private int status;
private Date createTime;
private int commentCount;
private double score;
}
建表语句
CREATE TABLE `discuss_post` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(45) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`content` text,
`type` int(11) DEFAULT NULL COMMENT '0-普通; 1-置顶;',
`status` int(11) DEFAULT NULL COMMENT '0-正常; 1-精华; 2-拉黑;',
`create_time` timestamp NULL DEFAULT NULL,
`comment_count` int(11) DEFAULT NULL,
`score` double DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. Mapper接口类和映射文件
数据层接口
@Mapper
public interface DiscussPostsMapper {
/**
* 如果是社区首页,那么userId=0(不传入userId),也就是说将全部讨论帖显示出来
* 如果是个人首页,那么userId!=0(传入UserId),也就是说仅显示个人发布的帖子
* 因此需要使用动态SQL,将SQL语句拼接起来。
*
* @param userId
* @param offset 每一页的起始行号
* @param limit 每一页显示的帖子数
* @return 返回的是执行SQL语句查询结果的DiscussPost对象的集合。
*/
List<DiscussPost> selelctDiscussPost(int userId,int offset,int limit);
/**
* 查询数据库中所有的帖子数,使用动态SQL,社区首页userId=0,个人首页userId!=0
* 用来分页查询时计算每页显示的帖子数:总数/页数
* @param userId
* @return
*
* @Param 注解的使用场景:
* 1、方法有多个参数时
* 2、方法参数需要起别名时
* 3、XML中的SQL使用了 $
* 4、动态sql中参数是非自定义pojo类型
*
* 这里并不是为了给userId起别名,而是如果方法只有一个参数,
* 并且在<if></if>中使用,必须加这个注解
*/
int selectDiscussPostRows(@Param("userId") int userId);
}
映射文件
<!--定义一个sql标签,唯一id,用来封装sql语句-->
<sql id="selectFields">
id, user_id, title, content, type, status, create_time, comment_count, score
</sql>
<select id="selectDiscussPosts" resultType="DiscussPost">
<!--使用include标签来引用封装好的sql语句-->
select <include refid="selectFields"></include>
from discuss_post
where status != 2
<!--动态sql, test属性常用条件判断语句中判断真假,如果传入的查询条件成立(userId!=0),就进行SQL组装-->
<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>
4. 业务层和表现层
service
/**
* Service层用于编写业务逻辑,调用dao层的方法
*/
@Service
public class DiscussPostService {
@Autowired
DiscussPostMapper discussPostMapper;
public List<DiscussPost> findDiscussPosts(int userId,int offset,int limit){
return discussPostMapper.selectDiscussPosts(userId,offset,limit);
}
public int findDiscussPostsRows(int userId){
return discussPostMapper.selectDiscussPostRows(userId);
}
}
controller前后端交互
@Controller
public class HomeController {
@Autowired
private DiscussPostService discussPostService;
@Autowired
private UserService userService;
/**
* 1、model对象用于传递控制方法处理数据到结果页面,把结果页面上需要的数据放到Model对象中
* 2、在方法调用前,SpringMvc会自动实例化Model和Page,并将Page注入Model,因此可以在thymeleaf中直接访问page中数据
* 3、使用limit #{offset}, #{limit}实现分页查询,查询数据库时方法中需要传入offset和limit
* 4、封装Page对象,用来封装实现分页查询的属性和方法:对象的三大特性之一
* 当前页码:current
* 每页显示的帖子数:limit
* 帖子总数:rows
* 查询路径:path
* 前端点击页码会在超链接中传入一个请求参数current,通过current可计算出当前页的起始行offset
* 传入的Page对象不但可获取offset和limit,而且可将数据库中查询信息封装在Page对象中,传递给页面
*/
@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model, Page page) {
page.setRows(discussPostService.findDiscussPostRows(0));
page.setPath("/index");
List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
List<Map<String, Object>> discussPosts = new ArrayList<>();
if (list != null) {
for (DiscussPost post : list) {
Map<String, Object> map = new HashMap<>();
map.put("post", post);
User user = userService.findUserById(post.getUserId());
map.put("user", user);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts", discussPosts);
// model.addAttribute("page",page);
return "/index";
}
}
5. 前端页面
<!-- 分页 -->
<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 ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}">
<a class="page-link" href="#" th:text="${i}">1</a>
</li>
<li th:class="|page-item ${page.current==page.total?'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.total})}">末页</a>
</li>
</ul>
</nav>