业务描述

基于Spring,MyBatis,SpringBoot,Thymeleaf,Ajax技术实现活动模块的查询,添加等操作。

项目环境初始化

准备工作

1. MySQL(5.7)
2. JDK (1.8)
3. Maven (3.6.3)
4. STS(4.7.1)

数据库初始化

打开mysql控制台,然后按如下步骤执行goods.sql文件。
第一步:登录mysql。

mysql –uroot –proot

第二步:设置控制台编码方式。

set names utf8;

第三步:执行activity.sql文件(切记不要打开文件复制到mysql客户端运行)

source d:/activity.sql

其中activity.sql文件内容如下:

drop database if exists dbactivity;
create database dbactivity default character set utf8;
use dbactivity;
create table tb_activity(
     id bigint primary key auto_increment,
     title varchar(100) not null,
     category varchar(100) not null,
     startTime datetime not null,
     endTime datetime not null,
     remark text,
     state tinyint,
     createdTime datetime not null,
     createdUser varchar(100)
)engine=InnoDB;
insert into tb_activity values (null,'ABS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'VALIDATE','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'VALIDATE','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'ABS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'ACCESS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');

创建项目并添加依赖

第一步:基于start.spring.io 创建项目并设置基本信息。
69XXD[0N8EV_GB}6Y}L}KWG.png!
第二步:创建项目时指定项目核心依赖。
%}0%R6)CM_JG6M{U23C5%G5.png
第三步:项目创建以后分析其结构。
LI@}74%%U_3%T6VS]H]$IEU.png

项目配置文件内容初始化

#server
server.port=80
#server.servlet.context-path=/
#spring datasource
spring.datasource.url=jdbc:mysql:///dbactivity?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

#spring mybatis
mybatis.mapper-locations=classpath:/mapper/*/*.xml

#spring logging
logging.level.com.cy=debug

#spring thymeleaf
spring.thymeleaf.prefix=classpath:/templates/modules/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false

启动类所有由spring管理的对象在执行时输出日志log

项目API架构设计

其API架构设计,如图所示:
ZMY6])PO8Q4CUB04T7[C)G2.png

活动模块查询业务实现

业务描述

从数据库查询所有活动(Activity)信息,然后呈现在页面上(基于JS方式进行呈现),如图所示:
KVIAOW(OUBGI~NH96{T9O57.png

业务时序分析

在浏览器地址栏输入查询活动信息的url获取活动信息,其时序过程如图所示:
E[PZJ`@}{]8[7E4UR(NOYS3.png
显式view 有http、自己提交的
隐式view:默认

POJO类设计及实现

创建Activity类,基于此类对象封装从数据库获取的活动信息,代码如下:

package com.cy.pj.activity.pojo;
import java.util.Date;
import lombok.Data;
@Data
public class Activity {
    private Integer id;
    private String title;
    private String category;
    private Date startTime;
    private Date endTime;
    private String remark;
    private Integer state;
    private Date createdTime;
    private String createdUser;
}
其中,@Data注解为Lombok注解,需要在IDE环境中已经安装lombok插件,项目中添加lombok依赖,假如没有也可以自己添加set/get相关方法

Dao接口方法及映射定义

定义活动模块数据层接口及查询方法,代码如下:

package com.cy.pj.activity.dao;
import com.cy.pj.activity.pojo.Activity;

@Mapper
public interface ActivityDao {
    
     @Select("select * from tb_activity order by createdTime desc")
     List<Activity> findActivitys();
}

Service接口方法定义及实现

定义service接口以及获取活动信息的方法,代码如下:

package com.cy.pj.activity.service;
import java.util.List;
import com.cy.pj.activity.pojo.Activity;
//引入包中的类:ctrl+shift+o

public interface ActivityService {
    List<Activity> findActivitys();
}

定义service接口实现类,并重写接口方法,代码如下:

package com.cy.pj.activity.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.cy.pj.activity.dao.ActivityDao;
import com.cy.pj.activity.pojo.Activity;
import com.cy.pj.activity.service.ActivityService;

@Service
public class ActivityServiceImpl implements ActivityService {
    @Autowired
    private ActivityDao activityDao;
    @Override
    public List<Activity> findActivitys() {
         return  activityDao.findActivitys();
    }
}

Controller方法定义及实现

package com.cy.pj.activity.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.cy.pj.activity.pojo.Activity;
import com.cy.pj.activity.service.ActivityService;

@Controller
public class ActivityController {
     @Autowired
     private ActivityService activityService;
     
     @RequestMapping("/activity/doActivityUI")
     public String doActivityUI(){
          return "activity";
     }
     
     /**查询所有活动信息*/
     @RequestMapping("/activity/doFindActivitys")
     @ResponseBody
     public List<Activity> doFindActivitys() {
         List<Activity>  list=activityService.findActivitys();
         return list;
     }
} 

