ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

JavaWeb阶段性项目2:QQZone项目梳理

2022-08-15 22:35:06  阅读:221  来源: 互联网

标签:11 JavaWeb 项目 KEY basic NULL id QQZone


前置知识

前置准备

知识准备

已掌握JavaSE/MySQL/JDBC+HTML/CSS/JavaScript基础

并已完成了Javaweb前置知识的学习

01-JavaWeb-HTML初识

02-JavaWeb-CSS初识

03-JavaWeb-JavaScript初识

04-JavaScript基础应用-鼠标悬浮/离开表格格式变化

05-JavaWeb-Tomcat8安装、Servlet初识

06-JavaWeb-Servlet方法/生命周期、HTTP/会话session

07-JavaWeb-视图模板技术Thymeleaf的使用

08-JavaWeb-Servlet保存作用域

09-JavaWeb-阶段性项目1:最简单的后台库存管理系统

  10-JavaWeb阶段性项目1:系统的servlet优化1 

  11-JavaWeb阶段性项目1:系统的servlet优化2

  12-JavaWeb阶段性项目1:系统的servlet优化3

  13-JavaWeb阶段性项目1:系统的servlet优化4

  14-JavaWeb阶段性项目1:系统的servlet优化5

15-JavaWeb阶段性项目1:Servlet-api、mvc-service引入、IOC和DI

资源准备

尚硅谷丨2022版JavaWeb教程视频

教学资源

https://pan.baidu.com/s/1TS7QJ_a2vHHmXkggAs8RMQ

提取码:yyds


本文已经完成了视频P55前的所有内容,接下来是一个练手项目,可以很好的巩固之前视频的知识,同时更接近真实项目开发,从理解需求开始,进行一个简单Javaweb项目的设计与实现——尚硅谷2022Javaweb视频教程P56-QQZone项目

以下主要是实现过程及思路的整理,不过多涉及代码如何实现。

1 需求分析

 视频中需求
 
 首先熟悉需求
​
    1) 用户登录
    2) 登录成功,显示主界面。左侧显示好友列表;上端显示欢迎词。如果不是自己的空间,显示超链接:返回自己的空间;下端显示日志列表
    3) 查看日志详情:
       - 日志本身的信息(作者头像、昵称、日志标题、日志内容、日志的日期)
       - 回复列表(回复者的头像、昵称、回复内容、回复日期)
       - 主人回复信息
    4) 删除日志
    5) 删除特定回复
    6) 删除特定主人回复
    7) 添加日志、添加回复、添加主人回复
    8) 点击左侧好友链接,进入好友的空间

需求分析需要对所进行项目的相关实际业务非常了解,确保深入理解了用户端的业务逻辑、表单结构、管理规范等。

用原型图的方式展示给用户以了解和挖掘用户的真实需求,重点是确认需求不会再有更改(如果是外包可以通过签订合同,如果是自研可以走OA流程最终确定)

2 数据库设计

2.1 概念结构设计

1) 抽取实体 : 用户登录信息、用户详情信息 、 日志 、 回贴  、 主人回复
2) 分析其中的属性:
  - 用户登录信息:账号、密码、头像、昵称
  - 用户详情信息:真实姓名、星座、血型、邮箱、手机号.....
  - 日志:标题、内容、日期、作者
  - 回复:内容、日期、作者、日志
  - 主人回复:内容、日期、作者、回复
3) 分析实体之间的关系
  - 用户登录信息 : 用户详情信息      1:1 PK
  - 用户 : 日志                   1:N
  - 日志 : 回复                   1:N
  - 回复 : 主人回复                1:1 UK
  - 用户 : 好友                   M : N

按照规范设计,我们将数据库的设计过程分为六个阶段:

  • 系统需求分析阶段;

  • 概念结构设计阶段;

  • 逻辑结构设计阶段;

  • 物理结构设计阶段;

  • 数据库实施阶段;

  • 数据库运行与维护阶段;

这里只进行了业务级别的概念结构设计,找到了实体,分析了实体的属性,建立了实体与实体之间的联系,也就是E-R模型图的内容。

对于实体的考虑

视频中老师抽取实体时不是完全描述现实世界的方式,而是根据实际业务来设计,比如将用户分为了用户登录信息、用户详情信息两个实体

对于属性的考虑

需要平衡数据库的性能和规范度

选择空间还是时间?要根据实际业务来,查询频次低的选择空间、提高规范度,频次高的选择时间,反范式化。

2.2 逻辑结构设计

创建表的SQL代码

