个人博客
核心功能
数据库设计
两张表,一位用户可以发布多篇博客。因此在Article表中使用了user_id作为外键将两张表关联起来。
User表
属性 | 类型 | 说明 |
---|---|---|
id | int | primary key auto_increment(主键作为user的唯一表示) |
username | varchar(20) | unique |
password | varchar(20) | not null |
Article表
属性 | 类型 | 说明 |
---|---|---|
id | int | primary key auto_increment(主键作为Artilce的唯一表示) |
title | varchar(20) | not null |
content | mediumtext | not null |
create_time | timestamp | default now() |
user_id | int | foreign key |
服务器API设计
注册
请求:
POST /register
data:username=abc&password=123
响应:
{"success":false,"code":"RegisterError","message":"用户已存在","data":null}
登录
请求:
POST /login
data:username=abc&password=123
响应:
{"success":false,"code":"Login003","message":"用户名或者密码错误","data":null}
新增博客
请求:
Get /articleAdd
响应:
{"success":true,"code":null,"message":null,"data":null}
删除博客
请求:
Get /articleDelete
响应:
{"success":true,"code":null,"message":null,"data":null}
修改博客
请求:
Get /articleDetail
响应:
{"success":true,"code":null,"message":null,"data":{"id":9,"title":"张继科yyds","content":"<p>科科最帅<br/></p>","userId":null,"createTime":null}}
数据库封装操作
使用数据库连接池,减少多次创建(释放)连接带来的开销
单例模式(双重校验锁)
volatile关键字:一个进程进行读操作,一个线程进行写操作;当实例化dataSource对象时涉及读取操作和修改操作,因此需要使用 volatile;
synchronized关键字:涉及对象操作非原子性;当实例化dataSource对象时涉及判断对象是否为空,然后再对其进行实例化,操作不是原子的,可能线程不安全,因此需要使用synchronized;
同时还用到了方法的重载,实现了代码的复用
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import org.example.exception.AppException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DBUtil {
private static String url = "jdbc:mysql://localhost:3306/servlet_blog?characterEncoding=utf-8&useSSL=false";
private static String username = "root";
private static String password = "261919Zss";
private static volatile DataSource dataSource;
private static DataSource getDataSource(){
if (dataSource == null){
synchronized (DBUtil.class){
if (dataSource == null){
dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL(url);
((MysqlDataSource)dataSource).setUser(username);
((MysqlDataSource)dataSource).setPassword(password);
}
}
}
return dataSource;
}
//获取Connection对象
public static Connection getConnection(){
try {
return getDataSource().getConnection();
} catch (SQLException e) {
//返回自定义异常
throw new AppException("DBError001","数据库连接异常",e);
}
}
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
try {
if (resultSet != null){
resultSet.close();
}
if (statement != null){
statement.close();
}
if (connection != null){
connection.close();
}
} catch (SQLException e) {
throw new AppException("DBError002","数据库关闭资源失败",e);
}
}
public static void close(Connection connection, PreparedStatement statement){
close(connection, statement,null);
}
}
代码复用
创建一个servlet的抽象类,做好初始化工作,后面子类直接继承即可
import org.example.exception.AppException;
import org.example.model.JSONResponse;
import org.example.util.JSONUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 创建一个servlet的抽象类,做好初始化工作,后面子类直接继承即可
* */
public abstract class AbstractBaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求(响应)编码和方式
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
//请求数据类型为application/json,req.getInputStream获取请求体,数据作为输入流获取
//通过json框架反序列化为一个Java对象,请求数据要和对象数据类型一致
//服务端也返回application/json
resp.setContentType("application/json");
//使用req.getServletPath()获取请求路径
JSONResponse json = new JSONResponse();
try {
//调用子类重写的方法
Object data = process(req, resp);
//子类的process方法正常执行,没有异常
json.setSuccess(true);
json.setData(data);
}catch (Exception e){
//处理异常,如SQLException,JSON异常,使用自定义异常返回异常
e.printStackTrace();
String s = "未知错误";
String code = "UnknownError";
if (e instanceof AppException){
code = ((AppException)e).getCode();
s = e.getMessage();
}
//json.setSuccess默认值就是false,此处可以不用设置
json.setCode(code);
json.setMessage(s);
}
PrintWriter pw = resp.getWriter();
pw.write(JSONUtil.serialize(json));
pw.flush();
pw.close();
}
//模板方法,用来完成servlet初始化操作
protected abstract Object process(HttpServletRequest req,
HttpServletResponse resp) throws IOException;
}
servlet层的具体实现
注册功能
import org.example.dao.UserDao;
import org.example.exception.AppException;
import org.example.model.User;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/register")
public class RegisterServlet extends AbstractBaseServlet{
@Override
protected Object process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = UserDao.findUser(username);
if (user == null){
User newUser = new User();
newUser.setUsername(username);
newUser.setPassword(password);
UserDao.addUser(newUser);
}else {
throw new AppException("RegisterError","用户已存在");
}
return null;
}
}
登录功能
import org.example.dao.LoginDao;
import org.example.exception.AppException;
import org.example.model.User;
import javax.servlet.annotation.WebServlet;
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 AbstractBaseServlet{
@Override
protected Object process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//解析请求数据
String username = req.getParameter("username");
String password = req.getParameter("password");
//进行数据库用户名密码校验
User user = LoginDao.query(username);
if (user == null){
throw new AppException("Login002","用户不存在");
}
if (!user.getPassword() .equals(password)){
throw new AppException("Login003","用户名或者密码错误");
}
HttpSession session = req.getSession();
session.setAttribute("user",user);
return null;
}
}
新增博客功能
import org.example.dao.ArticleDao;
import org.example.model.Article;
import org.example.model.User;
import org.example.util.JSONUtil;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/articleAdd")
public class ArticleAddServlet extends AbstractBaseServlet{
@Override
protected Object process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
HttpSession session = req.getSession(false);
User user = (User)session.getAttribute("user");
//请求数据类型是application/json,需要用输入流
InputStream inputStream = req.getInputStream();
Article article = JSONUtil.deserialize(inputStream,Article.class);
article.setUserId(user.getId());
int num = ArticleDao.insert(article);
return null;
}
}
删除功能
@WebServlet("/articleDelete")
public class ArticleDeleteServlet extends AbstractBaseServlet{
@Override
protected Object process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String ids = req.getParameter("ids");
int num = ArticleDao.delete(ids.split(","));
return null;
}
}
修改博客内容功能
import org.example.dao.ArticleDao;
import org.example.model.Article;
import org.example.util.JSONUtil;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/articleDetail")
public class ArticleUpdateServlet extends AbstractBaseServlet{
@Override
protected Object process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//请求的格式是application/json,需要将其反序列化为一个Java对象
InputStream inputStream = req.getInputStream();
Article article = JSONUtil.deserialize(inputStream,Article.class);
int num = ArticleDao.update(article);
return null;
}
}
部分功能截图展示
项目源码以及前端源码链接:
github链接