项目功能
- 文章列表(所有人的文章列表、个人文章列表)
- 文章详情
- 添加文章
- 修改文章
- 删除文章
- 用户注册
- 用户登录
- 文章阅读量统计
设计数据库和数据表
创建并使用数据库
用户表
- id
- 登录用户名(只能输入英文,不能输入中文)
- 密码
- 创建时间
- 修改时间
- 状态(扩展)
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
);
文章表
- 文章id
- 文章标题
- 正文
- UID(是谁发的文章)
- 状态
- 创建时间
- 修改时间
- 阅读量
drop table if exists articleinfo;
create table articleinfo (
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime datetime default now(),
updatetime datetime default now(),
uid int not null,
rcount int not null default 1,
`state` int default 1
);
创建Servlet项目
在pom.xml里面配置servlet,
确认是否有相应的引用
创建java源代码的根路径
在web.xml里面配置servlet的配置文件
这样的操作结束之后,Servlet项目就算正式创建完毕了
引入jdbc,这样才可以操作mysql数据库
点击导入之后判断到底添加成功没有
功能实现
数据库公共方法类DBUtils
/**
* 通用数据库操作类
* 1.对外提供connect 对象
* 2.提供统一的数据库的关闭方法
*/
public class DBUtils {
private static MysqlDataSource dataSource = null;
/**
* 对外提供统一的连接对象
* @return
* @throws SQLException
*/
public static Connection getConnect() throws SQLException {
if (dataSource == null) {
//说明是首次调用,先初始化
dataSource = new MysqlDataSource();
dataSource.setURL("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf-8&useSSL=true"); //设置连接的服务器的地址
dataSource.setUser("root");
dataSource.setPassword("111111");
}
return dataSource.getConnection();
}
public static void close (Connection connection, Statement statement, ResultSet resultSet) throws SQLException {
if (resultSet != null) connection.close();
if (statement != null) statement.close();
if (connection != null) connection.close();
}
}
后端公共的返回方法类ResultJSONUtils
后端操作关键:后端给前端返回数据的时候是response通过printWriter将后端的数据传给前端的,但是由于拼接json的时候很容易出错,所以引入了Jackson(操作json的库)
public static void write(HttpServletResponse response, HashMap<String,Object> map) throws IOException {
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
PrintWriter writer = response.getWriter();
ObjectMapper mapper = new ObjectMapper();
writer.println(mapper.writeValueAsString(map));
}
models下的实体类(用户,文章)
public class UserInfo {
private int id;
private String username;
private String password;
private Date createTime;
private Date updateTime;
private int state;
}
package models;
import java.util.Date;
/**
* articleInfo实体类
*/
public class ArticleInfo {
private int id;
private String title;
private String content;
private Date createTime;
private Date updateTime;
private int rCount;
private int uid;
private int state;
}
前后端交互的准备工作
前端请求的关键:ajax -> jQuery.getJSON()
写前端页面之前首先引入jquery
- 使用jQuery.getJSON(" 请求地址",{传给后端的参数}, function(data){ })可以进行前后端交互
- 其中" "里面是调用后端的地址
- { }里面放的是提交给后端时候的参数,传递的参数必须是json格式
- function(data){} 是后端返回给前端的参数(JSON对象)
Servlet中使用外部JS的步骤
1.将JS复制到webapp的目录下
2.设置Servlet的属性
点击File,Project Structure
关键能力
- 前端调试技能 Google 控制台
- 后端调试技能 idea debug
用户注册
用户注册的前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客注册</title>
<script src="jquery-1.9.1.min.js"></script>
<script>
function mysub(type) {
var username = jQuery("#username");
var password = jQuery("#password");
var password2 = jQuery("password2");
if (type == 2) {
//点击清空按钮
if (confirm("是否确认清空")) {
//执行清空操作
username.val("");
password.val("");
password2.val("");
}
}
if (username.val().trim() =="") {
alert("请先输入用户名");
username.focus();
return false;
}
if (password.val().trim() =="") {
alert("请先输入密码");
password.focus();
return false;
}
if (password2.val().trim() =="") {
alert("请先输入确认密码");
password2.focus();
return false;
}
//判断密码和确认密码是否一致
if (password.val() != password2.val()) {
alert("两次输入的密码不一致,请先检查");
password.focus();
return false;
}
//使用jquery提交信息给后端
jQuery.getJSON("reg",{ //reg代表传递给后端的谁
"username":username.val(),
"password":password.val()
},function (data) {
if (data != null && data.state==200) {
//说明请求成功
alert("注册成功!");
} else {
alert("注册失败,请重试!");
}
})
}
</script>
</head>
<body>
<div style="text-align: center; margin-top: 100px">
<h1>注册</h1>
用 户 名:<input id="username" name="username" type="text"> <p></p>
密     码:<input id="password" name="password" type="password"> <p></p>
确认密码:<input id="password2" name="password2" type="password"> <p></p>
<input type="button" value="提交" onclick="mysub(1)">
<input type="button" value="清空" onclick="mysub(2)">
</div>
</body>
</html>
前端页面完成之后配置路由,生成注册方法
根据前端jquery提交的数据找到路由里面的reg,进而找到后端类RegServlet
用户注册的后端代码
接下来写后端类中的注册方法
dao层里面操作数据库
//查询数据库是存在这个用户名的用户
public boolean query(UserInfo userInfo) throws SQLException {
boolean flag = false;
if (userInfo.getUsername() != null && userInfo.getPassword() != null) {
Connection connection = DBUtils.getConnect();
String sql = "select * from userinfo where username = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,userInfo.getUsername());
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
flag = true;
}
DBUtils.close(connection,statement,resultSet);
}
return flag;
}
//用户注册
public int add(UserInfo userInfo) throws SQLException {
int num = 0;
if (userInfo.getUsername() != null && userInfo.getPassword() != null) {
Connection connection = DBUtils.getConnect();
String sql = "insert into userinfo(username,password) values (?,?)";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,userInfo.getUsername());
statement.setString(2,userInfo.getPassword());
num = statement.executeUpdate();
DBUtils.close(connection,statement,null);
}
return num;
}
登录功能
登录功能的前端代码
location.href = “artlist.html”; 登录成功之后跳转到这个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
<script src="jquery-1.9.1.min.js"></script>
<script>
function mysub(type) {
var username = jQuery("#username");
var password = jQuery("#password");
if (type == 2) {
if (confirm("确认清空?")) {
username.val("");
password.val("");
username.focus();
}
return false;
}
if (username.val().trim() =="") {
alert("请先输入用户名");
username.focus();
return false;
}
if (password.val().trim() =="") {
alert("请先输入密码");
password.focus();
return false;
}
jQuery.getJSON("login",{
"username":username.val(),
"password":password.val()
},function (data) {
if (data != null && data.state == 200) {
//alert("登录成功");
//登录成功之后跳转到文章列表的页面
location.href = "artlist.html"
} else {
alert("登陆失败,"+data.msg);
}
})
}
</script>
</head>
<body>
<div style="margin-top: 100px; text-align: center">
<h1>登录</h1>
用户名:<input id="username" name="username"> <p></p>
密    码:<input id="password" name="password"><p></p>
<input type="button" value="登录" onclick="mysub(1)">
<input type="button" value="清空" onclick="mysub(2)">
</div>
</body>
</html>
登录功能的后端代码
public static boolean isLogin(UserInfo userInfo) throws SQLException {
boolean flag = false;
if (userInfo.getUsername() != null && userInfo.getPassword() != null) {
Connection connection = DBUtils.getConnect();
String sql = "select * from userinfo where username = ? and password = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,userInfo.getUsername());
statement.setString(2,userInfo.getPassword());
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
flag = true;
}
DBUtils.close(connection,statement,resultSet);
}
return flag;
}
展示我的文章
我的文章列表的前端页面代码
点击登录之后直接跳转到登陆人的文章的列表!!!
注意我的文章的前端页面展示的时候用表格的形式展示出来,表格展示的时候用到了新的前端页面的展示方法: table
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的文章列表</title>
<script src="jquery-1.9.1.min.js"></script>
<script>
jQuery(function () {
jQuery.getJSON("myartlist",
{
},function (data) {
if (data != null && data.list != null) {
//代表查询成功
//查询成功之后就在页面进行拼接
var html = "";
for (var i = 0; i < data.list.length; i++) {
var articleinfo = data.list[i];
html += "<tr>" +
"<td>" + articleinfo.id+ "</td>" +
"<td>" + articleinfo.title + "</td>" +
"<td>" + articleinfo.createTime + "</td>" +
"<td>" + articleinfo.rCount + "</td>" +
"<td>" +"修改 删除" + "</td>" +
"</tr>"
}
jQuery("#tab").append(html);
} else {
//查询失败
alert("查询失败"+data.msg);
}
})
});
</script>
</head>
<body>
<div style="text-align: center; margin-top: 50px;">
<h1>我的文章列表</h1>
<table id="tab" style="width: 80%; margin-left: 100px" border="1" cellspacing="0">
<tr>
<td>文章编号</td>
<td>标题</td>
<td>创建时间</td>
<td>阅读量</td>
<td>操作</td>
</tr>
</table>
</div>
</body>
</html>
并且在后端查找文章的时候需要获取到是谁登录的,然后根据登录的人的id获得他的所有的文章列表
所以在登录的时候需要创建session,然后在查询文章列表的时候用到session获取文章的创建者的id
后端代码
public List<ArticleInfo> getMyArtList(int uid) throws SQLException {
List<ArticleInfo> list = new ArrayList<>();
Connection connection = DBUtils.getConnect();
String sql = "select * from articleinfo where uid = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,uid);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
ArticleInfo articleInfo = new ArticleInfo();
articleInfo.setId(resultSet.getInt("id"));
articleInfo.setTitle(resultSet.getString("title"));
articleInfo.setContent(resultSet.getString("content"));
articleInfo.setCreateTime(resultSet.getTime("createtime"));
articleInfo.setUpdateTime(resultSet.getTime("updatetime"));
articleInfo.setrCount(resultSet.getInt("rcount"));
articleInfo.setState(resultSet.getInt("state"));
list.add(articleInfo);
}
return list;
}
删除功能
前端页面的代码
前端的代码就在我的文章列表的基础上面在删除上面添加一个js事件
a标签
然后实现一个mydel方法
//文章删除操作
function mydel(id){
if (confirm("确认删除?")) {
jQuery.getJSON("mydel",{
"id":id
},function (data) {
if (data != null && data.state == 200) {
//删除成功,进行刷新界面
location.href = location.href; //刷新当前页面
} else {
//删除失败
alert("删除失败:"+data.msg);
}
})
}
}
location.href = location.href; //刷新当前页面
后端代码
//删除文章
public int delArticleById(int id) throws SQLException {
int result = 0;
Connection connection = DBUtils.getConnect();
String sql = "delete from articleinfo where id = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,id);
result = statement.executeUpdate();
DBUtils.close(connection,statement,null);
return result;
}
添加文章
前端代码
添加文章的功能是从我的文章的列表页面跳转过来的
文章的正文的标签用的不再是text,而是textarea
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加文章</title>
<script src="jquery-1.9.1.min.js"></script>
<script>
function mysub(type) {
var title = jQuery("#title");
var content = jQuery("#content");
if (type == 2) {
if (confirm("确认清空?")){
title.val("");
content.val("");
}
return false;
}
if (title.val().trim()=="") {
alert("请输入标题");
title.focus();
return false;
}
if (content.val().trim()=="") {
alert("请输入正文");
content.focus();
return false;
}
jQuery.getJSON("addart",{
"title":title.val(),
"content":content.val()
},function (data) {
if (data != null && data.state == 200) {
alert("添加成功");
} else {
alert("添加失败:"+data.msg);
}
})
}
</script>
</head>
<body>
<div style="margin-left: 50px; margin-top: 70px">
<h1>添加文章</h1>
标题:<input type="text", id="title" name="title"> <p></p>
正文:<p></p><textarea id="content" name="content" style="width: 50%; height: 300px"></textarea>
<p></p>
<input type="button" onclick="mysub(1)" value="提交">
<input type="button" onclick="mysub(2)" value="清空">
</div>
</body>
</html>
后端代码
添加文章的时候也要用到用户的id,用户的id是从session里面取到的
//添加文章
public int add(String title, String content, int uid) throws SQLException {
int result = 0;
Connection connection = DBUtils.getConnect();
String sql = "insert into articleinfo(title,content,uid) values (?,?,?)";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,title);
statement.setString(2,content);
statement.setInt(3,uid);
result = statement.executeUpdate();
DBUtils.close(connection,statement,null);
return result;
}
修改文章
修改文章的操作有两步
- 1.先查询出来文章的内容
- 2.在查询出来的文章的内容的基础上进行修改
修改文章的时候需要获取文章的id,所以在前端我的页面点击修改的时候就要将文章的id传递过去
从url里面获取文章的id
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章修改</title>
<script src="jquery-1.9.1.min.js"></script>
<script>
var id = 0;
jQuery(function () {
//先获取文章的id ?id=×××
var thisParameter = location.search.substring(1);
id = thisParameter.split("=")[1];
jQuery.getJSON("initart",{
"id":id
},function (data) {
if (data != null && data.state == 200) {
var articleinfo = data.art;
jQuery("#title").val(articleinfo.title);
jQuery("#content").val(articleinfo.content);
} else {
alert("查询失败:"+data.msg);
}
})
})
function mysub(type) {
var title = jQuery("#title");
var content = jQuery("#content");
if (type == 2) {
if (confirm("确认清空?")){
title.val("");
content.val("");
}
return false;
}
if (title.val().trim()=="") {
alert("请输入标题");
title.focus();
return false;
}
if (content.val().trim()=="") {
alert("请输入正文");
content.focus();
return false;
}
jQuery.getJSON("upart",{
"id":id,
"title":title.val(),
"content":content.val()
},function (data) {
if(data != null && data.state == 200) {
location.href = "artlist.html"
} else {
alert("修改失败:"+data.msg);
}
})
}
</script>
</head>
<body>
<div style="margin-left: 50px; margin-top: 70px">
<h1>文章修改</h1>
标题:<input type="text", id="title" name="title"> <p></p>
正文:<p></p><textarea id="content" name="content" style="width: 50%; height: 300px"></textarea>
<p></p>
<input type="button" onclick="mysub(1)" value="提交">
<input type="button" onclick="mysub(2)" value="清空">
</div>
</body>
</html>
后端代码
//查询文章的具体内容
public ArticleInfo getArtById(int id) throws SQLException {
ArticleInfo articleInfo = new ArticleInfo();
Connection connection = DBUtils.getConnect();
String sql = "select * from articleinfo where id = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
articleInfo.setTitle(resultSet.getString("title"));
articleInfo.setContent(resultSet.getString("content"));
}
DBUtils.close(connection,statement,resultSet);
return articleInfo;
}
//修改文章的操作
public int update(String title, String content, int id) throws SQLException {
int result = 0;
Connection connection = DBUtils.getConnect();
String sql = "update articleinfo set title = ? ,content = ? where id = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,title);
statement.setString(2,content);
statement.setInt(3,id);
result = statement.executeUpdate();
DBUtils.close(connection,statement,null);
return result;
}
文章详情展示
前端代码
前端的代码就是在文章列表的文章标题里面加一个跳转的功能,跳转到文章的详情页
后端代码
后端的代码就是在进行修改文章的时候先进行查询文章的内容的代码上面进行修改
String sql = "select a.*,b.username from articleinfo a left join userinfon b on a.uid = b.id and a.id = ?";
//查询文章的具体内容
public ArticleInfoVO getArtById(int id) throws SQLException {
ArticleInfoVO articleInfoVO = new ArticleInfoVO();
Connection connection = DBUtils.getConnect();
String sql = "select a.*,b.username from articleinfo a left join userinfon b on a.uid = b.id and a.id = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
articleInfoVO.setTitle(resultSet.getString("title"));
articleInfoVO.setContent(resultSet.getString("content"));
articleInfoVO.setCreateTime(resultSet.getDate("createtime"));
articleInfoVO.setrCount(resultSet.getInt("rcount"));
articleInfoVO.setUsername(resultSet.getString("username"));
}
DBUtils.close(connection,statement,resultSet);
return articleInfoVO;
}
文章阅读量统计
前端
后端
所有文章列表
前端的页面和个人文章列表几乎完全相同,就不再这里展示了
后端代码
后端查询所有的文章列表的时候用到了一个联合查询,联合查询主要是为了查询到文章的作者是谁
//获取所有人的文章列表
public List<ArticleInfoVO> getList() throws SQLException {
List<ArticleInfoVO> list = new ArrayList<>();
Connection connection = DBUtils.getConnect();
String sql = "select a.*,u.username from articleinfo a left join userinfo u on a.uid = u.id";
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
ArticleInfoVO articleInfoVO = new ArticleInfoVO();
articleInfoVO.setTitle(resultSet.getString("title"));
articleInfoVO.setId(resultSet.getInt("id"));
articleInfoVO.setCreateTime(resultSet.getTime("createtime"));
articleInfoVO.setUsername(resultSet.getString("username"));
articleInfoVO.setrCount(resultSet.getInt("rcount"));
list.add(articleInfoVO);
}
DBUtils.close(connection,statement,resultSet);
return list;
}
分页功能
- limit x,y ; 跳过前x条,查询y条数据
- N=当前页, Y=查询条数,
- 分页公式:(N-1)* Y
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章列表</title>
<script src="jquery-1.9.1.min.js"></script>
<script>
var currpage = 1; //全局参数,当前页
//时间格式化
function datefm(timespam) {
var date = new Date(timespam);
var year = date.getFullYear();
var month = date.getMonth()+1;
var day = date.getDay();
return year+"-"+month+"-"+day;
}
jQuery(function () {
var myparams = location.search; //没有参数代表在第一页
if (myparams != "") {
currpage = parseInt(myparams.split("=")[1]);
}
jQuery.getJSON("list",{
//传入当前页码
"cpage":currpage,
"psize":2 //每页显示几条数据
}, function (data) {
var html = "";
if (data != null && data.state == 200) {
for (var i = 0; i < data.list.length; i++) {
var art = data.list[i];
html += "<tr>" +
"<td>"+art.id+"</td>"+
"<td>"+art.title+"</td>"+
"<td>"+datefm(art.createTime)+"</td>"+
"<td>"+art.rCount+"</td>"+
"<td>"+art.username+"</td>"+
"</tr>"
}
jQuery("#tab").append(html);
} else {
alert("查询失败"+data.msg);
}
})
});
//分页功能
function mypage(type) {
if (type == 1) {
//上一页
if (currpage > 1) {
//正常进入上一页
location.href = "list.html?c="+(currpage-1);
} else {
alert("已经是第一页");
}
} else {
//下一页
location.href = "list.html?c="+(currpage+1);
}
}
</script>
</head>
<body>
<div style="text-align: center">
<h1>所有文章列表</h1>
<table id="tab" style="width: 80%; margin-left: 100px" border="1" cellspacing="0">
<tr>
<td>文章标号</td>
<td>标题</td>
<td>创建时间</td>
<td>阅读量</td>
<td>作者</td>
</tr>
</table>
<hr>
<a href="javascript:mypage(1)">上一页</a> <a href="javascript:mypage(2)">下一页</a>
</div>
</body>
</html>
后端代码
这里的后端代码和所有文章列表的后端代码几乎一样,只有查询数据库的时候的sql语句用了limit来进行分页的查询
String sql = "select a.*,u.username from articleinfo a left join userinfo u on a.uid = u.id limit ?,?";
Servlet 开发方式:
-
xml 配置方式【web.xml配置,创建Servlet后端类】
-
Servlet 3.0 注解开发
注解开发的方式 1. web.xml -> metadata-complete(当前web.xml的配置文件是否是完整的路由) 设置为 false 2. 创建Servlet类,并添加 @WebServlet("/路由地址") 注意 / 不要忘记
在这个项目的开发过程中我配置路由的方式都是在web.xml里面添加路由