在Controller 下(Model model)"doGoodUI"

           List<Acticity> list=activity

这个是数据和页面绑定在一起,
缺点1:页面加载耗时,体验不好
缺点2:可拓展性不好
-》一个方法返回数据:dofindActivitys
-》一个方法返回页面:doActivitysUI

Activity 列表页面设计及实现

本次页面样式基于bootstrap(一个前端框架,官网为bootcss.com)进行实现,首先在项目工程中添加静态资源,如图所示:
FQO@F[G0AG`K102R3ND5QGX.png
设计activity.html页面,引入bootstrap,jquery等相关资源,如图所示:
4[0Y(0SCY_7HGI1FLH9[@BS.png
在activity.html页面中添加呈现Activity数据的html元素:

<table class="table">
    <thead>
        <tr>
            <th>title</th>
            <th>Category</th>
            <th>StartTime</th>
            <th>EndTime</th>
            <th>State</th>
            <th>Operation</th>
        </tr>
    </thead>
    <tbody id="tbodyId">
        <tr><td colspan="6">数据正在加载中.......</td></tr>
    </tbody>
</table>

基于Ajax异步加载服务端活动数据并进行局部刷新
方法一

<script type="text/javascript"> function doFindActivitys(){
          var url="/activity/doFindActivitys"
          //启动ajax技术,基于GET请求方式获取服务端json数据
          //getJSON函数默认会将服务端返回的json串转换为js对象
          $.getJSON(url,function(result){
              var tBody=$("#tbodyId");
              tBody.empty();//清空原有body内容
              for(var i=0;i<result.length;i++){//循环一次,迭代一行
                  //构建当前行对象
                  var tr=`<tr>
                          <td>${result[i].title}</td>
                          <td>${result[i].category}</td>
                          <td>${result[i].startTime}</td>
                          <td>${result[i].endTime}</td>
                          <td>${result[i].state==1?'有效':'无效'}</td>
                          <td><button type='button' class='btn btn-danger btn-sm'>delete</button></td>
                         </tr>`
                  //将每一行的内容都追加tbody中
                  tBody.append(tr);
              }
          });
      };
      doFindActivitys(); </script>

方法二

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <div class="container">
        <h1>The Activity Page</h1>
        <table class="table">
            <thead>
                <tr>
                    <th>title</th>
                    <th>Category</th>
                    <th>StartTime</th>
                    <th>EndTime</th>
                    <th>State</th>
                    <th>Operation</th>
                </tr>
            </thead>
            <tbody id="tbodyId">
                <tr>
                    <td colspan="7">数据正在加载中.......</td>
                </tr>
            </tbody>
        </table>
    </div>
    <script type="text/javascript" src="/jquery/jquery.min.js"></script>
    <script type="text/javascript">
    function findActivitys(){
        //debugger //此单词写的位置很重要(这是入口),你要对谁进行断点分析.
        let url="/activity/doFindActivitys";
        let params={};
        //借助jquery中的ajax函数,向服务端发送异步请求获取活动信息
        $.ajax({//具体格式官网有定义(假如不想看官网,看现成第三方网站找ajax)
            url:url,
            data:params,
            dataType:"json",
            success:function(result){
                doHandleQueryResult(result);
            }
        });
    };
    //处理服务器返回的活动信息
     function doHandleQueryResult(result){
        ///debugger
        console.log(result)
        //1.获取tbody对象,并清空原有内容
        var tBody=$("#tbodyId");//原生写法-document.querySelector("#tbodyId")
        tBody.empty();//清空tbody中原有内容
        //2.迭代result,将活动信息追加到tbody中   
        result.forEach((item)=>{//这里的item为一个变量,代表数组中某一个元素
            //debugger
            tBody.append(
              `<tr>
                 <td>${item.id}</td>
                 <td>${item.title}</td>
                 <td>${item.category}</td>
                 <td>${item.startTime}</td>
                 <td>${item.endTime}</td>
                 <td>${item.state==1?'有效':'已结束'}</td>
                 <td>${item.createdTime}</td>
               </tr>`        
            );
        });
     }
  //jquery中定义的页面加载完整执行方式如下:
  // $(function(){//假如所有的js代码放到html页面的head标签,建议这样写
       findActivitys();
   //});//页面加载完成以后执行
  </script>
</body>

K[42_NC8DV`Q{8)IVCARFD8.png
如果出现报错(bug),按F12
404-》检查路径是否正确
500-》HTML语法,书写是否正确

启动tomcat服务器进行访问测试分析

启动项目,在浏览器中输入activity页面的访问地址,呈现活动信息,如图所示:
9H$$6GQG7T1`E$3NRO33$JV.png
客户端断点调试分析,如图所示:
NXL_83@{_2[MDED[0~Y{$AN.png
运行时断点设置,如图所示:
HG2[Y20Q{`HQ]TRQ1E5AUD4.png
进入断点方法内部,如图所示:
9`]D374$3VXI%ZBSP%JE3{7.png
检查响应数据在客户端的呈现,如图所示:
C}8BRI3AG4[2@DZLO1RTAAA.png

项目启动及运行过程中的BUG分析

  • 数据库链接的url配置错误,如图所示:

image.png

  • 空指针异常,如图所示:

image.png

  • JS 引入错误,如图所示:

image.png
image.png
image.png

  • JS 语法错误,如图所示:

image.png

活动模块添加业务实现

在pojo包里面的Activity.java修改日期规范
@JsonFormat(pattern="yyyy/MM/dd HH:mm,timewod",timezone="GMT+8") 用于告诉springmvc转换json时,将日期按照指定格式进行转换
DKV`JEI)@GVEYG(MU9A11R5.png

业务描述

在活动列表页面,设计添加按钮,点击添加按钮时弹出模态框,在模态框中呈现活动添加表单元素,其添加表单原型设计,
如图所示:
image.png

业务时序分析

活动添加业务,其时序分析,如图所示:

image.png

Dao接口方法及映射定义

在ActivityDao接口中添加持久化活动信息的方法,代码如下:

int insertObject(Activity activity);

在ActivityMapper映射文件中添加映射元素,代码如下:

 <insert id="insertObject" parameterType="com.cy.pj.activity.pojo.Activity"
             useGeneratedKeys="true" keyProperty="id">
          insert into tb_activity
          (title,category,startTime,endTime,remark,state,createdUser,createdTime) 
          values    
          (#{title},#{category},#{startTime},#{endTime},
          #{remark},#{state},#{createdUser},now())
 </insert>

"#{}"表达式数据获取分析,如图所示:

image.png

执行insert操作时获取数据库主键id值,如图所示:

image.png

当然,也可以以注解的方式进行SQL映射,如图所示:

image.png

当活动创建好以后,假如到了结束时间,动态更新活动的状态,代码如下:

@Update("update tb_activity set state=0 where id=#{id}")
int updateState(Long id);

Service接口方法定义及实现

在ActivityService接口中,添加保存活动信息的方法,代码如下:

int saveActivity(Activity entity);

在ActivityServiceImpl实现类中添加,接口方法的实现,代码如下:

@Override
public int saveActivity(Activity entity) {
        int rows=activityDao.insertObject(entity);
        Timer timer=new Timer();
         //此对象可以负责去执行一些任务(这个对象内置一个线程和一个任务队列)
         //启动线程执行任务调度
        timer.schedule(new TimerTask() {//TimerTask为任务
            @Override
            public void run() {
                //一旦调用此任务的线程获得了CPU就会执行这个任务的run方法
                activityDao.updateState(entity.getId());
                timer.cancel();//退出任务调度(后续线程也会销毁)
            }
        }, entity.getEndTime());//按指定时间执行任务.
        return rows;
}

Controller方法定义及实现

在ActivityController中添加,处理添加请求的方法,代码如下:

@RequestMapping("/activity/doSaveActivity")
@ResponseBody
public String doSaveActivity(Activity activity) {
 activityService.saveActivity(activity);
 return "save ok";
} 

Activity 页面添加表单设计及实现

在activity.html页面中添加表单元素,用于实现与用户交互,首先在activity页面上添加一个添加按钮,代码如下:

<button type="button" class="btn btn-primary btn-sm"
            data-toggle="modal" data-target="#myModal">创建活动
</button>

在指定位置添加模态框(可参考bootcss.com),代码如下:

<div class="modal fade" id="myModal" tabindex="-1" role="dialog"
            aria-labelledby="myModalLabel">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal"
                                                aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <h4 class="modal-title" id="myModalLabel">创建活动</h4>
                    </div>
                    <div class="modal-body">    
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default"data-dismiss="modal">Close</button>
                        <button type="button" class="btn btn-primary" onclick="doSaveObject()">
                        Save Changes</button>
                    </div>
                </div>
            </div>
</div> 

在模态框”modal-body”位置,添加form表单,代码如下:

<form class="form-horizontal" th:action="@{/activity/doSaveActivity}" method="post">
<div class="form-group">
    <label for="titleId" class="col-sm-2 control-label">标题</label>
        <div class="col-sm-10">
         <input type="text" class="form-control"  name="title" id="titleId" placeholder="title">
        </div>
</div>
<div class="form-group">
<label for="categoryId" class="col-sm-2 control-label">类型</label>
    <div class="col-sm-10">
        <select id="categoryId" name="category" class="form-control">
            <option value="教育培训">教育培训</option>
            <option value="企业活动">企业活动</option>
            <option value="交友活动">交友活动</option>
        </select>
    </div>
</div>
<div class="form-group" >
<label for="startTimeId" class="col-sm-2 control-label">开始时间</label>
    <div class="col-sm-10">
       <input type="text" class="form-control form_datetime"
         name="startTime" id="startTimeId" placeholder="start time">
    </div>
</div>
<div class="form-group">
    <label for="endTimeId" class="col-sm-2 control-label">结束时间</label>
    <div class="col-sm-10">
           <input type="text" class="form-control form_datetime" 
            name="endTime" id="endTimeId" placeholder="end time">
    </div>
</div>
<div class="form-group">
    <label for="remarkId" class="col-sm-2 control-label">备注</label>
        <div class="col-sm-10">
           <textarea type="text" class="form-control"  rows="5" name="remark" id="remarkId">
            </textarea>
    </div>
</div>
</form> 

添加JS代码,处理模态框Save按钮事件,代码如下:

function doSaveObject(){
        //1.定义url
        let url="/activity/doSaveObject";
        //2.定义请求参数
        var params=
      $("#saveFormId").serialize();//serialize()为jquery中直接获取表单数据方法
        console.log("params",params);
        //3.发送异步请求
        $.ajax({
            type:"post",
            url:url,
            data:params,
            success:function(result){
                alert(result);
                //隐藏模态框
                $('#myModal').modal('hide');
                //重新执行查询,局部刷新
                findActivitys();
            }
        });
        
    }

启动tomcat服务器进行访问测试分析

启动tomcat ,访问活动列表页面,如图所示:
image.png

在活动列表页面,点击添加按钮,然后启动活动添加模块框,执行添加操作如图所示:
image.png

Spring MVC 为方法参数赋值分析,如图所示:

image.png

日历控件拓展实现(开始时间和结束时间通过选择实现),效果展示,如图所示:
image.png

Bootstrap-datapicker控件的实现过程,如图所示:
image.png

项目启动及运行过程中的BUG分析

  • BindingException,如图所示:

image.png

  • 400 异常,如图所示:

image.png

  • 服务端日期格式设计,如图所示:

image.png

  • 完整性约束异常,如图所示:

image.png

总结(Summary)

本模块中讲解了活动模块中类的设计,映射设计,交互设计以及实现,然后可以基于此模块的练习加强对业务和技术上的设计及是实现。


white night
1 声望0 粉丝