1.分析需求
要做啥,要做到啥程度,不用做啥.
1)注册新用户
2)登陆已有用户
3)展示博客列表(每一项包含了文章的标题,作者)
点击标题就会跳转到文章详情页.
分页功能也暂时不考虑
4)文章详情页中,可以看到文章标题,作者,文章内容.
评论功能,分类功能,推荐相关文章功能等,先不考虑
5)发布新的博客
不考虑富文本编辑.
嵌入图片,嵌入视频,嵌入代码片段等.
6)删除自己的博客
2.创建项目. maven webapp
加入依赖: .
- Servlet API
- MySQL connector
3.数据库设计.
设计当前的代码中用到的数据库有几张表,每个表有哪些字段,每个表之间
又是啥关联关系.
1)提取出需求中的"实体" 有哪些
(确定了需要有
两张表User Article)
关键性的名词~~
2)想清楚实体需要关注的属性
用户
文章/博客
(确定两张表中,都需要有哪些
设计的数据库的表,就是需要
字段)
保存这些实体的.
每个实体,先分配- -张表
用户: .
名字
密码
文章:
标题
内容
3)想清楚,实体和实体之间的关系~~
a) user表中加入- -个userld字段,身份标识
b) Article表中加入一个userld字段,表示该文章是哪个用户发的.
最好把建表sq|保存起来,以备后用(部署到其他主机上时)
4.开始写代码
先实现数据库的一些基本操作~~
把两张表所涉及到的一些基本的增删改查操作, 稍微封装一下
a)创建一个类管理数据库的连接.
为了让后面的代码用起来更加方便.
~~
就可以吧管理数据库的类放到这里面。
package model;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
//管理数据库连接
//1.建立连接
//2.断开连接
//JDBC中使用DataSource来管理连接
//DBUtil相当于是对DataSource再稍微包装一层
//因为DataSource每个应用程序应该只有一个实例(单例)
//本质就是实现了一个单例模式,管理了唯一一个DataSource实例
//饿汉模式
//懒汉模式
//此处用懒汉模式
public class DBUtil {
private static DataSource dataSource = null;
private static final String URL = "jdbc:mysql://127.0.0.1:3306/java_blogdemo?characterEncoding=utf-8&useSSL=true";
private static final String USERNAME = "root";
private static final String PASSWORD = "xxxxxx";
public static DataSource getDataSource() {
if (dataSource == null) {
dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL(URL);
((MysqlDataSource)dataSource).setUser(USERNAME);
((MysqlDataSource)dataSource).setPassword(PASSWORD);
}
return dataSource;
}
}
懒汉实现的单例模式有一个重要的问题:
线程不安全~~
首次使用getDataSource,如果是多线程调用,就可能会产生线程不安全的问题.
如何保证线程安全?
1.加锁
2.双重if判定;
3. volatile ,
public class DBUtil {
private static volatile DataSource dataSource = null;
private static final String URL = "jdbc:mysql://127.0.0.1:3306/java_blogdemo?characterEncoding=utf-8&useSSL=true";
private static final String USERNAME = "root";
private static final String PASSWORD = "xxxxxx";
public 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;
}
}
//通过这个方法获取连接
public static Connection getConnection() {
try {
return getDataSource().getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//通过这个来断开连接
public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
b)创建实体类
创建一个User类和Article 类~
这俩类的属性,要和数据库表结构,是相关联的~~
package model;
public class User {
private int userId;
private String name;
private String password;
public int getUserId() {
return userId;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public void setUserId(int userId) {
this.userId = userId;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
}
package model;
public class Article {
private int articleId;
private String title;
private String content;
private int userId;
public int getArticleId() {
return articleId;
}
public String getTitle() {
return title;
}
public String getContent() {
return content;
}
public int getUserId() {
return userId;
}
public void setArticleId(int articleId) {
this.articleId = articleId;
}
public void setTitle(String title) {
this.title = title;
}
public void setContent(String content) {
this.content = content;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
c)实现数据库基本的增删改查
DAO表示数据访问层.
通过UserDao这个类来完成针对用户的数据库表操作.
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//UserDao是常见的命名方式,表示数据访问操作
public class UserDao {
//新增用户(注册)
//把一个User对象插入到数据库中
void add(User user) {
//1.获取数据连接
Connection connection = DBUtil.getConnection();
//2.拼装SQL语句
String sql = "insert into user values (null,?,?)";
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
statement.setString(1,user.getName());
statement.setString(2,user.getPassword());
//3.执行SQL语句
int ret = statement.executeUpdate();
if (ret != 1) {
System.out.println("插入用户失败");
return;
}
System.out.println("插入新用户成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
//4.释放数据库连接
DBUtil.close(connection,statement,null);
}
}
//按照名字查找用户(登录)
public User selectByName(String name) {
//建立连接
Connection connection = DBUtil.getConnection();
//拼装SQL
String sql = "select * from user where name = ?";
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
statement.setString(1,name);
//执行SQL
resultSet = statement.executeQuery();
//遍历结果集
if (resultSet.next()) {
User user = new User();
user.getUserId(resultSet.getInt("userId"));
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放连接
DBUtil.close(connection,statement,resultSet);
}
return null;
}
}
通过ArticleDao这个类完成针对文章的数据库表操作.
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 ArticleDao {
//新增文章(发布文章)
public void add(Article article) {
Connection connection = DBUtil.getConnection();
String sql = "insert into article values (null,?,?,?)";
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
statement.setString(1,article.getTitle());
statement.setString(2,article.getContent());
statement.setInt(3,article.getUserId());
int ret = statement.executeUpdate();
if (ret != 1) {
System.out.println("插入文章失败");
return;
}
System.out.println("插入文章成功");
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,null);
}
}
//查看文章列表
public List<Article> selectAll() {
List<Article> articles = new ArrayList<>();
Connection connection = DBUtil.getConnection();
String sql = "select articleId,title,userId from article";
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
while (resultSet.next()) {
Article article = new Article();
article.getArticleId(resultSet.getInt("articleId"));
article.setTitle(resultSet.getString("title"));
article.setUserId(resultSet.getInt("userId"));
articles.add(article);
}
return articles;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
//查看指定文章详情
public Article selectById(int articleId) {
Connection connection = DBUtil.getConnection();
String sql = "select * from article where articleId = ?";
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
statement.setInt(1,articleId);
resultSet = statement.executeQuery();
if (resultSet.next()) {
Article article = new Article();
article.getArticleId(resultSet.getInt("articleId"));
article.setTitle(resultSet.getString("title"));
article.setContent(resultSet.getString("content"));
article.setUserId(resultSet.getInt("userId"));
return article;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
//删除文章(给定ID来删除)
public void delete(int articleId) {
Connection connection = DBUtil.getConnection();
String sql = "delete from article where articleId = ?";
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
statement.setInt(3,articleId);
int ret = statement.executeUpdate();
if (ret != 1) {
System.out.println("删除文章失败");
return;
}
System.out.println("删除文章成功");
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection,statement,null);
}
}
}
单元测试
针对刚才写的这些数据库操作进行简单的测试.
'单元测试:把-些类/方法(拥有相对完整的功能),视为一个单元,针对这个单元进行测试.
我们这里先使用简单粗暴地方法进行,
实际进行单元测试的时候,也需要设计一-些测试用例.
“测试用例设计方法” =>等价类,边界值,因果图.
单元测试的核心是一-种思想方法(方法论) .不依赖语言,库,框架…
但是有一些库/框架可以让单元测试更方便,更正规. (JUint)
5.进行前后端接口设计.
约定,服务器都能接收啥样的请求,每个请求具体的格式如何,对应的响应结果又如何.
一自定制协议. (基于HTTP的基础上进行扩展)
后端和前端要并行开发.就需要把交互接口设计好了,再开发.
这个接口一旦确定了,就尽量要减少变更. 一旦变更,改动成本会比较大.
实现同一个功能可能涉及多种不同的约定方式
按照哪种方式约定,都是ok的.只要确定一种方案, 就好了~~
1)获取注册页面.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>注册</title>
</head>
<body>
<form action="register" method="post">
<input type="text" name="name" placeholder="请输入用户名">
<br/>
<input type="text" name="password" placeholder="亲输入密码">
<br/>
<input type="submit" value="注册">
</form>
</body>
</html>
2)实现注册功能(注册页面中提交的form表单数据,需要服务器处理)
package API;
import model.User;
import model.UserDao;
import view.HtmlGenerator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RegisterServlet extends HttpServlet {
//浏览器是通过POST方法提交注册信息给服务器的
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
//1.获取前端 提交数据校验是否合法
String name = req.getParameter("name");
String password = req.getParameter("password");
if(name == null || "".equals(name)
|| password == null || "".equals(password)) {
//用户提交的数据有误,返回一个错误页面(HTML)
String html = HtmlGenerator.getMessagePage("用户名或者密码为空","register.html");
resp.getWriter().write(html);
return;
}
//2.拿用户名在数据库中查看是否存在,存在则注册失败
UserDao userDao = new UserDao();
User existUser = userDao.selectByName(name);
if (existUser != null) {
String html = HtmlGenerator.getMessagePage("用户名重复,请换一个名字",
"register.html");
resp.getWriter().write(html);
return;
}
//3.根据提交的对象构造user对象并插入到数据库中
User user = new User();
user.setName(name);
user.setPassword(password);
userDao.add(user);
//4.返回一个结果页面,提示注册成功
String html = HtmlGenerator.getMessagePage("注册成功,点击跳转到登录页面",
"login.html");
resp.getWriter().write(html);
}
}
package view;
public class HtmlGenerator {
//通过字符串拼接形式,构造一个html
public static String getMessagePage(String message, String nextUrl) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("<html>");
stringBuilder.append("<head>");
stringBuilder.append("<meta charset=\"utf-8\">");
stringBuilder.append("<title>提示页面</title>");
stringBuilder.append("</head>");
stringBuilder.append("<body>");
stringBuilder.append("<h3>");
stringBuilder.append(message);
stringBuilder.append("</h3>");
stringBuilder.append(String.format("<a href=\"%s\"> 快来点我鸭</a>",
nextUrl));
stringBuilder.append("</body>");
stringBuilder.append("</html>");
return stringBuilder.toString();
}
}
**3)获取登陆页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="login" method="post">
<input type="text" name="name" placeholder="请输入用户名">
<br/>
<input type="text" name="password" placeholder="亲输入密码">
<br/>
<input type="submit" value="登录">
</form>
</body>
</html>
4)实现登陆功能(登陆页面提交的数据,需要服务器来处理
package API;
import model.User;
import model.UserDao;
import view.HtmlGenerator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
String name = req.getParameter("name");
String password = req.getParameter("password");
if(name == null || "".equals(name)
|| password == null || "".equals(password)) {
//用户提交的数据有误,返回一个错误页面(HTML)
String html = HtmlGenerator.getMessagePage("用户名或者密码为空","login.html");
resp.getWriter().write(html);
return;
}
UserDao userDao = new UserDao();
User user = userDao.selectByName(name);
if(user == null || !password.equals(user.getPassword())) {
String html = HtmlGenerator.getMessagePage("用户名或者密码错误",
"login.html");
resp.getWriter().write(html);
return;
}
HttpSession httpSession = req.getSession(true);
httpSession.setAttribute("user",user);
String html = HtmlGenerator.getMessagePage("登录成功","article");
resp.getWriter().write(html);
}
}
5)获取文章列表.
6)获取文章详细内容
package API;
import model.Article;
import model.ArticleDao;
import model.User;
import model.UserDao;
import view.HtmlGenerator;
import javax.servlet.ServletException;
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;
public class ArticleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
//验证是否登录
HttpSession httpSession = req.getSession(false);
if (httpSession == null) {
String html = HtmlGenerator.getMessagePage("请先登录",
"login.html");
resp.getWriter().write(html);
return;
}
//登录成功则根据session获取用户信息
User user =(User)httpSession.getAttribute("user");
String articleIdStr = req.getParameter("articleId");
if (articleIdStr == null) {
getAllArticle(user,resp);
}else {
getOneArticle(Integer.parseInt(articleIdStr),user,resp);
}
}
private void getOneArticle(int articleId, User user, HttpServletResponse resp) throws IOException {
ArticleDao articleDao = new ArticleDao();
Article article = articleDao.selectById(articleId);
if (article == null) {
String html = HtmlGenerator.getMessagePage("文章不存在",
"article");
resp.getWriter().write(html);
return;
}
UserDao userDao = new UserDao();
User author = userDao.selectById(article.getUserId());
String html = HtmlGenerator.getArticleDetailPage(article,user,author);
resp.getWriter().write(html);
}
private void getAllArticle(User user, HttpServletResponse resp) throws IOException {
//查找数据库
ArticleDao articleDao = new ArticleDao();
List<Article> articles = articleDao.selectAll();
String html = HtmlGenerator.getArticleListPage(articles,user);
resp.getWriter().write(html);
}
}
public static String getArticleListPage(List<Article> articles, User user) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("<html>");
stringBuilder.append("<head>");
stringBuilder.append("<meta charset=\"utf-8\">");
stringBuilder.append("<title>提示页面</title>");
stringBuilder.append("<style>");
stringBuilder.append("a {" +
"color: #333;" +
"text-decoration: none;"+
"display: inline-block;"+
"width: 200px;"+
"height: 50px;"+
"}");
stringBuilder.append("a:hover {"+
"color: white;"+
"background-color:orange;"+
"}");
stringBuilder.append("body {"+
"background-image: url(\"http://p3.gexing.com/shaitu/20130202/2106/510d0f6a91760.jpg\");"+
"background-repeat: none;"+
"background-position: 0 center;"+
"}");
stringBuilder.append("</style>");
stringBuilder.append("</head>");
stringBuilder.append("<body>");
stringBuilder.append("<h3> 欢迎您! "+user.getName()+"</h3>");
stringBuilder.append("<hr>");
for (Article article:articles) {
stringBuilder.append(String.format("<div style=\"width: 200px; height: 50px line-height: 50px\"> <a href=\"article?articleId=%d\"> %s </a></div>",
article.getArticleId(),article.getTitle()));
}
stringBuilder.append("<hr>");
stringBuilder.append(String.format("<div>当前共有博客 %d 篇</div>",articles.size()));
stringBuilder.append("</body>");
stringBuilder.append("</html>");
return stringBuilder.toString();
}
public static String getArticleDetailPage(Article article, User user, User author) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("<html>");
stringBuilder.append("<head>");
stringBuilder.append("<meta charset=\"utf-8\">");
stringBuilder.append("<title>提示页面</title>");
stringBuilder.append("<style>");
stringBuilder.append("body {"+
"background-image: url(\"http://p3.gexing.com/shaitu/20130202/2106/510d0f6a91760.jpg\");"+
"background-repeat: none;"+
"background-position: 0 center;"+
"}");
stringBuilder.append("</style>");
stringBuilder.append("</head>");
stringBuilder.append("<body>");
stringBuilder.append("<h3> 欢迎您! "+user.getName()+"</h3>");
stringBuilder.append("<hr>");
stringBuilder.append(String.format("<h1>%s</h1>",article.getTitle()));
stringBuilder.append(String.format("<h4>作者:%s</h4>",author.getName()));
stringBuilder.append(String.format("<div>%s</div>",article.getContent()));
stringBuilder.append("</body>");
stringBuilder.append("</html>");
return stringBuilder.toString();
}
7)新增/发布文章
发布界面
stringBuilder.append(String.format("<div>当前共有博客 %d 篇</div>",articles.size()));
stringBuilder.append("<br>");
stringBuilder.append("<div>发布文章</div>");
stringBuilder.append("<div>");
stringBuilder.append("<form method=\"post\" action=\"article\" >");
stringBuilder.append("<input type=\"text\" name=\"title\" placeholder=\"请输入标题\">");
stringBuilder.append("<br>");
stringBuilder.append("<textarea name=\"content\" style=\"width: 500px;height: 300px;\"></textarea>");
stringBuilder.append("<br>");
stringBuilder.append("<input type=\"submit\" value=\"发布文章\">");
stringBuilder.append("</form>");
stringBuilder.append("</div>");
//实现发布文章的逻辑
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html; charset=utf-8");
//验证是否登录
HttpSession httpSession = req.getSession(false);
if (httpSession == null) {
String html = HtmlGenerator.getMessagePage("请先登录",
"login.html");
resp.getWriter().write(html);
return;
}
User user = (User)httpSession.getAttribute("user");
String title = req.getParameter("title");
String content = req.getParameter("content");
if (title == null || "".equals(title)
|| content == null || "".equals(content)) {
String html = HtmlGenerator.getMessagePage("提交的标题和正文为空",
"article");
resp.getWriter().write(html);
return;
}
Article article = new Article();
ArticleDao articleDao = new ArticleDao();
article.setTitle(title);
article.setContent(content);
article.setUserId(user.getUserId());
articleDao.add(article);
String html = HtmlGenerator.getMessagePage("发布成功","article");
resp.getWriter().write(html);
return;
}
8)删除文章**
import model.Article;
import model.ArticleDao;
import model.User;
import view.HtmlGenerator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class DeleteArticleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
HttpSession httpSession = req.getSession(false);
if(httpSession == null) {
String html = HtmlGenerator.getMessagePage("您尚未登录","login.html");
resp.getWriter().write(html);
return;
}
User user = (User)httpSession.getAttribute("user");
String articleIdStr = req.getParameter("articleId");
if (articleIdStr == null || "".equals(articleIdStr)) {
String html = HtmlGenerator.getMessagePage("要删除的文章ID不存在","article");
resp.getWriter().write(html);
return;
}
ArticleDao articleDao = new ArticleDao();
Article article = articleDao.selectById(Integer.parseInt(articleIdStr));
if (article.getUserId() != user.getUserId()) {
String html = HtmlGenerator.getMessagePage("您只能删除自己的文章","article");
resp.getWriter().write(html);
return;
}
articleDao.delete(Integer.parseInt(articleIdStr));
String html = HtmlGenerator.getMessagePage("删除成功","article");
resp.getWriter().write(html);
}
}
到这里一些简单的功能就实现啦。