CREATE` `DATABASE` `qqzonedb ``CHAR` `SET` `utf8;
USE qqzonedb;

 

CREATE` `TABLE` ``t_user_basic` (
 ```id` ``INT``(11) ``NOT` `NULL` `AUTO_INCREMENT,
 ```loginId` ``VARCHAR``(20) ``NOT` `NULL``,
 ```nickName` ``VARCHAR``(50) ``NOT` `NULL``,
 ```pwd` ``VARCHAR``(20) ``NOT` `NULL``,
 ```headImg` ``VARCHAR``(20) ``DEFAULT` `NULL``,
 ``PRIMARY` `KEY` `(`id`),
 ``UNIQUE` `KEY` ``loginId` (`loginId`)
) ENGINE=INNODB AUTO_INCREMENT=6 ``DEFAULT` `CHARSET=utf8;

 

INSERT` `INTO` ``t_user_basic`(`id`,`loginId`,`nickName`,`pwd`,`headImg`) 
VALUES` `(1,``'u001'``,``'jim'``,``'ok'``,``'h1.jpeg'``),
(2,``'u002'``,``'tom'``,``'ok'``,``'h2.jpeg'``),
(3,``'u003'``,``'kate'``,``'ok'``,``'h3.jpeg'``),
(4,``'u004'``,``'lucy'``,``'ok'``,``'h4.jpeg'``),
(5,``'u005'``,``'张三丰'``,``'ok'``,``'h5.jpeg'``);

 

CREATE` `TABLE` ``t_user_detail` (
 ```id` ``INT``(11) ``NOT` `NULL``,
 ```realName` ``VARCHAR``(20) ``DEFAULT` `NULL``,
 ```tel` ``VARCHAR``(11) ``DEFAULT` `NULL``,
 ```email` ``VARCHAR``(30) ``DEFAULT` `NULL``,
 ```birth` DATETIME ``DEFAULT` `NULL``,
 ```star` ``VARCHAR``(10) ``DEFAULT` `NULL``,
 ``PRIMARY` `KEY` `(`id`),
 ``CONSTRAINT` ``FK_detail_basic` ``FOREIGN` `KEY` `(`id`) ``REFERENCES` ``t_user_basic` (`id`)
) ENGINE=INNODB ``DEFAULT` `CHARSET=utf8;

 

