springboot模拟天猫整站,分类管理系统后端设计实现(查询)
11/21
(本文依照how2j思路,个人实现代码,仅供个人学习记录)
今天是做分类管理系统,用的工具是:
最新版IDEA(2020.2)
首先老套路,建立好基础的maven项目之后,在pom文件中设置各种依赖。
值得注意的是:
1.本项目学习的是1.5版本的springboot
2.mysql本人用的是5.1.47,其中在这里有个坑,就是有关时区的坑,后面讲述
3.删除src下的java和resources
4.开干
思路:
首先浏览器*问路径 /admin
2. 这个路径被 AdminPageController 的admin方法匹配,然后客户端跳转到 admin_category_list
3. admin_category_list 被 AdminPageController 的 listCategory 方法匹配,服务端跳转到 admin/listCategory.html
4. listCategory.html 这个html页面通过http协议传输到浏览器端
5. 浏览器根据html 上的js代码,异步调用 categories 这个地址。 CategoryController 获取捕捉到这个请求,到数据库里查出所有的分类数据,并转换为 json数组返回给浏览器。
6. 浏览器根据这个json数组,通过 vue 的v-for 方式把其遍历到 多个 tr 元素上,用户就看到了表格里的多条数据了。
编写程序
首先是pom.xml文件,依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 更换为1.5.9版本的springboot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lzf</groupId>
<artifactId>springboot_tmall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_tmall</name>
<description>Demo project for Spring Boot</description>
<!-- 设置jdk默认为1.8-->
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- 设置springboot web架构-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 设置提供Tomcat支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- 设置热部署,让springboot自动更新-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 设置缓存redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 设置springboot的测试支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 使用thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 使用elasticsearch-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- 与elasticsearch绑定的jna-->
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
<version>3.0.9</version>
</dependency>
<!-- legacyHTML5支持-->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
<!-- 设置junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 设置Tomcat支持-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.23</version>
</dependency>
<!-- 设置mysql版本-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>
<!-- commons-lang-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- 设置shiro依赖,用于加密-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- hsqldb-->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
目录如下:
然后建立java和resources路径
先创建application.properties配置数据库的链接与各种配置
#database设置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/tmall_springboot?serverTimezone=Asia/Shanghai&allowMultiQueries=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto = none
#thymeleaf配置
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
#context
server.context-path=/tmall_springboot
#设置上传文件大小,默认只有1 m
spring.http.multipart.maxFileSize=100Mb
spring.http.multipart.maxRequestSize=100Mb
#jpa对实体类的默认字段会把驼峰命名的属性,转换为字段名的时候自动加上下划线。 这个配置的作用就是去掉下划线
#比如属性名称是 createDate, jpa 默认转换为字段名 create_Date。 有了这个配置之后,就会转换为同名字段 createDate
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#显示 hibernate运行的 sql 语句
spring.jpa.show-sql=true
然后顺便把文件里面前端的东西copy到项目中
现在项目目录是这个样子的:
实体类pojo.Category
package com.lzf.tmall.pojo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javax.persistence.*;
@Entity
@Table(name = "category")
//这里我们做的是前后端的分离,前后端交互所使用的的是json格式
//所以我们在这里把handler和hibernateLazyInitializer这两个无需json化的属性忽略掉
@JsonIgnoreProperties({"handler","hibernateLazyInitializer"})
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
int id;
String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
编写dao层
package com.lzf.tmall.dao;
import com.lzf.tmall.pojo.Category;
import org.springframework.data.jpa.repository.JpaRepository;
//直接使用jpa的特性,不用编写分页和CRUD
public interface CategoryDao extends JpaRepository<Category,Integer> {
}
编写service层
package com.lzf.tmall.service;
import java.util.List;
import com.lzf.tmall.dao.CategoryDao;
import com.lzf.tmall.pojo.Category;
//这里sort的导包不要导错
import org.springframework.data.domain.Sort;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//抛弃了接口+Impl实现类的组合方式,直接使用Service类来作为实现类
@Service
public class CategoryService {
//自动装配dao层的对象
@Autowired
CategoryDao categoryDao;
//创建sort对象来进行ID的DESC(倒序)然后填充到categoryDao中
public List<Category> list(){
Sort sort = new Sort(Sort.Direction.DESC, "id");
return categoryDao.findAll(sort);
}
}
编写控制器AdminPageController
package com.lzf.tmall.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AdminPageController {
//访问admin路径的时候,就会从admin跳转到admin_category_list
//然后再跳转到admin_category_list
@GetMapping(value = "/admin")
public String admin(){
return "redirect:admin_category_list";
}
@GetMapping(value = "/admin_category_list")
public String listCategory(){
return "admin/listCategory";
}
}
因为是做前后端分离,所以数据是通过 RESTFUL接口来取的,而在业务上,除了 RESTFUL 服务要提供,还要提供页面跳转服务,所以所有的后台页面跳转都放在 AdminPageController 这个控制器里。 而RSTFUL 专门放在 Category 对应的控制器 CategoryController.java 里面。
编写控制器CategoryController
package com.lzf.tmall.web;
import com.lzf.tmall.pojo.Category;
import com.lzf.tmall.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class CategoryController {
@Autowired
CategoryService categoryService;
@GetMapping("/categories")
public List<Category> list() throws Exception{
// 因为是声明为 @RestController, 所以这个list集合,又会被自动转换为 JSON数组抛给浏览器。
return categoryService.list();
}
}
编写配置类
package com.lzf.tmall.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CORSConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
//令所有的请求都允许跨域
/*
因为是二次请求,第一次是获取 html 页面,
第二次通过 html 页面上的 js 代码异步获取数据,
一旦部署到服务器就容易面临跨域请求问题,
所以允许所有访问都跨域,就不会出现通过 ajax 获取数据获取不到的问题了
*/
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*");
}
}
编写异常处理类
package com.lzf.tmall.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@ControllerAdvice
public class GloabalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public String defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
e.printStackTrace();
Class constraintViolationException =
Class.forName("org.hibernate.exception.internal.CacheSQLExceptionConversionDelegate");
if (null != e.getCause() && constraintViolationException == e.getCause().getClass()) {
return "违反了外键的约束";
}
return e.getMessage();
}
}
编写启动类,先别急着启动main方法
package com.lzf.tmall;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
整个系统的目录现在是这个样子的:
先编写测试类,用jdbc来添加十条数据库数据
package com.lzf.tmall.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
//不要导错包
import java.sql.Statement;
public class TestTmall {
public static void main(String args[]){
//准备分类测试数据:
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/tmall_springboot?useUnicode=true&characterEncoding=utf8",
"root", "admin");
Statement s = c.createStatement();
)
{
for (int i = 1; i <=10 ; i++) {
String sqlFormat = "insert into category values (null, '测试分类%d')";
String sql = String.format(sqlFormat, i);
s.execute(sql);
}
System.out.println("已经成功创建10条分类测试数据");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
然后启动测试类:
可以看出已经导入了十条测试数据。
然后我们再启动启动类
表示启动成功无报错,打开我们的测试地址:
http://localhost:8080/tmall_springboot/admin
显示成功!
该分类系统就已经做完了。
接下来说中间的错误
错误都主要来源于pom文件中的依赖错误,还有数据库的时区设置问题,这方面我也搞不清楚,一会时区出错一会不出错,我的设置是如下的:
spring.datasource.url=jdbc:mysql://localhost:3306/tmall_springboot?serverTimezone=Asia/Shanghai&allowMultiQueries=true&characterEncoding=UTF-8
让他设置为上海的时区就可以了!
这个子系统的主要难点在于controller层的理解和config层的配置。