博客系统1.4.3(个人版本) (win64)
需求分析
场景1:用户管理页面(注册/登录页面)
显示注册框、登录输入框及注册/登录功能按钮;
场景2:首页/博客列表页
博客系统入口页;
展示历史已有博客的题目及内容节选(非正式用户也可查看);
场景2:查看博客
展示博客标题,当前博客正文所有内容,博客作者及发表时间;
场景3:发布博客
博客标题输入框、正文输入框;
发布博客功能按钮;
场景4:删除博客
删除博客功能按钮(登录后可用);
判断登录用户与计划删除博客作者是否为同一用户;
场景5:注销账户
注销账户功能按钮(已注册登录用户可用);
用户管理界面
1.设计数据库
①首先设计了两个表user表和blog表
user表, userId、username、password
blog表,blogId、title、content、userId、postTime
两张表通过userId进行关联,可得到对应博客的作者信息,也可对后续的删除博客进一步的逻辑设计。
注意:
可以将设计数据库的sql语句放在一个文件里面,有利于后续在服务器上进行建表操作,不用进行重复编写sql语句
②将操作数据库的代码进行封装,简化代码
JDBC操作数据库与数据库建立连接,封装和数据库建立连接的过程,使用单例类封装数据库建立连接的过程
package utils;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCUtil {
private static final String URL="jdbc:mysql:///rb?characterEncoding=utf8&useSSL=false";
private static final String USERNAME="root";
private static final String PASSWORD="数据库密码";
private static DataSource dataSource;
public static DataSource getDataSource(){
//首先要看DataSource是否已有实例,没有就创建新的,有就返回之前的
//为了确保DataSource指的是当前引用,要判断其是否为null
if(dataSource==null){
MysqlDataSource mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setUrl(URL);
mysqlDataSource.setUser(USERNAME);
mysqlDataSource.setPassword(PASSWORD);
dataSource=mysqlDataSource;
}
return dataSource;
}
public static Connection getConnection() {
try {
return getDataSource().getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2.创建实体类(blog user)
package dao;
import java.sql.Timestamp;
public class Blog {
private int blogId;
private String title;
private String content;
private int userId;
//java.sql.data只能表示日期,没有时分秒,而Timestamp有
private Timestamp postTime;
public int getBlogId() {
return blogId;
}
public void setBlogId(int blogId) {
this.blogId = blogId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public Timestamp getPostTime() {
return postTime;
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
@Override
public String toString() {
return "dao.Blog{" +
"blogId=" + blogId +
", title='" + title + '\'' +
", content='" + content + '\'' +
", userId=" + userId +
", postTime=" + postTime +
'}';
}
}
package dao;
public class User {
private int userId;
private String username;
private String password;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "dao.User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
3.针对博客和用户进行具体的增删改查操作(实现了UserDao,BlogDao)
①UserDao实现了新建用户、根据用户名获取密码、根据用户ID获取用户信息
package dao;
import entity.User;
import utils.JDBCUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDao {
public void insert(User user){
//1.和数据库建立连接;
Connection connection= JDBCUtil.getConnection();
//2.拼装sql
String sql="insert into user values(null,?,?)";
PreparedStatement statement=null;
try {
statement = connection.prepareStatement(sql);
statement.setString(1,user.getUsername());
statement.setString(2,user.getPassword());
//3.执行sql
statement.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//4.关闭连接
JDBCUtil.close(connection,statement,null);
}
}
//登陆的时候,需要根据用户名获取到密码
public User selectByName(String username){
//1.和数据库建立连接
Connection connection=JDBCUtil.getConnection();
//2.拼装sql
String sql="select * from user where username = ?";
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
statement=connection.prepareStatement(sql);
statement.setString(1,username);
//3.执行sql
resultSet =statement.executeQuery();
//4.遍历结果集,按照名字查找的结果是唯一的
if (resultSet.next()){
User user=new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
System.out.println("userDao"+user);
return user;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(connection,statement,resultSet);
}
return null;
}
//根据用户id获取用户信息
//显示博客的时候需要用到
public User selectById(int userId){
//1.和数据库建立连接
Connection connection=JDBCUtil.getConnection();
//2.拼装sql
String sql="select * from user where userId = ?";
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
statement=connection.prepareStatement(sql);
statement.setInt(1,userId);
//3.执行sql
resultSet=statement.executeQuery();
//4.遍历结果,预期结果是中有一个或者0个
if(resultSet.next()){
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(connection,statement,resultSet);
}
return null;
}
②BlogDao实现了添加博客、删除博客、查找博客、查询所有博客等内容
package dao;
import entity.Blog;
import utils.JDBCUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class BlogDao {
public void insert(Blog blog){
//1.和数据库服务器建立连接
Connection connection= JDBCUtil.getConnection();
//2.拼装SQL语句
String sql="insert into blog values(null,?,?,?,now())";
PreparedStatement statement=null;
try {
statement=connection.prepareStatement(sql);
statement.setString(1,blog.getTitle());
statement.setString(2,blog.getContent());
statement.setInt(3,blog.getUserId());
//执行SQL
statement.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(connection,statement,null);
}
}
//从数据库中删除博客
public void delete(int blogId){
//1.数据库服务器建立连接
Connection connection= JDBCUtil.getConnection();
//拼装SQL
String sql="delete from blog where blogId=?";
PreparedStatement statement=null;
try {
statement =connection.prepareStatement(sql);
statement.setInt(1,blogId);
//执行SQL
statement.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(connection,statement,null);
}
}
//limit offset 数据库中内容很多可进行分页
public List<Blog> selectAll(){
List<Blog> blogs=new ArrayList<Blog>();
//1.和数据库服务器建立连接
Connection connection= JDBCUtil.getConnection();
//2.拼装SQL
String sql="select * from blog order by blogId desc ";
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
statement=connection.prepareStatement(sql);
//3.执行sql
resultSet=statement.executeQuery();
//4.遍历结果集
while (resultSet.next()){
Blog blog=new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
String content=resultSet.getString("content");
if(content.length()>10){
content=content.substring(0,10)+".......";//左闭右开的区间
}
blog.setContent(content);
blog.setUserId(resultSet.getInt("userId"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
blogs.add(blog);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(connection,statement,resultSet);
}
return blogs;
}
public Blog selectOne(int blogId){
//1.和数据库建立连接
Connection connection= JDBCUtil.getConnection();
//2.拼装sql
String sql="select * from blog where blogId = ?";
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
statement=connection.prepareStatement(sql);
statement.setInt(1,blogId);
//执行sql
resultSet=statement.executeQuery();
//4.遍历结果集,要么使0个,要么只有一条记录;
if (resultSet.next()){
Blog blog=new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
blog.setContent(resultSet.getString("content"));
blog.setUserId(resultSet.getInt("userId"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
return blog;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(connection,statement,resultSet);
}
return null;
}
写完后可根据每个功能进行Junit测试,或者冒烟测试,保证代码的逻辑正确。
servlet
1.注册登录功能
获取请求中的username和password,然后进行判断。写代码的过程中一定要注意编码问题,要记得设定统一的编码,防止中文乱码。
其中的一些错误页面,也可以写出对应的静态页面,然后通过重定向。eg:resp.sendRedirect(“blogList”);
package servlet;
import entity.User;
import dao.UserDao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.先从请求中读取username和password
req.setCharacterEncoding("utf-8");
String username=req.getParameter("username");
String password=req.getParameter("password");
if(username==null||"".equals(username)||password==null||"".equals(password)){
resp.sendError(404,"用户名或密码为空");
return;
}
//2.从数据中查找指定用户名的信息
UserDao userDao=new UserDao();
User user= userDao.selectByName(username);
System.out.println(user);
if(user==null){
resp.sendError(404,"您的用户名或密码有误或未注册");
return;
}
if(!password.equals(user.getPassword())){
resp.sendError(404,"您的用户名或密码有误");
return;
}
//3.登陆成功,创建会话
HttpSession session= req.getSession(true);
session.setAttribute("user",user);
//4.直接把用户页面重定向到主页(博客列表页)
resp.sendRedirect("blogList");
}
}
package servlet;
import entity.User;
import dao.UserDao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如果不做任何限定,此时服务器这边读取Parameter时侯默认不是UTF-8
req.setCharacterEncoding("utf-8");
//1.先读取用户名提交的用户和密码
String username=req.getParameter("username");
String password=req.getParameter("password");
if(username==null||"".equals(username)||password==null||"".equals(password)){
resp.sendError(404,"提交的用户名或密码为空");
return;
}
//2.查询数据库看username是否存在
UserDao userDao=new UserDao();
User existUser=userDao.selectByName(username);
if(existUser!=null){
//用户已经存在提醒注册失败
resp.sendError(404,"用户名已存在");
return;
}
//3.构造User对象,插入到数据库中
User newuser=new User();
newuser.setUsername(username);
newuser.setPassword(password);
userDao.insert(newuser);
//4.返回一个结果
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("<h3>恭喜注册成功!</h3>");
}
2.博客列表页
博客列表页中涉及了一个有关Thmeleaf渲染的功能。
Thmeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个web页面。
Thmeleaf通过第三方库导入依赖,engine需要在多个servlet之间共享,所以将初始化engine的工作交给ServletContext,任意一个servlet都可以拿到一个ServletContext对象,这样也就能拿到同一个engine。
package servlet;
import entity.Blog;
import dao.BlogDao;
import entity.User;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
@WebServlet("/blogList")
public class BlogListServlet extends HttpServlet {
private TemplateEngine engine=null;
@Override
public void init() throws ServletException {
//init生命周期,初始化一次,我们可以利用这个方法,将Thmeleaf初始化
//1)创建一个engie(引擎),负责把java中的数据替换到模板中
engine=new TemplateEngine();
//2)创建一个resover对象(解析器)负责找到html模板在哪啦,供engie对象使用
ServletContextTemplateResolver resolver=new ServletContextTemplateResolver(getServletContext());
//3)给resover设置一些属性,让它能够找到html模板
resolver.setCharacterEncoding("utf-8");
//prefix表示前缀,设定了满足啥样条件的文件被加载到内存中作为HTML模板
resolver.setPrefix("/WEB-INF/template/");
//Suffix表示后缀
resolver.setSuffix(".html");
//4)把resolver和engine关联起来
engine.setTemplateResolver(resolver);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//0.从req中读取一下当前用户信息,判定用户是否登录
HttpSession session=req.getSession(false);
boolean isLogin=true;
User user=null;
if(session==null){
isLogin=false;
}else{
user=(User)session.getAttribute("user");
if(user==null){
isLogin=false;
}
}
//1.先从数据库查询出有那些博客
BlogDao blogDao=new BlogDao();
List<Blog> blogsList= blogDao.selectAll();
//2.构造博客页面
//1)通过Thymeleaf进行渲染,渲染的时候要定义”数据集合“这样的概念
//WebContext功能就是把要替换的数据收集起来,统一的交给模板引擎
WebContext webContext=new WebContext(req,resp,getServletContext());
//2)setVariable可以设置多个键值对,完全取决于模板代码怎么写。
//模板里的每一个${}里面的内容都需要在webContext设定进去
webContext.setVariable("blogs",blogsList);
webContext.setVariable("isLogin",isLogin);
webContext.setVariable("user",user);
//3)进行渲染
TemplateEngine engine=(TemplateEngine)getServletContext().getAttribute("engine");
String html= engine.process("blog_list",webContext);//渲染的文件名
System.out.println("模板渲染的内容:"+html);
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write(html);
}
}
3.博客发布页
如果用户尚未登录,则无法添加博客。
package servlet;
import entity.Blog;
import dao.BlogDao;
import entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/blogInsert")
public class BlogInsertServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//0.判定用户是否已经登陆
req.setCharacterEncoding("utf-8");
HttpSession session=req.getSession(false);
if(session==null){
resp.sendError(404,"请登录后发布博客");
return;
}
User user=(User) session.getAttribute("user");
if(user==null){
resp.sendError(404,"请登录后发布博客");
return;
}
//1.读取请求中的参数.
String title=req.getParameter("title");
String content=req.getParameter("content");
if(title==null||"".equals(title)||content==null||"".equals(content)){
resp.sendError(500,"标题或者正文为空");
return;
}
//2.根据读到的数据构造blog对象,并填入数据库
Blog blog=new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserId(user.getUserId());
BlogDao blogDao=new BlogDao();
blogDao.insert(blog);
//3.重定向到博客列表页
resp.sendRedirect("blogList");
}
}
4.博客正文页
根据对应的博客ID找到对应的博客再根据博客中的userId找到对应作者的信息。
package servlet;
import entity.Blog;
import dao.BlogDao;
import entity.User;
import dao.UserDao;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/blogContent")
public class BlogContentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String blogId = req.getParameter("blogId");
if(blogId==null||"".equals(blogId)){
resp.sendError(404,"blogId不存在");
return;
}
BlogDao blogDao=new BlogDao();
Blog blog=blogDao.selectOne(Integer.parseInt(blogId));
if(blog==null){
resp.sendError(404,"blogId指定的文章不存在");
return;
}
UserDao userDao=new UserDao();
User user=userDao.selectById(blog.getUserId());
//根据详细内容,渲染到模板中
TemplateEngine engine= (TemplateEngine) getServletContext().getAttribute("engine");
WebContext webContext=new WebContext(req,resp,getServletContext());
webContext.setVariable("blog",blog);
webContext.setVariable("username",user.getUsername());
String html=engine.process("blog_content",webContext);
//把渲染好的结果返回给·客户端
resp.getWriter().write(html);
}
}
5.博客删除页面
获取要删除的博客ID,判断用户是否登录,以及此博客对应的userId是否一致来判断是否可以删除此博客。
package servlet;
import entity.Blog;
import dao.BlogDao;
import entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String blohId=req.getParameter("blogId");
if(blohId==null||"".equals(blohId)){
resp.sendError(404,"您的blogId为空");
return;
}
HttpSession session=req.getSession(false);
if(session==null){
resp.sendError(404,"当前未登录不能删除");
return;
}
User user=(User)session.getAttribute("user");
if(user==null){
resp.sendError(404,"当前未登录不能删除");
return;
}
BlogDao blogDao=new BlogDao();
Blog blog=blogDao.selectOne(Integer.parseInt(blohId));
if(blog==null){
resp.sendError(404,"当前博客不存在!");
return;
}
if(blog.getUserId()!=user.getUserId()){
resp.sendError(403,"您无法删除此博客");
return;
}
blogDao.delete(Integer.parseInt(blohId));
//5.重定向到博客列表页
resp.sendRedirect("blogList");
}
}
前端实现
1.登录注册页面
2.博客列表页及正文页
3.博客发布页