CREATE` `TABLE` ``t_friend` (
 ```id` ``INT``(11) ``NOT` `NULL` `AUTO_INCREMENT,
 ```uid` ``INT``(11) ``DEFAULT` `NULL``,
 ```fid` ``INT``(11) ``DEFAULT` `NULL``,
 ``PRIMARY` `KEY` `(`id`),
 ``KEY` ``FK_friend_basic_uid` (`uid`),
 ``KEY` ``FK_friend_basic_fid` (`fid`),
 ``CONSTRAINT` ``FK_friend_basic_fid` ``FOREIGN` `KEY` `(`fid`) ``REFERENCES` ``t_user_basic` (`id`),
 ``CONSTRAINT` ``FK_friend_basic_uid` ``FOREIGN` `KEY` `(`uid`) ``REFERENCES` ``t_user_basic` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=11 ``DEFAULT` `CHARSET=utf8;

 

 

INSERT` `INTO` ``t_friend`(`id`,`uid`,`fid`) 
VALUES` `(1,1,2),(2,1,3),(3,1,4),(4,1,5),(5,2,3),(6,2,1),(7,2,4),(8,3,1),(9,3,2),(10,5,1);

 

CREATE` `TABLE` ``t_topic` (
 ```id` ``INT``(11) ``NOT` `NULL` `AUTO_INCREMENT,
 ```title` ``VARCHAR``(100) ``NOT` `NULL``,
 ```content` ``VARCHAR``(500) ``NOT` `NULL``,
 ```topicDate` DATETIME ``NOT` `NULL``,
 ```author` ``INT``(11) ``NOT` `NULL``,
 ``PRIMARY` `KEY` `(`id`),
 ``KEY` ``FK_topic_basic` (`author`),
 ``CONSTRAINT` ``FK_topic_basic` ``FOREIGN` `KEY` `(`author`) ``REFERENCES` ``t_user_basic` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=9 ``DEFAULT` `CHARSET=utf8;

 

INSERT` `INTO` ``t_topic`(`id`,`title`,`content`,`topicDate`,`author`) 
VALUES` `(3,``'我的空间开通了,先做自我介绍!'``,``'大家好,我是铁锤妹妹!'``,``'2021-06-18 11:25:30'``,2),(8,``'我的空间'``,``'我的空间'``,``'2021-07-14 16:16:40'``,1);

 

CREATE` `TABLE` ``t_reply` (
 ```id` ``INT``(11) ``NOT` `NULL` `AUTO_INCREMENT,
 ```content` ``VARCHAR``(500) ``NOT` `NULL``,
 ```replyDate` DATETIME ``NOT` `NULL``,
 ```author` ``INT``(11) ``NOT` `NULL``,
 ```topic` ``INT``(11) ``NOT` `NULL``,
 ``PRIMARY` `KEY` `(`id`),
 ``KEY` ``FK_reply_basic` (`author`),
 ``KEY` ``FK_reply_topic` (`topic`),
 ``CONSTRAINT` ``FK_reply_basic` ``FOREIGN` `KEY` `(`author`) ``REFERENCES` ``t_user_basic` (`id`),
 ``CONSTRAINT` ``FK_reply_topic` ``FOREIGN` `KEY` `(`topic`) ``REFERENCES` ``t_topic` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=6 ``DEFAULT` `CHARSET=utf8;

 

INSERT` `INTO` ``t_reply`(`id`,`content`,`replyDate`,`author`,`topic`) 
VALUES` `(3,``'回复'``,``'2021-07-14 16:16:54'``,2,8),
(4,``'回复2222'``,``'2021-07-14 16:17:11'``,3,8),
(5,``'这里是第三个回复'``,``'2021-07-14 16:30:49'``,1,8);

 

CREATE` `TABLE` ``t_host_reply` (
 ```id` ``INT``(11) ``NOT` `NULL` `AUTO_INCREMENT,
 ```content` ``VARCHAR``(500) ``NOT` `NULL``,
 ```hostReplyDate` DATETIME ``NOT` `NULL``,
 ```author` ``INT``(11) ``NOT` `NULL``,
 ```reply` ``INT``(11) ``NOT` `NULL``,
 ``PRIMARY` `KEY` `(`id`),
 ``KEY` ``FK_host_basic` (`author`),
 ``KEY` ``FK_host_reply` (`reply`),
 ``CONSTRAINT` ``FK_host_basic` ``FOREIGN` `KEY` `(`author`) ``REFERENCES` ``t_user_basic` (`id`),
 ``CONSTRAINT` ``FK_host_reply` ``FOREIGN` `KEY` `(`reply`) ``REFERENCES` ``t_reply` (`id`)
) ENGINE=INNODB ``DEFAULT` `CHARSET=utf8;

2.3 物理结构设计

3 系统代码实现

3.1 pojo、DAO、service

POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans

pojo

将数据库的表转化为pojo

可以通过IDEA将表结构直接身成为是实体类

DAO

数据访问对象接口(Data Access Object Interface)

创建DAO接口的实现类,继承BaseDAO

BaseDAO
package com.atguigu.myssm.basedao;
​
import java.lang.reflect.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
​
public abstract class BaseDAO<T> {
    protected Connection conn ;
    protected PreparedStatement psmt ;
    protected ResultSet rs ;
​
    //T的Class对象
    private Class entityClass ;
​
    public BaseDAO() {
        //getClass() 获取Class对象,当前我们执行的是new FruitDAOImpl() , 创建的是FruitDAOImpl的实例
        //那么子类构造方法内部首先会调用父类(BaseDAO)的无参构造方法
        //因此此处的getClass()会被执行,但是getClass获取的是FruitDAOImpl的Class
        //所以getGenericSuperclass()获取到的是BaseDAO的Class
        Type genericType = getClass().getGenericSuperclass();
        //ParameterizedType 参数化类型
        Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
        //获取到的<T>中的T的真实的类型
        Type actualType = actualTypeArguments[0];
​
        try {
            entityClass = Class.forName(actualType.getTypeName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new DAOException("BaseDAO 构造方法出错了,可能的原因是没有指定<>中的类型");
        }
​
    }
​
    protected Connection getConn(){
        return ConnUtil.getConn();
    }
​
    protected void close(ResultSet rs , PreparedStatement psmt , Connection conn){
​
    }
​
    //给预处理命令对象设置参数
    private void setParams(PreparedStatement psmt , Object... params) throws SQLException {
        if(params!=null && params.length>0){
            for (int i = 0; i < params.length; i++) {
                psmt.setObject(i+1,params[i]);
            }
        }
    }
​
    //执行更新,返回影响行数
    protected int executeUpdate(String sql , Object... params) {
        boolean insertFlag = false ;
        insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
​
        conn = getConn();
        try{
            if(insertFlag){
                psmt = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            }else {
                psmt = conn.prepareStatement(sql);
            }
            setParams(psmt,params);
            int count = psmt.executeUpdate() ;
​
            if(insertFlag){
                rs = psmt.getGeneratedKeys();
                if(rs.next()){
                    return ((Long)rs.getLong(1)).intValue();
                }
            }
            return 0 ;
        }catch (SQLException e){
            e.printStackTrace();
            throw new DAOException("BaseDAO executeUpdate出错了");
        }
    }
​
    //通过反射技术给obj对象的property属性赋propertyValue值
    private void setValue(Object obj ,  String property , Object propertyValue) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        Class clazz = obj.getClass();
​
        //获取property这个字符串对应的属性名 , 比如 "fid"  去找 obj对象中的 fid 属性
        Field field = clazz.getDeclaredField(property);
        if(field!=null){
​
            //获取当前字段的类型名称
            String typeName = field.getType().getName();
            //判断如果是自定义类型,则需要调用这个自定义类的带一个参数的构造方法,创建出这个自定义的实例对象,然后将实例对象赋值给这个属性
​
            if(isMyType(typeName)){
                //假设typeName是"com.atguigu.qqzone.pojo.UserBasic"
                Class typeNameClass = Class.forName(typeName);
                Constructor constructor = typeNameClass.getDeclaredConstructor(java.lang.Integer.class);
                propertyValue = constructor.newInstance(propertyValue);
            }
            field.setAccessible(true);
            field.set(obj,propertyValue);
        }
    }
​
    private static boolean isNotMyType(String typeName){
        return "java.lang.Integer".equals(typeName)
                || "java.lang.String".equals(typeName)
                || "java.util.Date".equals(typeName)
                || "java.sql.Date".equals(typeName);
    }
​
    private static boolean isMyType(String typeName){
        return !isNotMyType(typeName);
    }
​
    //执行复杂查询,返回例如统计结果
    protected Object[] executeComplexQuery(String sql , Object... params){
        conn = getConn() ;
        try{
            psmt = conn.prepareStatement(sql);
            setParams(psmt,params);
            rs = psmt.executeQuery();
​
            //通过rs可以获取结果集的元数据
            //元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
​
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取结果集的列数
            int columnCount = rsmd.getColumnCount();
            Object[] columnValueArr = new Object[columnCount];
            //6.解析rs
            if(rs.next()){
                for(int i = 0 ; i<columnCount;i++){
                    Object columnValue = rs.getObject(i+1);     //33    苹果      5
                    columnValueArr[i]=columnValue;
                }
                return columnValueArr ;
            }
        }catch(SQLException e){
            e.printStackTrace();
            throw new DAOException("BaseDAO executeComplexQuery出错了");
        }
​
        return null ;
    }
​
    //执行查询,返回单个实体对象
    protected T load(String sql , Object... params){
        conn = getConn() ;
        try{
            psmt = conn.prepareStatement(sql);
            setParams(psmt,params);
            rs = psmt.executeQuery();
​
            //通过rs可以获取结果集的元数据
            //元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
​
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取结果集的列数
            int columnCount = rsmd.getColumnCount();
            //6.解析rs
            if(rs.next()){
                T entity = (T)entityClass.newInstance();
​
                for(int i = 0 ; i<columnCount;i++){
                    String columnName = rsmd.getColumnName(i+1);            //fid   fname   price
                    Object columnValue = rs.getObject(i+1);     //33    苹果      5
                    setValue(entity,columnName,columnValue);
                }
                return entity ;
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new DAOException("BaseDAO load出错了");
        }
​
        return null ;
    }
​
    //执行查询,返回List
    protected List<T> executeQuery(String sql , Object... params){
        List<T> list = new ArrayList<>();
        conn = getConn() ;
        try{
            psmt = conn.prepareStatement(sql);
            setParams(psmt,params);
            rs = psmt.executeQuery();
​
            //通过rs可以获取结果集的元数据
            //元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
​
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取结果集的列数
            int columnCount = rsmd.getColumnCount();
            //6.解析rs
            while(rs.next()){
                T entity = (T)entityClass.newInstance();
​
                for(int i = 0 ; i<columnCount;i++){
                    String columnName = rsmd.getColumnLabel(i+1);            //fid   fname   price
                    Object columnValue = rs.getObject(i+1);     //33    苹果      5
                    setValue(entity,columnName,columnValue);
                }
                list.add(entity);
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new DAOException("BaseDAO executeQuery出错了");
        }
        return list ;
    }
}

Service

创建各类服务层,调用各类DAO实现类,用其中的方法完成各类业务操作。

以UserBasicService为例,完成登录和通过特定用户信息查询其好友列表

UserBasicService接口

package com.atguigu.qqzone.service;
​
import com.atguigu.qqzone.dao.UserBasicDAO;
import com.atguigu.qqzone.pojo.UserBasic;
​
import java.util.List;
​
public interface UserBasicService {
    UserBasic login(String loginId , String pwd );
    List<UserBasic> getFriendList(UserBasic userBasic);
}

UserBasicService实现类

package com.atguigu.qqzone.service.impl;
​
import com.atguigu.qqzone.dao.UserBasicDAO;
import com.atguigu.qqzone.pojo.UserBasic;
import com.atguigu.qqzone.service.UserBasicService;
​
import java.util.ArrayList;
import java.util.List;
​
public class UserBasicServiceImpl implements UserBasicService {
​
    private UserBasicDAO userBasicDAO = null ;
​
    @Override
    public UserBasic login(String loginId, String pwd) {
        UserBasic userBasic = userBasicDAO.getUserBasic(loginId,pwd);
        return userBasic;
    }
​
    @Override
    public List<UserBasic> getFriendList(UserBasic userBasic) {
        List<UserBasic> userBasicList = userBasicDAO.getUserBasicList(userBasic);
        List<UserBasic> friendList = new ArrayList<>(userBasicList.size());
        for (int i = 0; i < userBasicList.size(); i++) {
            UserBasic friend = userBasicList.get(i);
            friend = userBasicDAO.getUserBasicById(friend.getId());
            friendList.add(friend);
        }
        return friendList;
    }
}

3.2 配置applicationContext.xml

通过依赖注入DI,在Tomcat启动时读取该配置文件,提前创建相应运行时service对象,根据配置文件中的bean,根据客户端发送的不同参数dispatcher中央控制器会使用不同的controller,调用不同的service来完成相应业务。

<?xml version="1.0" encoding="utf-8"?>
​
<!DOCTYPE beans [
    <!ELEMENT beans (bean*)>
    <!ELEMENT bean (property*)>
    <!ELEMENT property (#PCDATA)>
​
    <!ATTLIST bean id ID #REQUIRED>
    <!ATTLIST bean class CDATA #IMPLIED>
    <!ATTLIST property name CDATA #IMPLIED>
    <!ATTLIST property ref IDREF #IMPLIED>
]>
​
<beans>
    <bean id="userBasicDAO" class="com.atguigu.qqzone.dao.impl.UserBasicDAOImpl"/>
    <bean id="topicDAO" class="com.atguigu.qqzone.dao.impl.TopicDAOImpl"/>
​
    <bean id="userBasicService" class="com.atguigu.qqzone.service.impl.UserBasicServiceImpl">
        <property name="userBasicDAO" ref="userBasicDAO"/>
    </bean>
​
    <bean id="topicService" class="com.atguigu.qqzone.service.impl.TopicServiceImpl">
        <property name="topicDAO" ref="topicDAO"/>
    </bean>
​
    <bean id="user" class="com.atguigu.qqzone.controller.UserController">
        <property name="userBasicService" ref="userBasicService"/>
        <property name="topicService" ref="topicService"/>
    </bean>
    <bean id="page" class="com.atguigu.myssm.myspringmvc.PageController"/>
​
</beans>

XML 验证


拥有正确语法的 XML 被称为"形式良好"的 XML。

通过 DTD 验证的XML是"合法"的 XML。

<?xml version="1.0" encoding="utf-8"?>
​
<!DOCTYPE beans [
    <!ELEMENT beans (bean*)>
    <!ELEMENT bean (property*)>
    <!ELEMENT property (#PCDATA)>
​
    <!ATTLIST bean id ID #REQUIRED>
    <!ATTLIST bean class CDATA #IMPLIED>
    <!ATTLIST property name CDATA #IMPLIED>
    <!ATTLIST property ref IDREF #IMPLIED>
]>

3.3 修改login.html中的action

对登录页面,提交的是user.do,调用userBasicService、和topicService,经过thymeleaf渲染实现登录后页面。

th:action="@{/user.do}

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/login.css">
</head>
<body>
    <div id="div0">
        <div id="div_container">
            <p class="center">用户登录1</p>
            <form th:action="@{/user.do}

标签:11,JavaWeb,项目,KEY,basic,NULL,id,QQZone
来源: https://www.cnblogs.com/fancy2022/p/16589875.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有