一 用户模块的实现
本节主是对于用户表的增删改查操作,用户表将贯穿整个系统。下面在数据访问层dao中和业务层service中实现对于用户的各种操作以方便后面其它业务对于用户业务的调用。
- dao层的实现,对于用户表属性的增删改查操作,下面的实现是对于用户比较频繁的操作,比如通过
id
获取用户的信息,通过用户名查找用户,更改用户的头像等,其中最为常见的操作为通过用户的id获取用户信息,在后面系统的实现中可以看到对于该接口的调用,后面将会通过更加高效的方式比如redi缓存的方式实现该接口。
- 数据访问层的实现
@Mapper
public interface UserMapper {
User getUserById(int id);
User getUserByName(String name);
User getUserByEmail(String email);
int insertUser(User user);
int updateUserByStatus(int id,int status);
int updateHeader(int id,String headerUrl);
int updatePassword(int id,String password);
}
- 在
Resources/mapper
下面创建目录UserMpper.xml
文件编写每条sql语句和上面编写的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.wjiangquan.community.dao.UserMapper">
<sql id="selectFields">
id,username,password,salt,email,type,status,activation_code,header_url,create_time
</sql>
<sql id="insertFields">
username,password,salt,email,type,status,activation_code,header_url,create_time
</sql>
<!--根据id查询用户-->
<select id="getUserById" resultType="User">
select <include refid="selectFields"/>
from user
where id=#{id}
</select>
<!--根据用户名查询用户-->
<select id="getUserByName" resultType="User">
select <include refid="selectFields"/>
from user
where username=#{name}
</select>
<!--根据邮件创建用户-->
<select id="getUserByEmail" resultType="User">
select id,username,password,salt,email,type,status,activation_code,header_url,create_time
from user
where email=#{email}
</select>
<!--增加用户-->
<insert id="insertUser" parameterType="User" keyProperty="id">
insert into user(<include refid="insertFields"/>)
values(#{username},#{password},#{salt},#{email},#{type},#{status},#{activationCode},#{headerUrl},#{createTime})
</insert>
<!--通过id更新状态-->
<update id="updateUserByStatus">
update user set status=#{status}
where id=#{id}
</update>
<!--通过id更新头像-->
<update id="updateHeader">
update user set header_url=#{headerUrl}
where id=#{id}
</update>
<update id="updatePassword">
update user set password=#{password}
where password=#{password}
</update>
</mapper>
二 首页开发
需求:
- 查询所有的帖子,首页展示的是所有的帖子,用户个人界面展示用户的帖子
- 查询帖子的总数。
需要注意:
- 使用动态
sql
的方法动态查询(根据userId传入的值不同,当userId
为0式为查询首页的数据,其它值时为查询特定用户的帖子)
- entity,dao,mapper.xml的编写
首先导入静态资源。静态资源中的部分代码已经提前写好直接导入即可,将css
,img
,js
文件放入到static目录下。其中static下面的html文件夹中html文件用于测试。
具体实现:
下面将按照代码的实现流程的的方式展示首页开发的整体流程以及对于部分细节的详细讲解。
- 在
entity
包中写帖子表对应的实体类
public class DiscussPost {
private int UserId;
private String title;
private String content;
private int type; //贴子类型 0普通类型 1置顶帖子
private int status; //帖子状态 0-正常 1-精华 2-拉黑
private Date createTime;
private int commentCount;
private double score;
//省略getter方法和setter方法
}
可以根实际的需求,是否重写Object类
tostring
方法和equals
方法
- dao层,在dao包中创建一个类
DiscussPostMapper
类
@Mapper
public interface DiscussPostMapper {
//获取全部帖子(首页和个人界面 传入的不为0代码的是个人贴子查询)
List<DiscussPost> getAllDiscussPost(@Param("userId") int userId,@Param("offset") int offset,@Param("limit") int limit);
//当使用动态生成并且只有一个参数时(如使用if时,需要使用@param参数)
int getDiscussRows(@Param("userId") int userId);
}
对应的DiscussPostMapper.xml
文件的代码。其中if标签用于判断,属性test是if标签的条件。当满足userid!=0时将执行userid=#{userid},此时根据用户的id进行查询,即查询特定用户的所有帖子。
<sql id="selectFields">
id,user_id,title,content,type,status,create_time,comment_count,score
</sql>
<select id="getAllDiscussPost" resultType="DiscussPost">
select <include refid="selectFields"/>
from discuss_post
where status!=2 # 为2时表示该帖子已经删除
<if test="userId!=0">
and user_id = #{userId}
</if>
order by type desc,create_time desc
limit #{offset},#{limit}
</select>
<!--查总数-->
<!--查总数-->
<select id="getDiscussRows" resultType="int">
select count(*)
from discuss_post
where status!=2
<if test="userId!=0">
and user_id=#{userId}
</if>
</select>
- service层,service层采用接口和实现类的方式,在下面的代码中将只展示实现类的代码,对于接口的代码进行省略,可以根据自己项目的情况确定是否需要写接口。
@Service
public class DiscussPostServiceImpl implements DiscussPostService {
@Autowired
private DiscussPostMapper discussPostMapper;
@Override
public List<DiscussPost> getAllDiscussPost(int userId, int offset, int limit) {
List<DiscussPost> allDiscussPost = discussPostMapper.getAllDiscussPost(userId, offset, limit);
return allDiscussPost;
}
@Override
public int getDiscussRows(int userId) {
return discussPostMapper.getDiscussRows(userId);
}
}
- controller层,用于处理前端的请求,向前端响应数据。
@Controller
public class DiscussPostController {
@Autowired
private UserService userService;
@Autowired
private DiscussPostService discussPostService;
/**
* 首页帖子展示
* @param model
* @return
*/
@RequestMapping(name = "/index",method = RequestMethod.GET)
public String getByPage(Model model, Page page){
// 方法调用之前,SpringMcv会自动实例化Model和Page,并将Page注入到Model
// 所以,在thymeleaf中可以直接访问Page对象中的数据
//获取总行数
int discussRows = discussPostService.getDiscussRows(0);
page.setRows(discussRows);
page.setPath("/index");
//获取帖子
List<DiscussPost> allDiscussPost = discussPostService.getAllDiscussPost(0, page.getOffset(), page.getLimit());
//用于存放用户和帖子
List<Map<String,Object>> discussPosts = new ArrayList<>();
// 获取用户
if(allDiscussPost!=null){
for (DiscussPost discussPost:allDiscussPost){
Map<String,Object> map = new HashMap<>();
User user = userService.getUserById(discussPost.getUserId());
map.put("post",discussPost);
map.put("user",user);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts",discussPosts);
return "/index";
}
}
由于本项目使用分页的地方比较频繁,为了更好的处理分页,封装一个实现类Page
用于处理分页。
public class Page {
private int current = 1; //当前页
private int limit = 112; //每页的条数(显示的上限)
private int rows; //总行数(用于计算总页数)
private String path; //查询路径(用于复用分页的链接)
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
if(current>=0){
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;
}
/**
* 获取总页数
* @return
*/
public int getTotal(){
if(rows/limit==0){
return rows/limit;
}else{
return rows/limit+1;
}
}
/**
* 获取起始页码
*/
public int getFrom(){
int from = current-2;
return Math.max(from, 1); //等价于from<1?1:from
}
/**
* 获取结束页码
* @return
*/
public int getTo(){
int to = current+2;
int total = getTotal();
return Math.min(to, total);
}
}
-
前端页面展示。页面展示渲染通过model传递数据通过列表的方式进行分页展示。
下面为帖子列表渲染的代码。
th:each="item:${discussPosts}"
吗,其中item表示discussPosts集合中的每一项。
<!-- 帖子列表 -->
<ul class="list-unstyled">
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="item:${discussPosts}">
<a href="site/profile.html">
<img th:src="${item.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="${item.post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</a>
<span class="badge badge-secondary bg-primary" th:if="${item.post.type==1}">置顶</span>
<span class="badge badge-secondary bg-danger" th:if="${item.post.status==1}">精华</span>
</h6>
<div class="text-muted font-size-12">
<u class="mr-3" th:utext="${item.user.username}">寒江雪</u> 发布于 <b th:text="${#dates.format(item.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</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>
</ul>
分页列表的代码。对于分页列表的代码,后面将会反复的引用。th:fragment="pagination"表示给这个部分代码去一个名字, 名字为pagination方便其它页面引用。
<!-- 分页 -->
<nav class="mt-5" th:if="${page.rows>0}" th:fragment="pagination">
<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" th:href="@{${page.path}(current=${i})}" 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>