07-SpringBoot+MyBatis+Spring 技术整合实现商品品牌模块的CRUD操作

小韩 2020-11-10 09:13:21
技术 mybatis spring SpringBoot 07-springboot+mybatis+spring


业务描述

任何一个电商系统中都有一个商品子系统,而与商品相关联都会有一个品牌信息管理,在当前业务系统设计中我们就是要对商品品牌信息的管理进行设计和实现.
在品牌(Brand)信息管理中就是要实现对商品品牌信息的添加,修改,查询,删除等业务,如图所示:
image.png

业务基本原型设计

基于品牌业务描述,对品牌模块的业务原型进行分析和设计,如图所示:

  • 品牌列表页面

image.png

  • 品牌编辑页面

image.png

项目技术架构分析及设计

在品牌管理模块实现过程,我们采用典型的C/S架构进行实现.客户端我们基于浏览器进行实现,服务端采用tomcat,数据库使用MySQL.具体应用层基于MVC分层架构进行实现.

项目技术栈应用分析及选型

  • 客户端技术:html,css,javascript,bootstrap
  • 服务端技术:hikaricp,mybatis,spring,springboot,thymeleaf
  • 数据库技术:mysql,sql
  • 开发工具集:jdk1.8,maven3.6.3,idea2020.2
  • 项目品牌模块核心API分析与设计

基于分层架构设计思想,现对品牌API进行设计,如图所示:

image.png

数据库及表的分析与设计

设计并创建数据库

假如数据库已存在,则先删除数据库,代码如下:

drop database if exists dbbrand;

创建新的数据库,代码如下:

create database dbbrand default character set utf8;

设计并创建品牌(Brand)表

打开数据库,语句如下:

use dbbrand;

在dbbrand数据库中创建品牌表.

create table tb_brand(
id bigint primary key auto_increment,
name varchar(100) not null,
remark text,
createdTime datetime not null
)engine=InnoDB;

基于SQL脚本执行数据库初始化

将数据库设计脚本写到brand.sql中,然后按如下步骤执行:

打开mysql自带客户端,登录mysql,指令如下:

mysql -uroot -proot

设置客户端编码,指令如下:

set names utf8;

执行sql脚本,指令如下:

source d:/brand.sql

脚本执行成功以后,在客户端查询数据之前,先执行如下语句:

set names gbk;

项目环境初始化

准备操作

1)JDK 1.8
2)Maven 3.6.3
3)IDEA 2020.2
4)MySQL 5.7+

创建项目Module

打开idea,然后基于设计,创建项目module,如图所示:

image.png

添加项目Module依赖

  • MySQL 驱动
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
  • Srping Jdbc 提供了HikariCP连接池
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
  • MyBatis 资源
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
  • Spring Web 依赖 (内置一个tomcat服务)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • Thymeleaf 依赖 (html模板引擎)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

项目Module基础配置

打开项目Module配置文件application.properties,并添加如下内容:

