案例2:qqZone

一.业务需求分析

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

二.数据库的设计

  1. 抽取实体:用户登录信息、用户详情信息、日志、回帖、主人回帖
  2. 属性分析
  3. 实体之间的关系

QQZone

三.代码实现

1. pojo类的实现

1.1设计原则

  1. 主键应该是没有实际意义的列,不要和业务由关联。因为将来可能涉及两表合并,主键将无法合并。

  2. 设计时间类型时:

    –年月日时分秒(java.util.Date),

    –年月日(java.sql.Date),时分秒(java.sql.TIme

  3. 类与类之间的关系也要建立

    public class UserBasic {
        private Integer id;
        private String loginId;
        private String nickName;
        private String pwd;
        private String headImg;
    	
        //类与类之间的关系
        private UserDetail userDetail;  //1:1
        private List<Topic> topicList;  //1:N
        private List<UserBasic> friendList; //M:N
    
        public UserBasic() {
        }
    }
    

1.2可能的异常

  1. 查询好友列表时,查询到的rsmd.getColumnNamefid,而UserBasic中的属性为id。可以在sql语句中使用别名代替,并rsmd.getColumnName替代
  2. 数据库连接失败:1.驱动是否导入,2.数据库选择,3.用户名和密码。
  3. 获取Topic列表时调用setValue(entity,columnName,columnValue)设置UserBasic author属性,但是数据库中返回的类型是Integer

2. dao

3. service

3.1 接口

3.2 接口的实现类

4. controller

4.1UserController

4.2TopicController

需要根据topicId获取对应的topic信息

5.HTML的设计

4.1可能的异常`

  1. 页面无法渲染。

    原因:没有经过processTemplate(methodReturnStr,req,resp);语句的渲染,而是直接跳转到静态页面

    解决:通过PageController控制渲染,调用PageController中的page方法跳转到Page参数对应的页面。

     iframe th:src="@{/page.do?operate=page&page=frames/left}"
    
    public class PageController {
        protected String page(String page){
           return page;
       }
    }
    
  2. 点击好友名称时,在iframe中刷新整个页面。

    原因:target没有设置

    解决:超链接后添加 target="_top"语句,使得新页面在顶层显示。

  3. 访问动态页面时,一定要经过渲染。案例使用PageController渲染

  4. return语句的使用:

4.2login登录界面

  1. 静态页面:可以直接用地址访问。本案例使用page.do?operate=page&page=login渲染得到。

  2. 启动时,访问的页面是:

    http:// localhost :8080 /pro23 /page.do ?operate=page&page=login

    协议 ServerIP port context root request.getPath query String

4.3index展示界面

4.3.1top页面信息
  1. 显示欢迎进入好友页面:th:text="|欢迎进入${session.friend.nickName}的空间!|"

  2. 如果不是在自己空间,显示连接返回自己空间(即将friend更新为当前userBasic

    <span th:if="${session.userBasic.id!=session.friend.id}">
        <a th:href="@{|/user.do?operate=friend&id=${session.userBasic.id}|}" target="_top">返回自己的空间!</a>
    </span>
    
4.3.2left好友列表
  1. 点击好友时,页面刷新为好友的日志列表。
    • 根据id查询好友的userBasic信息,覆盖session.friend
    • main页面展示friend的topicList,而不是userBasic中的topicList。方法:给连接添加target="_top"
4.3.3main日志显示列表
  1. 日期显示的格式化与解析

    java

    //解析:看的懂 ---> 看不懂
    String dateStr = "2022-04-11 12:58:13";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    try{
        Date date = sdf.parse(dateStr);
    }catch (ParseException e){
        e.printStackTrance();
    }
    //格式化:看不懂 ---> 看得懂
    Date date = new Date();
    String dateStr = sdf.format(date);
    

    thymeleaf

    #dates.format(topic.topicDate,'yyyy-MM-dd HH:mm:ss')
    
4.3.4detial日志详情
  1. 一句里面不能有两个style

  2. 配置thymeleaf文件

    @{}:表示省略了pro21/的绝对路径

    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" th:href="@{css/common.css}">
        <link rel="stylesheet" th:href="@{css/main.css}">
    </head>
    
  3. 需要获取主人日志,回复列表(每个回复对应的主人日志)。

  4. 日志中的主人回复没有显示

    <!--head中-->
    <script language="JavaScript">
        function showDelImg(imgId){
            document.getElementById(imgId).style.display='inline';
        }
        function hiddenDelImg(imgId){
            document.getElementById(imgId).style.display='none';
        }
    </script>
    <!--body中-->
    <td  th:onmouseover="|showDelImg('a${reply.id}')|" th:onmouseout="|hiddenDelImg('a${reply.id}')|">
    <a th:unless="${reply.hostReply!=null}" th:id="|a${reply.id}|" th:href="#" style="float: right;display: inline;">主人回复</a>
    </td>
    
  5. 可以删除的条件

    • 在我自己的空间
    • 这条评论是我发表的
  6. 异常:删除父表时,需要保证没有子表在引用父表

    MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`qqzonedb`.`t_host_reply`, CONSTRAINT `FK_host_reply` FOREIGN KEY (`reply`) REFERENCES `t_reply` (`id`))	
    

四.开发套路总结

1.拷贝myssm

2.新建配置文件applicationContext.xml或者其他名字,在web.xml中指定文件名

3.在web.xml文件中配置:

  1. 配置前缀和后缀,这样thtmeleaf引擎就可以根据我们返回的字符串进行拼接,再跳转

    <context-param>
        <param-name>view-prefix</param-name>
        <param-value>/</param-value>
    </context-param>
    <context-param>
        <param-name>view-suffix</param-name>
        <param-value>.html</param-value>
    </context-param>
    
  2. 配置监听器要读取的参数,目的是加载IOC容器的配置文件(applicationContext.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>applicationContext</param-value>
    </context-param>
    

4.开发具体的业务模块:

  1. 一个具体的业务模块的纵向结构:

    • html页面
    • POJO
    • DAO接口和实现类
    • Service接口和实现类
    • Controller控制器组件
  2. 如果html页面有thymeleaf表达式,一定不能够直接访问,必须要经过PageController

  3. applicationContext.xml中配置DAO,Service,Controller,以及三者之间的依赖关系

  4. DAO实现类中,继承BaseDAO,然后实现具体的接口,注意泛型不能写错,例如:

    public class UserDAOImpl extends BaseDAO<User> implements UserDAO{}

  5. Service是业务控制类,需要注意

    • 业务逻辑封装在Service这一层,不要分散在Controller层。也不要出现在DAO层(需要保证DAO方法的单精度特性)
    • 当需要使用其他模块的功能的时候,尽量调用别人的service,而不是深入到其他模块的DAO细节
  6. Controller类的编写规则

    • applicationContext.xml中来配置Controller

      <bean id="user" class="com.atlff.qqzone.controllers.UserController">

      那么,用户在前端发请求时,对应的servletPath就是/User.do,其中“user”就是此处beanid

    • Controller中设计的方法名需要和operate的值一致

      public String login(String loginId, String pwd, HttpSession session){
          return "index";
      }
      

      此时验登录的表单如下:

      <form th:action="@{/user.do}" method="post">
          <input type="hidden" name="operate" value="login">
      </form>
      
    • 在表单中,组件的name属性和Controller中的方法参数名一致

      <input type="text" name="loginId">

      public String login(String loginId, String pwd, HttpSession session)

    • 另外,Controller中的方法不一定都是通过请求参数获取

      if("request".equals...) else if("resqonse".equals...) else if("session".equals...){
          //直接赋值
      }else{
          //此处才是从request的请求参数中获取
          request.getParameter("loginId")...
      }
      
    • DispatcherServlet中步骤的分类:

      0.从application作用域获取IOC容器

      1.解析servletPath,在IOC容器中寻找对应的Controlelr组件

      2.准备operate指定的方法所要求的参数

      3.调用operate指定的方法

      4.接收到执行operate指定的方法的返回值,对返回值进行处理 ----视图处理

    • 为什么DispatcherServlet能够从application作用域获取到IOC容器?

      ContextLoaderListener在容器启动时会执行初始化任务,而他的操作是:

      1.解析IOC的配置文件,创建一个一个的组件,并完成组件之间的依赖注入关系

      2.将IOC容器保存到application作用域

5.数据库连接异常

  1. 数据库连接失败

    原因:druid没有添加到部署包

    解决:在项目结构中的ProjectStructure中将jar包添加部署

  2. 数据库报错连接太多

    Data source rejected establishment of connection, message from server:"Too many connections"

    原因:DruidDataSourceFactory.createDataSource(properties);出现在createConnection中,导致创建多个连接池.

    解决:将连接池放置在初始化的代码段,而不是出现在方法中.

  3. 数据库连接失败

    原因:druid没有添加到部署包

    解决:在项目结构中的ProjectStructure中将jar包添加部署

  4. 数据库报错连接太多

    Data source rejected establishment of connection, message from server:"Too many connections"

    原因:DruidDataSourceFactory.createDataSource(properties);出现在createConnection中,导致创建多个连接池.

    解决:将连接池放置在初始化的代码段,而不是出现在方法中.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值