博客简介
开发环境:idea 2019.03
数据库:MySQL
主要内容:实现一个迷你博客论坛项目
具体包括:项目背景、项目介绍、具体流程、实现代码、结果分析
项目实现
一、立项
项目名称: 迷你论坛
项目背景: 为了验证学习成果、印证知识点的掌握程度,模仿 “CSDN” 实现一个迷你博客论坛,实现其部分功能。主要模仿用户对博客类论坛的基础功能需求,目的是实现用户的注册、登录、对文章的查看、点赞、评论等操作,基于Java语言的特性和组件开发的特点,预留了其他的功能实现接口留待日后补充。
二、需求分析
核心需求: 查看文章、发布文章、评论文章、点赞文章
核心功能: 文章的管理
必要辅助功能: 用户管理(注册、登录、注销等)
三、可行性评估
需要完成的主要功能有:
1、用户注册
2、用户登录
3、查看文章列表(只给出Id,按照发表时间倒序显示)
4、发表文章(要求用户必须登录)
5、查看指定文章内容
6、发表评论(要求用户必须登录)
7、对指定文章进行点赞(要求用户必须登录)
四、数据建模
需要的数据模型:用户users、文章articles、评论表comment、点赞关系表like_relations
各模型属性:
Users: id(用户id )、nickname(显示用户名称)、username(登录用户昵称)、password(用户密码)
articles: author_id(发表文章作者的id)、title (文章题目)、published_at(发表时间)、content(文章内容)
like_relations: user_id(点赞者id)、article_id(文章id)
comments: id(自增id)、user_id(评论者id)、article_id(文章id)、published_at(发表时间)、content(评论内容)
五、代码实现
1、建库建表
--建库
create database shuju_boke charset utf8mb4;
use shuju_boke;
--建表
--用户表
create table users (
id int primary key auto_increment comment '自增id',
username varchar(200) not null unique comment '唯一的用户名',
nickname varchar(200) not null comment '显示名称',
password varchar(200) not null comment '登录密码'
);
--文章表
create table articles (
id int primary key auto_increment comment '自增id',
author_id int not null comment '作者id',
title varchar(200) not null comment '文章标题',
published_at datetime not null comment '文章发表时间',
content text not null comment '文章正题'
);
--评论表
create table comments (
id int primary key auto_increment comment '自增 id',
user_id int not null comment '评论者id',
article_id int not null comment '文章id',
published_at datetime not null comment '评论时间',
content varchar(200) not null comment '评论正文'
);
-- 点赞关系表
-- 使用成复合主键的形式
create table like_relations (
user_id int not null comment '评论者id',
article_id int not null comment '文章id',
primary key (user_id, article_id)
);
2、UI设计——菜单->用户选择的循环
3、Action(interface)抽象一个功能void run();
4、Main类的职责:
- 1.打印菜单
- 2.打印提示
- 3.获取用户输入
- 4.根据用户不同的输入,分发给不同的Action对象去处理用户的请求
package blog;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
// 当前的登录用户信息
// 没有登录 user == null
// 否则,指向具体的用户对象
//private static User user = null;
private static List<String> featureList = new ArrayList<>();//featureList功能的名称
private static List<Action> actionList = new ArrayList<>();//actionList功能对应的动作
private static void initFeatureList() {//功能
featureList.add("用户注册");
featureList.add("用户登录");
featureList.add("查看文章列表-按照发表时间倒序给出");
featureList.add("发表文章-要求先登录");
featureList.add("查看指定文章内容");
featureList.add("评论指定文章-要求先登录");
featureList.add("点赞指定文章-要求先登录");
}
private static void initActionList() {
actionList.add(new UserRegisterAction());//用户注册
actionList.add(new UserLoginAction());//用户登录
actionList.add(new ArticlelishAction());//文章列表
actionList.add(new ArticlePublishAction());//发表文章
actionList.add(new ArticleDetailAction());//
}
public static void main(String[] args) {//先初始化
initFeatureList();//功能名称
initActionList();//功能背后对应的动作
Scanner scanner = new Scanner(System.in);
while (true) {
showMenu();//打印菜单
showPrompt();//打印提示符
int select = scanner.nextInt();//获取用户输入
doAction(select);//根据用户不同的输入,分发给不同的Action对象去处理用户的请求(Dispatch分发)
}
}
private static void doAction(int select) {
if (select == 0) {
System.out.println("欢迎下次再来!");
System.exit(0);
}
System.out.println("您的选择是: " + featureList.get(select - 1));
if (select - 1 < actionList.size()) {
Action action = actionList.get(select - 1);
action.run();
} else {
System.out.println("该功能尚未支持,敬请期待...");
}
}
private static void showPrompt() {//提示
System.out.print("请输入功能的序号> ");
}
private static void showMenu() {//菜单
System.out.println("欢迎使用《迷你论坛》,支持以下功能");
for (int i = 0; i < featureList.size(); i++) {
System.out.printf(" %d. %s%n", i + 1, featureList.get(i));
}
System.out.println(" 0. 退出");
}
private static void userRegister() {//注册
System.out.println("开始用户注册");
}
}
菜单实现
功能1:用户注册
package blog;
import java.sql.*;
import java.util.Scanner;
//完整实现用户注册的功能
public class UserRegisterAction implements Action{
@Override
public void run() {
//1.提示用户输入需要的信息,并使用jdbc执行sql;
System.out.print("开始用户注册");
System.out.println();
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名称");
String username = scanner.nextLine();
System.out.print("请输入用户昵称");
String nickname = scanner.nextLine();
System.out.print("请输入用户密码");
String password = scanner.nextLine();
try(Connection connection = DBUtil.getConnection()) {
String sql = "insert into users(username,nickname,password) values(?,?,?)";
try (PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1,username);
statement.setString(2,nickname);
statement.setString(3,password);
statement.executeUpdate();
int id;
try(ResultSet r = statement.getGeneratedKeys()) {
r.next();
id = r.getInt(1);
}
System.out.println("注册成功,欢迎您的加入"+nickname);
//是否应该让刚注册的这个用户自动登录成功呢?好说让该用户重新登陆?
//两种方法都可以接受,我们选择自动登录成功
User user = new User();
user.id = id;//用户表中的id来自自增主键
user.nickname = nickname;
user.username = username;
User.login(user);
}
}catch (SQLException e) {
System.out.println("错误:" + e.getMessage());
}
}
}
运行代码,测试注册功能。经测试,注册功能成功实现
功能2:用户登录
package blog;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
//负责用户登录
public class UserLoginAction implements Action{
public void run() {
System.out.println("开始用户登录...");
System.out.println();
//读取用户输入的信息
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名");
String username = scanner.nextLine();
System.out.print("请输入密码");
String password = scanner.nextLine();
try(Connection c = DBUtil.getConnection()) {
//执行SQL,后面的??可以执行替换
String sql = "select id,nickname from users where username =? and password = ?";
try (PreparedStatement s = c.prepareStatement(sql)) {
s.setString(1,username);
s.setString(2,password);
try(ResultSet rs = s.executeQuery()) {
//因为username是unique
//所以查找的过程,要不返回1行数据,要不返回0 行数据
//不可能多个
if(rs.next()) {
int id = rs.getInt(1);
String nickname = rs.getString(2);
User user = new User();
user.id = id;
user.nickname = nickname;
user.username = username;
//进行登录
User.login(user);
}else {
System.out.println("用户名或者密码错误,请重新输入!!");
}
}
}
}catch (SQLException e) {
System.out.println("错误:"+e.getMessage());
}
//搞定用户登录的过程
//根据username+password判断用户是否登录成功
//select id,username,nickname from users where username = ? and password = ?
}
}
运行代码,测试登录功能。经测试,登录功能成功实现
功能3:发表文章
package blog;
import javax.naming.CompositeName;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class ArticlePublishAction implements Action {
//发表文章-要求先登录
@Override
public void run() {
if (!User.isLogined()) {
System.out.println("需要先登录,才能操作该功能!!");
return;
}
//和注册用户基本一致
//获取用户的输入(标题,正文)
//根据当前登录用户,获取作者id
//通过调用API,获取当前时间
System.out.println("发表文章中...");
Scanner scanner = new Scanner(System.in);
System.out.print("请输入文章标题");
String title = scanner.nextLine();
System.out.print("请输入文章正文");
String content = scanner.nextLine();
int authorId = User.getCurrentUser().id;
Date publishedAt = new Date();//new完的对象,本来就是当前时间
//published现在是Date对象,我们把Date对象format成String格式
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String publishedAtStr = format.format(publishedAt);
//现在信息已经获取完全,通过JDBC执行insert操作
try {
Connection c = DBUtil.getConnection();
Throwable var9 = null;
try {
String sql = "insert into articles (author_id, title, published_at, content) values (?, ?, ?, ?)";
PreparedStatement s = c.prepareStatement(sql);
Throwable var12 = null;
try {
s.setInt(1, authorId);
s.setString(2, title);
s.setString(3, publishedAtStr);
s.setString(4, content);
s.executeUpdate();
System.out.println("《" + title + "》 文章发表成功!");
} catch (Throwable var37) {
var12 = var37;
throw var37;
} finally {
if (s != null) {
if (var12 != null) {
try {
s.close();
} catch (Throwable var36) {
var12.addSuppressed(var36);
}
} else {
s.close();
}
}
}
} catch (Throwable var39) {
var9 = var39;
throw var39;
} finally {
if (c != null) {
if (var9 != null) {
try {
c.close();
} catch (Throwable var35) {
var9.addSuppressed(var35);
}
} else {
c.close();
}
}
}
} catch (SQLException var41) {
System.out.println("错误:" + var41.getMessage());
}
}
}
运行代码,测试发表功能。经测试,发表文章功能成功实现
查看mysql中的文章表,是否有新文章《下雨了》
功能4:查看文章列表
package blog;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
public class ArticlelishAction implements Action {
//查看文章列表-按照发表时间倒序给出
@Override
public void run() {
try(Connection c = DBUtil.getConnection()) {
List<String[]> articleList = new ArrayList<>();
List<String[]> authorIdList = new ArrayList<>();
String sql = "select id,author_id,title,published_at from articles order by published_at desc";
try(PreparedStatement s = c.prepareStatement(sql)) {
try(ResultSet rs = s.executeQuery()) {
while(rs.next()) {
String[] article = new String[5];
String id = rs.getString("id");
String authorId = rs.getString("author_id");
String title = rs.getString("title");
String publishedAt = rs.getString("published_at");
article[0] = id;
article[1] = authorId;
article[2] = title;
article[3] = publishedAt;
articleList.add(article);
}
}
}
//TODO打印作者的昵称而不是Id
//需要根据作者id,再次去users表中查询出 用户的昵称信息
Set<String> authorIdSet = new HashSet<>();
for (String[] article : articleList) {
String authorId = article[1];
authorIdSet.add(authorId);
}
String querySql = "select id,nickname from users where id in (";;
for(int i = 0; i < authorIdSet.size(); i++) {
querySql +="?,";
}
querySql += "?)";
System.out.println("DEBUG:"+ querySql.toString());
Map<String,String> userIdToNicknameMap = new HashMap<>();
try(PreparedStatement s = c.prepareStatement(querySql.toString())) {
int i = 1;
for(String id : authorIdSet) {
s.setString(i++,id);
}
try(ResultSet rs = s.executeQuery()) {
while(rs.next()) {
String id = rs.getString("id");
String nickname = rs.getString("nickname");
userIdToNicknameMap.put(id,nickname);
}
}
}
System.out.printf("#ID | 标题 | 作者 | 发表时间%n");
for(String[]article:articleList) {
String id = article[0];
String authorId = article[1];
String authorNickname = userIdToNicknameMap.get(authorId);
String title = article[2];
String publishedAt = article[3];
System.out.printf("%-4s | %-40s | %-10s | %s%n ",id,title,authorNickname,publishedAt);
}
} catch (SQLException e) {
System.out.println("错误:"+ e.getMessage());
}
}
}
运行代码,测试查看功能。经测试,查看功能成功实现
功能5/6:点赞文章、评论文章
package blog;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
public class ArticleDetailAction implements Action {
public ArticleDetailAction() {
}
public void run() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入要查看的文章的id :");
String id = scanner.nextLine();
String authorId = null;
String title = null;
String content = null;
String publishedAt = null;
int likeCount = 0;
try (Connection c = DBUtil.getConnection()) {
//1.查找文章信息
String queryArticleSql = "select id,author_id,title,content,published_at from articles where id = ?";
try(PreparedStatement s = c.prepareStatement(queryArticleSql)) {
s.setString(1,id);
try(ResultSet rs = s.executeQuery()) {
if(!rs.next()) {
System.out.println("没有这篇文章!!");
return;
}
authorId = rs.getString("author_id");
title = rs.getString("title");
content = rs.getString("content");
publishedAt = rs.getString("published_at");
}
}
//2.查询点赞的情况
String likeCountSql = "select count(*) from like_relations where article_id = ?";
try(PreparedStatement s = c.prepareStatement(likeCountSql)) {
s.setString(1,id);
try(ResultSet rs = s.executeQuery()) {
rs.next();
likeCount = rs.getInt(1);
}
}
//3.查询评论信息
List<String[]>commentList = new ArrayList<>();
String queryCommentSql = "select user_id,content,published_at from comments where article_id = ?";
try(PreparedStatement s = c.prepareStatement(queryCommentSql)) {
s.setString(1,id);
try(ResultSet rs = s.executeQuery()) {
while (rs.next()) {
String[] comment = new String[3];
comment[0] = rs.getString("user_id");
comment[1] = rs.getString("content");
comment[2] = rs.getString("published_at");
commentList.add(comment);
}
}
}
//4.根据用户id,查询用户昵称
//作者id和评论者id
Set<String>userIdSet = new HashSet<>();
userIdSet.add(authorId);
for(String[] comment : commentList) {
userIdSet.add(comment[0]);
}
StringBuilder queryNicknameSql = new StringBuilder("select id,nickname from users where id in (");
for(int i = 1;i<userIdSet.size();i++) {
queryNicknameSql.append("?, ");
}
queryNicknameSql.append("?)");
Map<String,String> userIdNicknameMap = new HashMap<>();
try(PreparedStatement s = c.prepareStatement(queryNicknameSql.toString())) {
int i = 1;
for(String userId : userIdSet) {
s.setString(i++,userId);
}
try(ResultSet rs = s.executeQuery()) {
while (rs.next()) {
userIdNicknameMap.put(rs.getString("id"),rs.getString("nickname"));
}
}
}
//5.根据所有的信息,进行打印
System.out.println(title);
System.out.println(userIdNicknameMap.get(authorId));
System.out.println(publishedAt);
System.out.println("点赞人数:" + likeCount);
System.out.println("=========================================");
System.out.println(content);
System.out.println("=========================================");
for(String[] comment : commentList) {
System.out.println(userIdNicknameMap.get(comment[0] + "|" + comment[1] + "|" + comment[2]));
}
} catch (SQLException e) {
System.out.println("错误:"+ e.getMessage());
}
}
}
运行代码,测试点赞和评论功能。经测试,点赞和评论功能成功实现
六概述
目前这个项目1.0已经完成实现,它还有许多可以进一步优化的地方,功能也可以再大幅增强,目前只实现基础功能,主要部分已经完成,基于语言的特性,以后可以进一步细化各种功能,尽可能打造一个完整的项目,作为自己学习的巩固和学习成果的检验。