#spring server
server.port=80
# spring datasource
spring.datasource.url=jdbc:mysql:///dbbrand?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
# spring mybatis
mybatis.mapper-locations=classpath:/mapper/*/*.xml
# spring log
logging.level.com.cy=debug
#spring thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

启动项目进行初步环境测试分析

  • 端口号被占用

image.png

  • 数据库连接错误

image.png

品牌数据的查询及呈现

业务描述

将数据库中的品牌信息查询出来,然后在客户端基于html技术进行呈现,如图所示:
image.png

品牌数据的查询及呈现

业务描述

将数据库中的品牌信息查询出来,然后在客户端基于html技术进行呈现,如图所示:
image.png

服务端品牌查询时序设计

基于查询请求,进行访问时序设计,如图所示:
image.png

领域对象(POJO)设计及实现

设置Brand对象,基于此对象封装从数据库查询到的品牌信息,代码如下:

package com.cy.pj.brand.pojo;
import java.util.Date;
public class Brand {
private Integer id;
private String name;
private String remark;
private Date createdTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
@Override
public String toString() {
return "Brand{" +
"id=" + id +
", name='" + name + ''' +
", remark='" + remark + ''' +
", createdTime=" + createdTime +
'}';
}
}

数据逻辑对象(DAO)查询方法设计及实现

设计用于访问Brand数据的数据访问对象及方法,其关键步骤如下:

第一步:定义BrandDao接口,代码如下:

package com.cy.pj.brand.dao;
@Mapper
public interface BrandDao{
}

第二步:在BrandDao中定义品牌查询方法,代码如下:

List<Brand> findBrands(String name);

第三步:基于查询方法定义SQL映射.
在resources目录中创建mapper/brand目录,并在目录中添加BrandMapper.xml文件,关键代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.pj.brand.dao.BrandDao">
<select id="findBrands" resultType="com.cy.pj.brand.pojo.Brand">
select id,name,remark,createdTime
from tb_brand
<if test="name!=null and name!=''">
where name like concat("%",#{name},"%")
</if>
</select>
</mapper>

其中:concat为mysql中提供的字符串连接函数.

第四步:对数据层的查询方法进行单元测试,代码如下:

package com.cy.pj.brand.dao;
@SpringBootTests
public class BrandDaoTests{
@Autowired
private BrandDao brandDao;
@Test
void testFindBrands(){
List<Brand> list=brandDao.findBrands("TCL");
for(Brand b:list){
System.out.println(b);
}
}
} 

第五步:测试过程中的BUG分析?

  • BindingException,如图所示:

image.png

  • ExecutorException,如图所示:

image.png

业务逻辑对象(Service)查询方法设计及实现

业务逻辑对象负责模块的具体业务处理,例如参数校验,事务控制,权限控制,日志记录等.

第一步:定义业务接口

package com.cy.pj.brand.service;
public interface BrandService{
} 

第二步:在BrandService接口中添加品牌查询方法

List<Brand> findBrands(String name);

第三步:定义BrandService接口实现类BrandServiceImpl.

package com.cy.pj.brand.service.impl;
@Service
public class BrandServiceImpl implements BrandService{
private static final Logger log=
LoggerFactory.getLogger(BrandServiceImpl.class);
@Autowired
private BrandDao brandDao;
public List<Brand> findBrands(String name){
long t1=System.currentTimeMillis();
List<Brand> list=brandDao.findBrands(name);
long t2=System.currentTimeMillis();
log.info("findBrands->time->{}",(t2-t1));
return list;
}
} 

第三步:定义BrandService接口方法的单元测试类,并业务进行测试分析

package com.cy.pj.brand.service;
@SpringBootTest
public class BrandServiceTests{
@Autowired
private BrandService brandService;
@Test
void testFindBrands(){
List<Brand> list=brandService.findBrands();
for(Brand b:list){
System.out.println(b);
}
}
}

第四步:测试过程中的Bug分析

  • NoSuchBeanDefinition,如图所示:

image.png

  • NullPointerException,如图所示:

image.png

控制逻辑对象(Controller)查询方法设计及实现

在控制逻辑对象中主要是负责请求和响应逻辑控制,例如请求url映射,参数映射,请求方式,结果集的封装,解析,响应的设计等.

第一步:定义Controller类

package com.cy.pj.brand.controller;
@Controller
public class BrandController{
@Autowired
private BrandService brandService;
}

第二步:在Controller添加处理查询请求的方法

@GetMapping(value={"/brand/doFindBrands/{name}","/brand/doFindBrands"})
public String doFindBrands(@PathVariable(required = false) String name, Model model){
List<Brand> list=brandService.findBrands(name);
model.addAttribute("list", list);
return "brand/brand";//第一个brand为目录,第二brand为view name
} 

其中,
1)@GetMapping描述方法时,表示这个方法只能处理Get请求,注解内部的value属性可以指定多个url.
2)@PathVariable 用于描述方法参数,表示方法参数的值可以来自url中{}内部的变量值,required=false表示参数可以不传值.

客户端品牌列表页面设计及实现

在项目的templates目录下创建brand目录并添加brand.html页面,其关键代码如下:

<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>createdTime</th>
</tr>
</thead>
<tbody>
<tr th:each="brand: ${list}">
<td th:text="${brand.id}">10</td>
<td th:text="${brand.name}">AAA</td>
<td th:text="${#dates.format(brand.createdTime, 'yyyy/MM/dd
HH:mm')}">2020/10/11</td>
</tr>
</tbody>
</table>

其中:
1)${}为thymeleaf为中的EL表达式,用于从服务端model中获取数据
2)th:each为thymeleaf定义的自定义标签属性,用于迭代数据.
3)th:text为thymeleaf定义的自定义标签属性,用于设置文本内容.

启动服务进行访问测试并对结果进行分析

启动服务,打开浏览器输入指定url,进行访问,其数据呈现过程,如图所示:
image.png

启动及运行过程中的Bug分析及解决方案

  • 页面元素解析异常,如图所示:

image.png

  • 模板页面找不到,如图所示:

image.png

品牌模块删除业务分析及实现

业务描述

在品牌列表页面中,点击当前行记录后面的删除按钮,基于当前行的记录id执行品牌删除操作,删除成功以后,重新刷新页面.如图所示:

image.png

业务时序分析与设计

客户端向服务端发起删除请求,服务器端的处理时序如下:

image.png

数据逻辑对象(DAO)中删除方法设计及实现

基于业务,在BrandDao接口中添加删除方法,代码如下:

int deleteById(Integer id);

基于删除方法,定义SQL映射(本次直接以注解方式进行定义),代码如下:

@Delete("delete from tb_brand where id=#{id}")
int deleteById(Integer id);

在BrandDaoTests单元测试类中添加单元测试方法,对删除操作进行测试,关键代码如下

@Test
void testDeleteById(){
int rows=brandDao.deleteById(10);
System.out.println("rows="+rows);
}

业务逻辑对象(Service)中删除方法设计及实现

在业务逻辑对象方法中,要处理删除操作需要的一些业务逻辑(后续有参数校验,权限控制,....).

第一步:在BrandService接口中添加,品牌删除的业务方法,代码如下:

int deleteById(Integer id);

第二步:在BrandServiceImpl类中添加删除业务的具体实现,代码如下:

public int deleteById(Integer id){
//1.参数校验
//2.执行删除业务
int rows=brandDao.deleteById(id);
//3.校验结果并返回
return rows;
} 

第三步:在BrandServiceTests类中添加单元测试方法,对其删除业务做测试?

@Test
void testDeleteById(){
int rows=brandService.deleteById(10);
System.out.println("row="+row);
}

第四步:测试过程中的Bug分析?

控制逻辑对象(Controller)中删除方法设计及实现

在控制层对象中定义处理删除请求的方法,具体代码如下:

@GetMapping("/brand/doDeleteById/{id}")
public String doDeleteById(@PathVariable Integer id,Model model){
brandService.deleteById(id);
List<Brand> list=brandService.findBrands();
model.addAttribute("list",list);
return "brand/brand";
} 

客户端删除按钮事件定义及处理

在tbody的tr中添加一列,代码如下:

<td>
<button type="button" th:onclick="doDeleteById([[${brand.id}]])">delete</button>
</td>

定义javascript函数,处理删除事件,代码如下:

function doDeleteById(id){
//给出提示信息
if(!confirm("您确认删除吗"))return;//confirm为浏览器中window对象的函数
//执行删除业务
location.href=`http://localhost/brand/doDeleteById/${id}ss`;
}

品牌模块添加业务分析及实现

业务描述

在列表页面上,设计添加按钮,当点击添加按钮时,跳转到添加页面,然后在添加页面上数据品牌信息,点击Save按钮就数据提交到服务端进行保存.

image.png

image.png

添加时序分析及设计

image.png

数据逻辑对象(Dao)中方法设计及实现

第一步:在BrandDao中添加用于保存品牌信息的方法,代码如下:

int insertBrand(Brand brand);

第二步:在BrandMapper中添加品牌保存操作对应的sql映射,代码如下:

<insert id="insertBrand">
insert into tb_brand
(name,remark,createdTime)
values
(#{name},#{remark},now())
</insert>

业务逻辑对象(Service)中方法设计及实现

第一步:在BrandService业务接口中中定义用于保存品牌信息的方法,代码如下:

int saveBrand(Brand brand);

第二步:在BrandServiceImpl业务实现类中添加保存品牌信息的具体实现,代码如下:

public int saveBrand(Brand brand){
int rows=brandDao.insertBrand(brand);
return rows;
}

控制逻辑对象(Controller)中方法设计及实现

第一步:在BrandController中添加用于处理请求添加页面的方法,代码如下:

@GetMapping("/brand/doAddUI")
public String doAddUI(){
return "brand/brand-add";
}

第二步:在BrandController添加用于处理添加品牌信息页面的方法,代码如下:

@PostMapping("/brand/doSaveBrand")
public String doSaveBrand(Brand brand,Model model){
System.out.println("save.brand="+brand);
brandService.saveBrand(brand);
List<Brand> list=brandService.findBrands(null);
model.addAttribute("list",list);
return "brand/brand";
}

品牌添加操作客户端业务分析、设计及实现.

第一步:设计品牌添加页面brand-add.html,代码如下

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
</head>
<body>
<div class="container">
<h1>The Brand Add Page</h1>
<form th:action="@{/brand/doSaveBrand}" method="post">
<div class="form-group">
<label for="nameId">Name</label>
<input type="text" class="form-control" name="name" id="nameId" placeholder="Brand Name">
</div>
<div class="form-group">
<label for="remarkId">Remark</label>
<textarea class="form-control" rows="5" cols="100" name="remark" id="remarkId">
</textarea>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</body>
</html>

第二步:在品牌列表brand.html页面,设计添加按钮,代码如下:

<button type="button" onclick="doAddUI()" class="btn btn-primary btn-sm">Add Brand</button>

第三步:点击添加按钮时,加载品牌添加页面,事件处理函数如下:

function doAddUI(){
location.href="/brand/doAddUI";
}

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

image.png

image.png

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

  • 客户端提交到服务端的数据拿不到?

image.png

品牌模块修改业务分析及实现

业务描述

在品牌列表页面,点击当前行的修改按钮,先基于id查询当前行记录,并将记录呈现在编辑页面,如图所示:

image.png

image.png

业务时序分析与设计

基于id查询品牌信息并呈现在页面上,其时序分析如图所示:

image.png

在品牌编辑页面,编辑数据,点击save按钮保存更新,其时序如图所示:
image.png

数据逻辑对象(Dao)中方法设计与实现

在BrandDao中添加基于id查询品牌信息的方法及SQL映射,代码如下:

@Select("select * from tb_brand where id=#{id}")
Brand findById(Integer id);

在BrandDao中添加基于id执行品牌更新的方法及SQL映射,代码如下:

 @Update("update tb_brand set name=#{name},remark=#{remark} where id=#{id}")
int updateBrand(Brand Brand);

业务逻辑对象(Service)中方法设计与实现

在BrandService 中添加基于id查询品牌信息和更新品牌信息的方法,代码如下:

Brand findById(Integer id);
int updateBrand(Brand brand);

在BrandServiceImpl中基于id查询品牌信息和更新品牌信息的方法,代码如下:

 @Override
public Brand findById(Integer id) {
//.....
return brandDao.findById(id);
}
 @Override
public int updateBrand(Brand brand) {
return brandDao.updateBrand(brand);
}

控制逻辑对象(Controller)中方法设计与实现

在BrandController中添加基于id查询品牌信息的方法,代码如下:

 @RequestMapping("/brand/doFindById/{id}")
public String doFindById(@PathVariable Integer id,Model model) {
Brand brand=brandService.findById(id);
model.addAttribute("brand",brand);
return "/brand/brand-update";
}

在BrandController中添加更新品牌信息的方法,代码如下:

 @RequestMapping("/brand/doUpdateBrand")
public String doUpdateBrand(Brand brand,Model model) {
brandService.updateBrand(brand);
List<Brand> list=brandService.findBrands(null);
model.addAttribute("list",list);
return "brand/brand";
}

客户端品牌编辑页面设计与实现

第一步:设计品牌修改页面brand-update.html,代码如下

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
</head>
<body>
<div class="container">
<h1>The Brand Update Page</h1>
<input type="hidden" name="id" th:value="${brand.id}">
<form th:action="@{/brand/doUpdateBrand}" method="post">
<div class="form-group">
<label for="nameId">Name</label>
<input type="text" class="form-control" name="name" id="nameId" th:value="${brand.name}" placeholder="Brand Name">
</div>
<div class="form-group">
<label for="remarkId">Remark</label>
<textarea class="form-control" rows="5" cols="100" name="remark" th:text="${brand.remark}" id="remarkId">
</textarea>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</body>
</html>

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

启动服务,先进入品牌列表页面,然后点击修改按钮如图所示:
image.png
此时,进入品牌编辑页面,如图所示:
image.png
在品牌编辑页面,编辑数据以后,点击save按钮,执行更新操作。

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

  • 405 异常

image.png

  • 呈现的数据不正确

image.png

  • 页面元素解析异常

image.png

总结(Summary)

本章节,主要基于学过的springboot,Hikaricp,MyBatis,Spring,Thymeleaf等技术,对商品品牌模块做了具体实现。重点掌握其基本设计及实现过程。

版权声明
本文为[小韩]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000037780913

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云