springboot

SpringBoot

课程目录

  • 初始SpringBoot框架
  • SpringBoot快速搭建和入门
  • SpringBoot访问静态资源文件
  • SpringBoot全局异常处理
  • SpringBoot中YAML和Properties详解
  • SpringBoot中使用Freemarker
  • SpringBoot中使用Thymeleaf
  • SpringBoot整合SpringDataJPA
  • SpringBoot整合MyBatis(含Generator版本)
  • SpringBoot整合MongoDB
  • SpringBoot整合Redis
  • SpringBoot集成邮箱服务
  • SpringBoot之定时任务详解

为什么要使用SpringBoot

  • 依赖太多了, 且存在版本问题
  • 配置太多了且每次都一样, 大部分工程, 配置每次都是一样的, 从一个地方拷贝到另外一个地方. 且Spring发展10多年, 各种配置版本太多, 对于很多程序员来说, 分不清哪个是有效, 哪个无效.
  • 部署太麻烦. 需要tomcat部署, 项目结构也需要照着Java EE的目录结构来写

什么是SpringBoot(重点面试题)

  • springboot是一个快速整合第三方的框架
  • 底层是采用的maven继承的方式实现的
  • springboot简化了Spring的应用开发,采用约定大于配置的理念
  • 开箱即用,简化了xml的配置,完全注解化
  • 内置有Http服务器(Netty和Tomcat),帮助开发者能够实现快速开发
  • SpringBoot的Web组件 默认集成的是SpringMVC框架 SpringMVC是控制层

SpringBoot是如何发展出来的

这个要从Spring的发展史讲起

Spring 1.x 时代
在 Spring1.x 时代,都是通过 xml 文件配置 bean,随着项目的不断扩大,需要将 xml 配置分放到不同的配置文件中,需要频繁的在 java 类和 xml 配置文件中切换。

Spring 2.x 时代
随着 JDK 1.5 带来的注解支持,Spring2.x 可以使用注解对 Bean 进行申明和注入,大大的减少了 xml 配置文件,同时也大大简化了项目的开发。
那么,问题来了,究竟是应该使用 xml 还是注解呢?
最佳实践:
- 应用的基本配置用 xml,比如:数据源、资源文件等
- 业务开发用注解,比如:Service 中注入 bean 等

Spring 3.x 时代
从 Spring3.x 开始提供了 Java 配置方式,使用 Java 配置方式可以更好的理解你配置的 Bean,现在我们就处于这个时代,并且 Spring4.x 和 Spring boot 都推荐使用 java 配置的方式。

Spring 5.x 时代
Spring5.x 是 Java 界首个支持响应式的 Web 框架,是 Spring 的一个重要版本,距离 Spring4.x 差不多四年。在此期间,大多数增强都是在 SpringBoot 项目中完成的,其最大的亮点就是提供了完整的端到端响应式编程的支持(新增 Spring WebFlux 模块)。

Spring WebFlux 同时支持使用旧的 Spring MVC 注解声明 `Reactive Controller`。和传统的 `MVC Controller` 不同,`Reactive Controller` 操作的是 **非阻塞** 的 `ServerHttpRequest` 和 `ServerHttpResponse`,而不再是 Spring MVC 里的 HttpServletRequest 和 HttpServletResponse。

至此也代表着 Java 正式迎来了响应式异步编程的时代。

SpringBoot和SpringMVC的关系

SpringBoot的Web组件 默认集成的是SpringMVC框架。SpringMVC是控制层。
讲到SpringBoot,肯定有不少人会联想到SpringCloud

SpringBoot和SpringCloud关系

SpringCloud和SpringBoot是依赖关系,SpringCloud不能独立存在,必须依赖于SpringBoot组件,使用SpringMVC编写Http协议接口,同时SpringBoot+SpringCloud才是一套完整的微服务解决框架。

SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架

讲到SpringCloud,可能大家又会联想到另一个名词,它就是微服务

微服务

2014就有这么一个概念,英文: martin fowler

可能2014年就有这个微服务的概念了,但是火的时候还是这两年,尤其是2018年,因为SpringBoot的快速发展带动了微服务的发展

接下来我们聊一聊微服务的发展历程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EOuFJYK1-1613632564256)(D:/叮当猫/上课资料/教案/常见设计模式/assets/wps1.png)]

单一应用架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。

此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键。

垂直应用架构

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。

此时,用于加速前端页面开发的 Web框架(MVC) 是关键。

· 分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。

此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键。

· 流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。

此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键。

微服务架构

其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。

微服务架构 = 80%的SOA服务架构思想 + 100%的组件化架构思想 + 80%的领域建模思想

SpringBoot的优缺点

优点

  • 快速构建项目
  • 对主流开发框架的无配置集成
  • 项目可独立运行,无需外部依赖 Servlet 容器
  • 提供运行时的应用监控
  • 极大地提高了开发、部署效率
  • 与云计算的天然集成

缺点

  • 版本迭代速度很快,一些模块改动很大
  • 由于不用自己做配置,报错时很难定位
  • 网上现成的解决方案比较少

系统要求

  • jdk1.8:Spring Boot2.0要求JDK必须是1.8及以上

  • maven3.5.4:Spring Boot2.0要求Maven必须3.5以上

  • IntelliJIDEA2018.2:你们随便,我的反正是用的这个

  • SpringBoot 2.2.1.RELEASE:2.2.1;

  • MySQL8.0.13:建议使用这个版本的MySQL

  • Spring Framework 4.1.5及以上

第一个SpringBoot项目的创建

1.创建一个Maven工程(youruike_springboot)

2.导入SpringBoot相关的依赖


<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.2.1.RELEASEversion>
parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
dependencies>

3.编写主程序启动SpringBoot项目

@SpringBootApplication
public class Application {
     
    public static void main(String[] args) {
     
        // Spring应用启动起来
        SpringApplication.run(Application.class, args);
    }
}

4.编写Controller测试SpringBoot

@RestController
public class HelloController {
     
    
    @RequestMapping("/hello")
    public String hello(){
     
        return "Hello SpringBoot and youruike!";
    }
}

5.打包成jar


<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
        plugin>
    plugins>
build>

导入这个maven插件,利用idea打包,生成的jar包,可以使用java -jar xxx.jar启动

Spring Boot 使用嵌入式的Tomcat无需再配置Tomcat

SpringBoot项目解读

  • pom.xml配置文件
<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.2.1.RELEASEversion>
parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
dependencies>

**spring-boot-starter-parent:**Spring Boot的版本仲裁中心,以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖仍然需要声明版本号)

**spring-boot-starter-web:**spring-boot场景启动器,帮我们导入了web模块正常运行所依赖的组件(不需要外置Tomcat,默认端口8080…);

**SpringBoot开发手册:**https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/html/using-spring-boot.html#using-boot-starter

  • 主程序类
@SpringBootApplication
public class Application {
     

    public static void main(String[] args) {
     

        // Spring应用启动起来
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootApplication : Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
     
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
     
  • @SpringBootConfiguration : Spring Boot的配置类,标注在某个类上,表示这是一个Spring Boot的配置类
  • @Configuration : 配置类上来标注这个注解,配置类也是容器中的一个组件@Component
  • @EnableAutoConfiguration:开启自动配置功能

以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效;

@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
     
  • @AutoConfigurationPackage:自动配置包
    • 将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;
  • @Import(EnableAutoConfigurationImportSelector.class),给容器中导入组件
    • EnableAutoConfigurationImportSelector:导入哪些组件的选择器;

工程的目录结构

│  .gitignore
│  pom.xml
│
│
└─src
    ├─main
    │  ├─java
    │  │  └─com
    │  │      └─youruike
    │  │          └─hello
    │  │              └─spring
    │  │                  └─boot
    │  │                          HelloSpringBootApplication.java
    │  │
    │  └─resources
    │      │  application.properties
    │      │
    │      ├─static
    │      └─templates
    └─test
        └─java
            └─com
                └─youruike
                    └─hello
                        └─spring
                            └─boot
                                    HelloSpringBootApplicationTests.java
  • .gitignore:Git 过滤配置文件
  • pom.xml:Maven 的依赖管理配置文件
  • HelloSpringBootApplication.java:程序入口
  • resources:资源文件目录
    • static: 静态资源文件目录
    • templates:模板资源文件目录
    • application.properties:Spring Boot 的配置文件,实际开发中会替换成 YAML 语言配置(application.yml)

pom.xml


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.7.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.ddmzxgroupId>
    <artifactId>springboot1artifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>springboot1name>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
    dependencies>

    
    <build>
        <finalName>springbootfinalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
project>
  • parent:继承了 Spring Boot 的 Parent,表示我们是一个 Spring Boot 工程
  • spring-boot-starter-web:包含了 spring-boot-starter 还自动帮我们开启了 Web 支持

功能演示

@RequestMapping("/hello")
public String sayHello(){
     
    return "Hello SpringBoot";
}

神奇之处

  • 没有配置 web.xml
  • 没有配置 application.xml,Spring Boot 帮你配置了
  • 没有配置 application-mvc.xml,Spring Boot 帮你配置了
  • 没有配置 Tomcat,Spring Boot 内嵌了 Tomcat 容器

Spring Boot 单元测试

@Autowired
private ApplicationContext ioc;

@Test
public void contextLoads() {
     

    String[] names = ioc.getBeanDefinitionNames();
    for (String name:names
        ) {
     
        System.out.println(name);
    }
}

Spring Boot 常用配置

自定义 Banner

我们在 src/main/resources 目录下新建一个 banner.txt

通过 http://patorjk.com/software/taag

网站生成字符串,将网站生成的字符复制到 banner.txt 中

再次运行这个程序

${AnsiColor.BRIGHT_RED}

//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//            佛祖保佑       永无Bug     涨薪升职                    //

关闭特定的自动配置

关闭特定的自动配置使用 @SpringBootApplication 注解的 exclude 参数即可,这里以关闭数据源的自动配置为例

@SpringBootApplication(exclude = {
     DataSourceAutoConfiguration.class})

访问静态资源文件

  • 默认配置

    在我们开发Web应用的时候,需要引用大量的js、css、图片等静态资源。
    Spring Boot默认提供静态资源目录位置需置于classpath下,目录名需符合如下规则:
    1.classpath:/static
    2.classpath:/public
    3.classpath:/resources
    4.classpath:/META-INF/resources
    
    举例:我们在src/main/resources目录下新建 public、resources、static、/META-INF/resources 四个目录,并分别放入 1.jpg 2.jpg 3.jpg 4.jpg 四张图片。然后通过浏览器分别访问:
    1.http://localhost:8080/1.jpg
    2.http://localhost:8080/2.jpg
    3.http://localhost:8080/3.jpg
    3.http://localhost:8080/4.jpg
    地址均可以正常访问,Spring Boot 默认会从 public resources static /META-INF/resources 四个目录里面查找是否存在相应的资源。
    
  • 在application.properties配置

    #静态文件请求匹配方式
    spring.mvc.static-path-pattern=/youruike/**
    #修改默认的静态寻址资源目录 多个使用逗号分隔
    spring.resources.static-locations = classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/mytest/
    

    配置中配置了静态模式为/youruike/**,访问时候就只能通过/youruike/xx 来访问。

    访问地址:http://localhost:8080/youruike/5.jpg

  • 自定义静态资源地址

    MyWebMvcConfiguration  WebMvcConfigurer
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
           
      registry.addResourceHandler("/mystatic/**").addResourceLocations("classpath:/mystatic/");
    }
    

    访问地址:http://localhost:8080/mystatic/6.jpg

SpringBoot实现全局异常处理

通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。

@ControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上

@ExceptionHandler:用于全局处理控制器里的异常。

package com.yrkedu.springboot.error;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandler {
     
    /**
     * 全局异常捕捉处理
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = ArithmeticException.class)
    public Map handleArithmeticException(ArithmeticException  e) {
     
        e.printStackTrace();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("errorCode", "201");
        map.put("errorMsg", "算数异常");
        return map;
    }

    @ResponseBody
    @ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
    public Map handleArithmeticException(ArrayIndexOutOfBoundsException  e) {
     
        e.printStackTrace();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("errorCode", "202");
        map.put("errorMsg", "数组越界");
        return map;
    }

    /**
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map myErrorHandler(Exception e) {
     
        e.printStackTrace();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("errorCode", "101");
        map.put("errorMsg", "未知异常");
        return map;
    }
}

Main.java

@RequestMapping("/hello")
public String hello(){
     
    //int i = 1/0;
    String a = null;
    System.out.println(a.equals("a"));
    return "hello springboot and youruike";
}

YAML和Properties详解

1.Yaml的介绍

SpringBoot使用一个全局的配置文件,配置文件名是固定的;

  • application.properties
  • application.yml
  • bootstrap.properties
  • bootstrap.yml
因为bootstrap.properties的加载是先于application.properties的。

因为bootstrap.yml的加载是先于application.yml的。

配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;

YAML(YAML Ain’t Markup Language)

​ YAML A Markup Language:是一个标记语言

​ YAML isn’t Markup Language:不是一个标记语言

标记语言:

​ 以前的配置文件;大多都使用的是 xxx.xml文件;

​ YAML:以数据为中心,比json、xml等更适合做配置文件;

Yaml的使用


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-configuration-processorartifactId>
    <optional>trueoptional>
dependency>
public class Teacher {
     

    private String name;

    private String introduce;

    public String getName() {
     
        return name;
    }

    public void setName(String name) {
     
        this.name = name;
    }

    public String getIntroduce() {
     
        return introduce;
    }

    public void setIntroduce(String introduce) {
     
        this.introduce = introduce;
    }
}
@Component
@ConfigurationProperties(prefix = "student")
public class Student {
     

    private String name;

    private Integer age;

    private Boolean sex;

    private Date birth;

    private Map<String,Object> address;

    private List<Object> hobbies;

    private Teacher teacher;

    public String getName() {
     
        return name;
    }

    public void setName(String name) {
     
        this.name = name;
    }

    public Integer getAge() {
     
        return age;
    }

    public void setAge(Integer age) {
     
        this.age = age;
    }

    public Boolean getSex() {
     
        return sex;
    }

    public void setSex(Boolean sex) {
     
        this.sex = sex;
    }

    public Date getBirth() {
     
        return birth;
    }

    public void setBirth(Date birth) {
     
        this.birth = birth;
    }

    public Map<String, Object> getAddress() {
     
        return address;
    }

    public void setAddress(Map<String, Object> address) {
     
        this.address = address;
    }

    public List<Object> getHobbies() {
     
        return hobbies;
    }

    public void setHobbies(List<Object> hobbies) {
     
        this.hobbies = hobbies;
    }

    public Teacher getTeacher() {
     
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
     
        this.teacher = teacher;
    }

    @Override
    public String toString() {
     
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", birth=" + birth +
                ", address=" + address +
                ", hobbies=" + hobbies +
                ", teacher=" + teacher +
                '}';
    }
}
student:
  name: "优锐课"
  age: 20
  sex: true
  address: {
     province: "湖南省",city: "长沙市",area: "岳麓区"}
  hobbies:
    - "撩妹"
    - "唱"
    - "跳"
    - "rap"
  teacher:
    name: Ange
    introduce: "长沙吴亦凡,球场蔡徐坤"
  birth: 2012/02/02

@Value的使用

@SpringBootApplication
@RestController
public class YouruikeSpringbootQuicklyApplication {
     

    @Value("${student.name}")
    private String name;

    @RequestMapping("/getName")
    public String getName(){
     
        return name;
    }

    public static void main(String[] args) {
     
        SpringApplication.run(YouruikeSpringbootQuicklyApplication.class, args);
    }
}

多环境profiles配置

spring:
  profiles:
    active: dev

yml支持多文档块方式

server:
  port: 8080
spring:
  profiles:
    active: prod  #指定属于哪个环境

---
server:
  port: 8081
spring:
  profiles: dev


---

server:
  port: 8083
spring:
  profiles: prod  

日志配置

Spring Boot 对各种日志框架都做了支持,我们可以通过配置来修改默认的日志的配置

默认情况下,Spring Boot 使用 Logback 作为日志框架

logging:
  file: logs/log.log
  level: debug

但是也可以使用@Slf4j,使用更加的方便,需要导入jar包

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <version>1.18.10version>
dependency>
private final Logger logger = LoggerFactory.getLogger(YouruikeSpringbootQuicklyApplication.class);

@Value("${student.name}")
private String name;

@RequestMapping("/getName")
public String getName(){
     
    logger.info("info");
    return name;
}

FreeMarker的使用

什么是 FreeMarker?

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据,而在模板之外可以专注于要展示什么数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fhrYQd55-1613632564258)(D:\youruike\SpringBoot\assets\overview.png)]

这种方式通常被称为 MVC (模型 视图 控制器) 模式,对于动态网页来说,是一种特别流行的模式。 它帮助从开发人员(Java 程序员)中分离出网页设计师(HTML设计师)。设计师无需面对模板中的复杂逻辑, 在没有程序员来修改或重新编译代码时,也可以修改页面的样式。

而FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。

FreeMarker 是 免费的, 基于Apache许可证2.0版本发布。

FreeMarker 中文官方参考手册

SpringBoot集成FreeMarker

1.添加依赖

<dependency>
   <groupId>org.springframework.bootgroupId>
   <artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
   <groupId>org.springframework.bootgroupId>
   <artifactId>spring-boot-starter-freemarkerartifactId>
dependency>

2.配置属性文件

# 是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性。
spring.freemarker.allow-request-override=false
# 是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性。
spring.freemarker.allow-session-override=false
# 是否启用模板缓存。
spring.freemarker.cache=false
# 模板编码。
spring.freemarker.charset=UTF-8
# 是否检查模板位置是否存在。
spring.freemarker.check-template-location=true
# Content-Type value.
spring.freemarker.content-type=text/html
# 是否启用freemarker
spring.freemarker.enabled=true
# 设定所有request的属性在merge到模板的时候,是否要都添加到model中.
spring.freemarker.expose-request-attributes=false
# 是否在merge模板的时候,将HttpSession属性都添加到model中
spring.freemarker.expose-session-attributes=false
# 设定是否以springMacroRequestContext的形式暴露RequestContext给Spring’s macro library使用
spring.freemarker.expose-spring-macro-helpers=true
# 是否优先从文件系统加载template,以支持热加载,默认为true
spring.freemarker.prefer-file-system-access=true
# 设定模板的后缀.
spring.freemarker.suffix=.ftl
# 设定模板的加载路径,多个以逗号分隔,默认: 
spring.freemarker.template-loader-path=classpath:/templates/
# 设定FreeMarker keys.
spring.freemarker.settings.template_update_delay=0
spring.freemarker.settings.default_encoding=UTF-8
spring.freemarker.settings.classic_compatible=true

3.编写Controller

@Controller
public class FreemarkController {
     

    @RequestMapping("/")
    public String index(Model model) {
     
        return "index";
    }
}

4.编写ftl页面

 
<html lang="en"> 
<head> 
	<title>SpringBoot + Freemarkertitle> 
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
head> 
<body> 
	<h1>Hello YouRuiKe,h1><br>
	<p>当前时间:${.now?string("yyyy-MM-dd HH:mm:ss.sss")}p>
body> 
html>

常用的freemarker语法

下面详细介绍在ftl模板中如何使用列表、map、字符串、数字、日期、switch以及macro宏指令等语法。

修改下controller,传递一些需要处理的参数

@RequestMapping("/")
public String index(Model model) {
     
    Map map = new LinkedHashMap<>();
    for (int i = 0; i < 5; i++) {
     
        map.put("key" + i, "value" + i);
    }
    model.addAttribute("list", Arrays.asList("string1", "string2", "string3", 								"string4", "string5", "string6"));
    model.addAttribute("map", map);
    model.addAttribute("name", "youruike");
    model.addAttribute("htmlText", "html内						容");
    model.addAttribute("num", 123.012);
    model.addAttribute("null", null);
    model.addAttribute("dateObj", new Date());
    model.addAttribute("bol", true);
    return "index";
}

重写index.ftl


<html lang="en">
<head>
    <title>Freemarker 语法大全title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <style>
        html {
      
            font-size: 14px;
            font-weight: 400;
        }
        .exp {
      
            font-size: 12px;
            color: red;
        }
    style>
head>
<body>
<p>当前时间:${.now?string("yyyy-MM-dd HH:mm:ss.sss")}p>
<dl>
    <dt>list长度:<span class="exp">${list?size}span>dt>
    <dt>列表dt>
    <#list list as item>
        <dd>${item }, 索引:${item_index },hasNext:${item_has_next}dd>
    #list>

    <dt>数字遍历dt>
    <#list 1..3 as item>
        <dd>数字${item}dd>
    #list>

    <dt>mapdt>
    <#list map?keys as key>
        <dd>${map[key]}, 索引:${key_index },hasNext:${key_has_next}dd>
    #list>
dl>
<dl>
    <dt>字符串dt>
    <dd>普通字符串:<span class="exp">${name}span>dd>
    <dd>非html编码:<span class="exp">${htmlText}span>dd>
    <dd>html编码:<span class="exp">${htmlText?html}span>dd>
    <dd>首字母大写:<span class="exp">${name?cap_first}span>dd>
    <dd>首字母小写:<span class="exp">${name?uncap_first}span>dd>
    <dd>全小写:<span class="exp">${name?lower_case}span>dd>
    <dd>全大写:<span class="exp">${name?upper_case}span>dd>
    <dd>去除首位空格:<span class="exp">${name?trim}span>dd>
    <dd>空字符串:<span class="exp">${null?if_exists}span>dd>
    <dd>是否包含某个字符串:<span class="exp">${name?contains("wWw")?string}span>dd>
    <dd>默认值:<span class="exp">${null?default("空值默认")}span>dd>
    <dd>“${name}”字符串长度:<span class="exp">${name?length}span>dd>
    <dd>定义字符串:<span class="exp">str=优锐课<#assign str="优锐课"/>span>dd>
    <dd>字符串拼接(1):<span class="exp">${"字符串拼接 + " + str}span>dd>
    <dd>字符串拼接(2):<span class="exp">${"字符串拼接 + ${str}"}span>dd>
    <dd>字符串截取单个字符(1):<span class="exp">${str[1]}span>dd>
    <dd>字符串截取(2):<span class="exp">${str?substring(1)}span>dd>
    <dd>字符串截取(3):<span class="exp">${str?substring(1,2)}span>dd>
    <dd>indexOf:<span class="exp">${str?index_of("一")}span>dd>
    <dd>split分割字符串:<span class="exp">
    <#list "a|b|c"?split("|") as item>
        ${item}
    #list>
    span>dd>
    <dd>if...elseif...else:
        <span class="exp">
			<#if null == ''>
                匹配if显示
            <#elseif null == '1'>
                匹配elseif显示
            <#else>
                匹配else显示
            #if>
        span>
    dd>
dl>

<dl>
    <dt>switchdt>
    <dd>
        <#switch str>
            <#case "你好">
                匹配“你好”
                <#break >
            <#case "优锐课">
                匹配“优锐课”
                <#break >
            <#default>
                默认匹配
        #switch>
    dd>
dl>

<dl>
    <dt>数字dt>
    <dd>普通数字:<span class="exp">${num}span>dd>
    <dd>数字类型:<span class="exp">${num?string.number}span>dd>
    <dd>货币类型:<span class="exp">${num?string.currency}span>dd>
    <dd>百分比类型:<span class="exp">${num?string.percent}span>dd>
    <dd>格式化数字:<span class="exp">${num?string("#.###")}span>dd>
    <dd>取数字的整数部分:<span class="exp">${num?int}span>dd>
dl>

<dl>
    <dt>运算符dt>
    <dd>不等于:!= <span class="exp">例如:${(1 != 2)?string('1 != 2', '1 == 2')}span>dd>
    <dd>等于:== <span class="exp">例如:${(1 == 1)?string('1 == 1', '1 != 1')}span>dd>
    <dd>大于(1):> <span
                class="exp">例如:${(2 > 1)?string('2 > 1', '2 < 1')}。<strong>注:使用> 时必须加括号,否则可能会被当成普通的标签闭合符号而引起报错strong>span>
    dd>
    <dd>大于(2):gt <span class="exp">例如:${(2 gt 1)?string('2 gt 1', '2 lte 1')}span>dd>
    <dd>大于等于:gte <span class="exp">例如:${(2 gte 2)?string('2 gte 2', '2 lt 2')}span>dd>
    <dd>小于(1):< <span
                class="exp">例如:${(1 < 2)?string('1 < 2', '1 > 2')}。<strong>注:使用< 时必须加括号,否则可能会被当成普通的标签闭合符号而引起报错strong>span>
    dd>
    <dd>小于(2):lt <span class="exp">例如:${(1 lt 2)?string('1 lt 2', '1 gte 2')}span>dd>
    <dd>小于等于:lte <span class="exp">例如:${(2 lte 2)?string('2 lte 2', '2 gt 2')}span>dd>
dl>

<dl>
    <dt>booleandt>
    <dd>普通boolean输出:<span class="exp">${bol}span>dd>
    <dd>boolean判断输出:<span class="exp">${bol?string('true的时候显示','false的时候显示')}span>dd>
dl>

<dl>
    <dt>日期dt>
    <dd>${dateObj?date}dd>
    <dd>${dateObj?time}dd>
    <dd>${dateObj?string("yyyy-MM-dd HH:mm:ss.SSS")}dd>
dl>

<dl>
    <dt>importdt>
    <dd>
        <#import "import.ftl" as importObj>
        <p>${importObj.importStr}p>
        <p>${importObj.importStr1}p>
    dd>
dl>

<dl>
    <dt>macro宏模板dt>
    <dd>
        <@listMacro items=["item1", "item2", "item3"] title="Items">
            nested标签表示可以插入自定义的内容
        @listMacro>
    dd>
    <dd>
        <#macro listMacro title items>
            <p>${title?cap_first}:
            <ul>
                <#list items as item>
                    <li>${item?cap_first}li>
                #list>
            ul>
            <#nested >
        #macro>
    dd>
dl>

<p>includep>
<#include "youruike.ftl">
body>
html>

Thymeleaf的使用

Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点

  • Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板 + 数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
  • Thymeleaf 开箱即用的特性。它提供标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式效果,避免每天套模板、改 JSTL、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
  • Thymeleaf 提供 Spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。

引入依赖

主要增加 spring-boot-starter-thymeleafnekohtml 这两个依赖

  • spring-boot-starter-thymeleaf:Thymeleaf 自动配置
  • nekohtml:允许使用非严格的 HTML 语法

完整的 pom.xml 如下:

<dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-tomcatartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>net.sourceforge.nekohtmlgroupId>
            <artifactId>nekohtmlartifactId>
            <version>1.9.22version>
        dependency>
    dependencies>

使用Thymeleaf实现简单的国际化登录以及CURD

login.properties

login.btn=登 录
login.UserName=用户名
login.PassWord=密 码

login_en_US.properties

login.btn=Login
login.UserName=UserName
login.PassWord=PassWord

login_zh_CN.properties

login.btn=登 录
login.UserName=用户名
login.PassWord=密 码

application.yml

spring:
 thymeleaf:
  cache: false # 开发时关闭缓存,不然没法看到实时页面
  mode: HTML # 用非严格的 HTML
  encoding: UTF-8
  servlet:
   content-type: text/html
 messages:
  basename: i18n.login

Student类

package com.yrkedu.thymeleaf.pojo;

import lombok.Data;

@Data
public class Student {
     

    private String id;

    private String name;

    private String sex;

    private Integer age;
}

package com.yrkedu.thymeleaf.dao;

import com.yrkedu.thymeleaf.pojo.Student;

import java.util.List;
import java.util.Map;

public interface StudentDao {
     

    Map<String,Student> findAll();

    int addStudent(Student student);

    int delStudent(String id);

    Student findById(String id);

    int updStudent(Student student);
}

package com.yrkedu.thymeleaf.dao.impl;

import com.yrkedu.thymeleaf.dao.StudentDao;
import com.yrkedu.thymeleaf.pojo.Student;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository
public class StudentDaoImpl implements StudentDao {
     

    private static Map<String,Student> studentMap = null;
    static {
     
        studentMap = new HashMap<>();
        Student student1 = new Student();
        student1.setId(UUID.randomUUID().toString());
        student1.setName("ange");
        student1.setSex("male");
        student1.setAge(18);
        studentMap.put(student1.getId(),student1);
    }

    @Override
    public Map<String, Student> findAll() {
     
        return studentMap;
    }

    @Override
    public int addStudent(Student student) {
     
        try {
     
            studentMap.put(student.getId(),student);
            return 1;
        }catch (Exception e){
     
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public int delStudent(String id) {
     
        try {
     
            studentMap.remove(id);
            return 1;
        }catch (Exception e){
     
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public Student findById(String id) {
     
        return studentMap.get(id);
    }

    @Override
    public int updStudent(Student student) {
     
        try {
     
            studentMap.put(student.getId(),student);
            return 1;
        }catch (Exception e){
     
            e.printStackTrace();
            return 0;
        }
    }
}
package com.yrkedu.thymeleaf.controller;

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

@Controller
public class LoginController {
     


    @RequestMapping("/gotologin")
    public String gotologin(){
     
        return "login";
    }

    @RequestMapping("/login")
    public String login(String userName,String password){
     
        System.out.println(userName+"---->"+password);
        return "redirect:/findAll";
    }
}
package com.yrkedu.thymeleaf.controller;

import com.yrkedu.thymeleaf.dao.StudentDao;
import com.yrkedu.thymeleaf.pojo.Student;
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 org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.UUID;

@Controller
public class StudentController {
     

    @Autowired
    private StudentDao studentDao;

    @RequestMapping("/findAll")
    public String findAll(Model model){
     
        Map<String, Student> students = studentDao.findAll();
        model.addAttribute("students",students);
        return "index";
    }

    @RequestMapping("/addStudent")
    public String addStudent(Student student){
     
        //return studentDao.addStudent(student);
        student.setId(UUID.randomUUID().toString());
        studentDao.addStudent(student);
        return "redirect:/findAll";
    }

    @RequestMapping("/edit")
    public String edit(String id,Model model){
     
        if(id!=null){
     
            Student student = studentDao.findById(id);
            model.addAttribute("student",student);
        }
        return "edit";
    }

    @RequestMapping("/findById")
    public Student findById(String id){
     
        return studentDao.findById(id);
    }

    @RequestMapping("/updStudent")
    public String updStudent(Student student){
     
        studentDao.updStudent(student);
        return "redirect:/findAll";
    }

    @RequestMapping("/delStudent")
    public String delStudent(String id){
     
        studentDao.delStudent(id);
        return "redirect:/findAll";
    }
}
package com.yrkedu.thymeleaf.resolver;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

public class MyLocaleResolver implements LocaleResolver {
     
    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
     
        String lang = httpServletRequest.getParameter("lang");
        Locale aDefault = Locale.getDefault();
        if (!StringUtils.isEmpty(lang)){
     
            String[] s = lang.split("_");
            aDefault=  new Locale(s[0],s[1]);
        }
        return aDefault;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
     

    }
}
package com.yrkedu.thymeleaf.config;

import com.yrkedu.thymeleaf.resolver.MyLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyConfig implements WebMvcConfigurer {
     

    @Bean
    public LocaleResolver localeResolver(){
     
        return new MyLocaleResolver();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
     
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/META-INF/resources/")
                .addResourceLocations("classpath:/resources/")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/public/");
    }
}

login.html


<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
    <form action="" method="post" th:action="@{/login}">
        <p><span th:text="#{login.UserName}">用户名span> :<input type="text" id="" name="userName" value="" />p>
        <p><span th:text="#{login.PassWord}">密码span>:<input type="password" name="password" />p>
        <input type="submit" value="登录" th:value="#{login.btn}"/>
    form>
    <a href="@{/login}" th:href="@{/gotologin(lang='zh_CN')}">中文a>
    <a href="@{/login}" th:href="@{/gotologin(lang='en_US')}">Englisha>
body>
html>

index.html


<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
   <a href="javascript:void(0)" th:href="@{/edit}">增加学生a>
    <table  border="1" cellspacing="0" cellpadding="10">
        <tr align="left">
            <th width="30%">编号th>
            <th width="20%">姓名th>
            <th width="20%">性别th>
            <th width="20%">年龄th>
            <th width="10%">操作th>
        tr>
        <tr th:each="student : ${students.values()}">
            <td th:text="${student.id}" width="30%">td>
            <td th:text="${student.name}" width="20%">td>
            <td th:text="${student.sex eq 'male'}?'':''" width="20%">td>
            <td th:text="${student.age}" width="20%">td>
            <td width="10%">
                <a href="javascript:void(0)" th:href="@{/edit(id=${student.id})}">修改a>
                <a href="javascript:void(0)" th:href="@{/delStudent(id=${student.id})}">删除a>
            td>
        tr>
    table>
body>
html>

edit.html


<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<form action="" method="post" th:action="${student==null}?@{/addStudent}:@{/updStudent}">
    <input type="hidden" th:value="${student?.id}" name="id"/>
    <p>姓名:<input type="text" name="name" th:value="${student?.name}"/>p>
    <p>性别:<input type="text" name="sex" th:value="${student?.sex}"/>p>
    <p>年龄:<input type="text" name="age" th:value="${student?.age}"/>p>
    <input type="submit" value="提交"/>
form>
body>
html>

SpringBoot整合JDBC

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root

StudentService

@Service
public class StudentServiceImpl implements StudentService {
     

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public int addStudent(Student student) {
     
        return jdbcTemplate.update("INSERT INTO `studentdb`.`student`(`id`, `name`, `sex`, `gradeId`) VALUES (null, ?, ?, ?)",student.getName(),student.getSex(),student.getGradeId());
    }

    @Override
    public int delStudent(Integer id) {
     
        return jdbcTemplate.update("delete from student where id=?",id);
    }

    @Override
    public int updateStudent(Student student) {
     
        return jdbcTemplate.update("UPDATE `studentdb`.`student` SET `name` = ?, `sex` = ?, `gradeId` = ? WHERE `id` = ?",student.getName(),student.getSex(),student.getGradeId(),student.getId());
    }

    @Override
    public Student findStudentById(Integer id) {
     
        Student student = new Student();
        jdbcTemplate.query("select * from student where id = ?", new Object[]{
     id}, new RowCallbackHandler() {
     
            @Override
            public void processRow(ResultSet rs) throws SQLException {
     
                student.setId(rs.getInt("id"));
                student.setName(rs.getString("name"));
                student.setSex(rs.getString("sex"));
                student.setGradeId(rs.getInt("gradeId"));
            }
        });
        return student;
    }

    @Override
    public List<Student> findAll() {
     
        List<Student> students = new ArrayList<>();
        jdbcTemplate.query("select * from student", new RowCallbackHandler() {
     
            @Override
            public void processRow(ResultSet rs) throws SQLException {
     
                Student student = new Student();
                student.setId(rs.getInt("id"));
                student.setName(rs.getString("name"));
                student.setSex(rs.getString("sex"));
                student.setGradeId(rs.getInt("gradeId"));
                students.add(student);
            }
        });
        return students;
    }
}

SpringBoot整合SpringDataJPA

SpringData:其实SpringData就是Spring提供了一个操作数据的框架。而SpringData JPA只是SpringData框架下的一个基于JPA标准操作数据的模块。
SpringData JPA:基于JPA的标准数据进行操作。简化操作持久层的代码。只需要编写接口就可以

1、Spring-data-jpa的基本介绍;

2、和SpringBoot整合;

3、基本的使用方式;

4、复杂查询,包括多表关联,分页,排序等;

1.pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-jpaartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>

    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.13version>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintagegroupId>
                <artifactId>junit-vintage-engineartifactId>
            exclusion>
        exclusions>
    dependency>
dependencies>

2.实体类

package com.yrkedu.jpa.pojo;

import lombok.Data;

import javax.persistence.*;

@Entity
@Table(name = "sys_user")
@Data
public class SysUsers {
     
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    private String name;

    private Integer age;

    private String addr;

    @Override
    public String toString() {
     
        return "SysUsers{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
            ", addr='" + addr + '\'' +
            '}';
    }
}

3.service接口

package com.yrkedu.jpa.service;

import com.yrkedu.jpa.pojo.SysUsers;
import org.springframework.data.jpa.repository.JpaRepository;

public interface SysUsersRepository extends JpaRepository<SysUsers,Integer> {
     
    /**
     * JpaRepository
     *
     * T:当前需要映射的实体。 ID:当前映射实体中ID的类型
     */
}

4.测试类

package com.yrkedu.jpa;

import com.yrkedu.jpa.pojo.SysUsers;
import com.yrkedu.jpa.service.SysUsersRepository;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = JpaApplication.class)
class JpaApplicationTests {
     

    @Autowired
    private SysUsersRepository sysUsersRepository;


    @Test
    public void save() {
     
        SysUsers users = new SysUsers();
        users.setName("ange");
        users.setAge(18);
        users.setAddr("长沙");
        sysUsersRepository.save(users);
    }

    @Test
    public void update() {
     
        SysUsers users = new SysUsers();
        users.setId(1);
        users.setName("ange");
        users.setAge(20);
        users.setAddr("长沙岳麓区");
        sysUsersRepository.save(users);
    }

    @Test
    @Transactional
    public void getOne() {
     
        SysUsers users = sysUsersRepository.getOne(1);
        System.out.println(users.toString());
    }

    @Test
    @Transactional
    public void getAll() {
     
        List<SysUsers> users = sysUsersRepository.findAll();
        for (SysUsers user:users) {
     
            System.out.println(user.toString());
        }
    }

    @Test
    public void delete() {
     
        //SysUsers user = sysUsersRepository.getOne(1);
        sysUsersRepository.deleteById(1);
    }

}

Spring Data JPA提供的核心接口

  • Repository 接口
  • CrudRepository 接口
  • pagingAndSortingRepository 接口
  • JpaRepository 接口
  • JpaSpecificationExecutor 接口

Repository接口的使用

该接口给我们提供了两种查询方法:方法名称命名查询方式,基于@Query注解的查询与更新。

方法名称命名查询方式

1.编写接口

public interface SysUsersRepository extends Repository<SysUsers, Integer> {
     
	/**
	 * 方法名称查询方式
	 * 
	 * 名称规则:方法的名称必须遵循驼峰式名称规则 : findBy(关键字) + 属性名称(首字母大写) + 查询条件(首字母大写)
	 */
 
	// 单条件
	List<SysUsers> findByName(String name);
 
	// 多条件(and)
	List<SysUsers> findByNameAndAge(String name, int age);
 
	// 多条件(or)
	List<SysUsers> findByNameOrAge(String name, int age);
 
	// 单条件(like)
	List<SysUsers> findByNameLike(String name);
}

2.编写测试代码

public class RepositoryTest {
     
 
	@Autowired
	private SysUsersRepository sysUsersRepository;
 
	/**
	 * 单条件查询测试
	 */
	@Test
	public void TestfindByName() {
     
		List<SysUsers> ls = this.sysUsersRepository.findByName("ange");
		for (SysUsers sysUsers : ls) {
     
			System.out.println(sysUsers);
		}
 
		List<SysUsers> ls2 = this.sysUsersRepository.findByNameLike("%ange%");
		for (SysUsers sysUsers : ls2) {
     
			System.out.println(sysUsers);
		}
	}
 
	/**
	 * 多条件查询测试
	 */
	@Test
	public void TestfindByNameAndAge() {
     
		List<SysUsers> ls = this.sysUsersRepository.findByNameAndAge("Lucy", 12);
		for (SysUsers sysUsers : ls) {
     
			System.out.println(sysUsers);
		}
 
		List<SysUsers> ls2 = this.sysUsersRepository.findByNameOrAge("Lucy", 12);
		for (SysUsers sysUsers : ls2) {
     
			System.out.println(sysUsers);
		}
	}
}

基于@Query注解的查询与更新

1.编写接口

public interface SysUsersRepositoryQueryAnnotation extends Repository<SysUsers, Integer> {
     
	// 注意点:这种写法语句中 SysUsers 必须是和实体类名称一样 不能是数据里的表名称(sys_users)
	// 底层会对HQL语句就行转换,这种方法nativeQuery默认为false
	@Query("from SysUsers where name = ?")
	List<SysUsers> QueryByNameHQL(String name);
 
	// 注意点:nativeQuery= true 说明这的语句就是正常的SQL语句,底层不会对改语句进行转换
	@Query(value = "select * from sys_users where name = ?", nativeQuery = true)
	List<SysUsers> QueryByNameSQL(String name);
 
	@Query("update SysUsers set name = ? where id =?")
	@Modifying // 需要加上@Modifying Annotation
	void UpdateSysUsersNameById(String name, Integer id);
}

2.编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = JpaApplication.class)
public class RepositoryTest {
     
 
	@Autowired
	private SysUsersRepositoryQueryAnnotation sysUsersRepositoryQueryAnnotation;
 
	@Test
	public void TestQueryByNameHQL() {
     
		List<SysUsers> ls = this.sysUsersRepositoryQueryAnnotation.QueryByNameHQL("ange");
		for (SysUsers sysUsers : ls) {
     
			System.out.println(sysUsers);
		}
 
		List<SysUsers> ls2 = this.sysUsersRepositoryQueryAnnotation.QueryByNameSQL("ange");
		for (SysUsers sysUsers : ls2) {
     
			System.out.println(sysUsers);
		}
	}
 
	@Test
	@Transactional // 注意: @Transactional和@Test一起用的时候事务是自动回滚的
					// 所以需要加上@Rollback(false) 标识不回滚
	@Rollback(false)
	public void TestUpdateSysUsersNameById() {
     
		this.sysUsersRepositoryQueryAnnotation.UpdateSysUsersNameById("ange", 2);
	}
 
}

CrudRepository接口的使用

CrudRepository接口,主要是完成一些增删改查的操作。注意:CrudRepository接口集成了Repository接口

1.编写接口

public interface SysUsersCrudRepository extends CrudRepository<SysUsers, Integer> {
     
    /**
	 * 先不需要写接口
	 */
}

2.编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
     
 
	@Autowired
	private SysUsersCrudRepository sysUsersCrudRepository;
 
	@Test
	public void TestSysUsersCrudRepositorySave() {
     
		SysUsers users = new SysUsers();
		users.setAddr("北京");
		users.setAge(25);
		users.setName("李四");
 
		this.sysUsersCrudRepository.save(users);
	}
 
	@Test
	public void TestSysUsersCrudRepositoryUpdate() {
     
		SysUsers users = new SysUsers();
		users.setId(5);
		users.setAddr("北京海淀区");
		users.setAge(25);
		users.setName("李四");
 
		this.sysUsersCrudRepository.save(users);
	}
 
	@Test
	public void TestSysUsersCrudRepositoryFindOne() {
     
 
		SysUsers users = this.sysUsersCrudRepository.findOne(5);
		System.out.println(users);
 
		List<SysUsers> ls = (List<SysUsers>) this.sysUsersCrudRepository.findAll();
		for (SysUsers sysUsers : ls) {
     
			System.out.println(sysUsers);
		}
	}
 
	@Test
	public void TestSysUsersCrudRepositoryDel() {
     
		this.sysUsersCrudRepository.delete(4);
	}
}

注意:在试用CrudRepository接口的时候无需自己添加@Transactional回滚,因为CrudRepository为需要添加事务的方法已经添加了事务。

PagingAndSortRepository接口使用

pagingAndSortRepository接口,提供了分页与排序的操作,注意:该接口集成了CrudRepository接口。

1.编写接口

public interface SysUsersPagingAndSortRepository extends PagingAndSortingRepository<SysUsers, Integer> {
     
 
}

2.编写测试类

package com.yrkedu.springdata_jpa2;



import com.yrkedu.springdata_jpa2.dao.SysUsersPagingAndSortRepository;
import com.yrkedu.springdata_jpa2.pojo.SysUsers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.List;

@SpringBootTest
public class SysUsersPagingAndSortRepositoryTests {
     

    @Autowired
    @Qualifier("sysUsersPagingAndSortRepository")
    private SysUsersPagingAndSortRepository sysUsersPagingAndSortRepository;

    @Test
    public void TestSysUsersPagingAndSortRepositorySort() {
     

        Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id"); // 定义排序规则
        Sort sort = Sort.by(order); // 封装了排序的规则
        Iterable<SysUsers> ls = this.sysUsersPagingAndSortRepository.findAll(sort);
        for (SysUsers sysUsers : ls) {
     
            System.out.println(sysUsers);
        }
    }

    @Test
    public void TestSysUsersPagingAndSortRepositoryPaging() {
     

        // Pageable:封装了分页的参数:当前页,每页显示的条数,注意:他的当前也开始数
        // Pageable pageable = new PageRequest(page, size, sort);
        // page:当前页 ,size :每页显示的条数,sort: 排序

        Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id"); // 定义排序规则
        Sort sort = Sort.by(order);// 封装了排序的规则

        Pageable pageable = PageRequest.of(0,2);
        Page<SysUsers> page = this.sysUsersPagingAndSortRepository.findAll(pageable);

        System.out.println("总条数:" + page.getTotalElements());
        System.out.println("总页数:" + page.getTotalPages());

        List<SysUsers> ls = page.getContent();
        for (SysUsers sysUsers : ls) {
     
            System.out.println(sysUsers);
        }

    }
}

JpaRepository 接口使用

JpaRepository接口特点:该接口集成PagingAndSortingRepository接口,该接口对继承的父接口中方法的返回值进行适配。

1.编写接口

public interface SysUsersJpaRepository extends JpaRepository<SysUsers, Integer> {
     
 
	/**
	 * Repository
	 * 
	 * T:当前需要映射的实体。 ID:当前映射实体中ID的类型
	 */
}

2.编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
     
 
	@Autowired
	private SysUsersJpaRepository sysUsersJpaRepository;
 
	@Test
	public void TestSysUsersJpaRepository() {
     
 
		Order order = new Order(Direction.DESC, "id");
		Sort sort = new Sort(order);
		List<SysUsers> ls = this.sysUsersJpaRepository.findAll(sort);
		for (SysUsers sysUsers : ls) {
     
			System.out.println(sysUsers);
		}
	}
}

JPASpecificationExecutor接口的使用

JPASpecificationExecutor接口:该接口主要是提供了多条件查询的支持,并且可以在查询中添加分页和排序。

注意:JPASpecificationExecutor接口单独存在,完全独立。使用时需配合其他接口使用。

1.编写接口

public interface SysUserJPASpecificationExecutor extends JpaSpecificationExecutor<SysUsers>, JpaRepository<SysUsers, Integer> {
     
 
}

2.编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
     
 
	@Autowired
	private SysUserJPASpecificationExecutor sysUserJPASpecificationExecutor;
 
	/**
	 * 单条件查询
	 */
	@Test
	public void TestSysUsersJpaRepository() {
     
 
		/**
		 * Specification:用户封装查询条件。
		 */
		Specification<SysUsers> spec = new Specification<SysUsers>() {
     
			@Override
			public Predicate toPredicate(Root<SysUsers> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
     
				/**
				 * Predicate: 封装了单个查询条件; root: 查询对象属性的封装。
				 * query:封装了要查询中的各个部分的信息(eg:select from order by) cb:查询条件的构造器
				 */
				// where name = 'Lucy'
				Predicate pre = cb.equal(root.get("name"), "LucyLily");
 
				return pre;
			}
		};
 
		List<SysUsers> ls = this.sysUserJPASpecificationExecutor.findAll(spec);
		for (SysUsers user : ls) {
     
			System.out.println(user);
		}
	}
 
	/**
	 * 多条件查询
	 */
	@Test
	public void TestSysUsersJpaRepository2() {
     
		Specification<SysUsers> spec = new Specification<SysUsers>() {
     
			@Override
			public Predicate toPredicate(Root<SysUsers> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
     
				// where name = ? and age = ?
 
				List<Predicate> list = new ArrayList<>();
				list.add(cb.equal(root.get("name"), "LucyLily"));
				list.add(cb.equal(root.get("age"), 12));
 
				Predicate[] arr = new Predicate[list.size()];
 
				return cb.and(list.toArray(arr)); // cb.or(list.toArray(arr));
				/**
				 * 第二种多条件方式
				 */
				// return cb.and(cb.equal(root.get("name"), "LucyLily"),cb.equal(root.get("age"), 12));
			}
		};
 
		Sort sort = new Sort(new Order(Direction.DESC, "id"));
		List<SysUsers> ls = this.sysUserJPASpecificationExecutor.findAll(spec, sort);
		for (SysUsers user : ls) {
     
			System.out.println(user);
		}
	}
}

关联映射操作

一对多的关联关系

需求:角色与用户一对多的关联关系

角色:一方

用户:多方

1.创建实体类 SysUsers.java与SysRoles.java

@Entity
@Table(name = "sys_users")
public class SysUsers {
     
 
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Integer id;
 
	@Column(name = "name")
	private String name;
 
	@Column(name = "age")
	private Integer age;
 
	@Column(name = "addr")
	private String addr;
 
	@ManyToOne
	@JoinColumn(name = "rolesId")
	private SysRoles sysRoles;
 
	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 Integer getAge() {
     
		return age;
	}
 
	public void setAge(Integer age) {
     
		this.age = age;
	}
 
	public String getAddr() {
     
		return addr;
	}
 
	public void setAddr(String addr) {
     
		this.addr = addr;
	}
 
	public SysRoles getSysRoles() {
     
		return sysRoles;
	}
 
	public void setSysRoles(SysRoles sysRoles) {
     
		this.sysRoles = sysRoles;
	} 
}
@Entity
@Table(name = "sys_roles")
public class SysRoles {
     
 
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "rolesId")
	private Integer rolesId;
 
	@Column(name = "rolesName")
	private String rolesName;
 
	@OneToMany(mappedBy = "sysRoles")
	private Set<SysUsers> sysUsers = new HashSet<>();
 
	public Integer getRolesId() {
     
		return rolesId;
	}
 
	public void setRolesId(Integer rolesId) {
     
		this.rolesId = rolesId;
	}
 
	public String getRolesName() {
     
		return rolesName;
	}
 
	public void setRolesName(String rolesName) {
     
		this.rolesName = rolesName;
	}
 
	public Set<SysUsers> getSysUsers() {
     
		return sysUsers;
	}
 
	public void setSysUsers(Set<SysUsers> sysUsers) {
     
		this.sysUsers = sysUsers;
	}
}

测试一对多的关联关系(编写测试类)

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
     
 
	@Autowired
	private SysUsersJpaRepository sysUsersJpaRepository;
 
	@Test
	public void TestOneToManyAdd() {
     
		// 创建一个用户
		SysUsers user = new SysUsers();
		user.setAddr("AA....");
		user.setAge(30);
		user.setName("AAAAAA");
 
		// 创建一个角色
		SysRoles role = new SysRoles();
		role.setRolesName("超级管理员");
		// 用户角色关联
		role.getSysUsers().add(user);
		user.setSysRoles(role);
 
		// 保存
		sysUsersJpaRepository.save(user);
	}
 
	@Test
	public void TestOneToManyQuery() {
     
 
		SysUsers user = sysUsersJpaRepository.findOne(8);
		System.out.println(user);
 
		System.out.println(user.getSysRoles().getRolesName());
	}
}

多对多的关联关系

需求:角色与菜单多对多的关联关系

1.编写实体类SysRoles.java与Menus.java

@Entity
@Table(name = "sys_roles")
public class SysRoles {
     
 
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "rolesId")
	private Integer rolesId;
 
	@Column(name = "rolesName")
	private String rolesName;
 
	@OneToMany(mappedBy = "sysRoles")
	private Set<SysUsers> sysUsers = new HashSet<>();
 
	@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
	// @JoinTable映射中间表
	// JoinColumn 当前表的主键所关联中间表中的外键字段
	// inverseJoinColumns
	@JoinTable(name = "sys_roles_menus", joinColumns = @JoinColumn(name = "rolesId"), inverseJoinColumns = @JoinColumn(name = "menusId"))
	private Set<Menus> roles = new HashSet();
 
	public Integer getRolesId() {
     
		return rolesId;
	}
 
	public void setRolesId(Integer rolesId) {
     
		this.rolesId = rolesId;
	}
 
	public String getRolesName() {
     
		return rolesName;
	}
 
	public void setRolesName(String rolesName) {
     
		this.rolesName = rolesName;
	}
 
	public Set<SysUsers> getSysUsers() {
     
		return sysUsers;
	}
 
	public void setSysUsers(Set<SysUsers> sysUsers) {
     
		this.sysUsers = sysUsers;
	}
 
	@Override
	public String toString() {
     
		return "SysRoles [rolesId=" + rolesId + ", rolesName=" + rolesName + "]";
	}
}
// 创建菜单
Menus menu = new Menus();
menu.setMenusName("系统管理");
menu.setPid(0);

Menus menu2 = new Menus();
menu2.setMenusName("基础管理");
menu2.setPid(1);

2.编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
     
 
	@Autowired
	private SysUsersJpaRepository sysUsersJpaRepository;
 
	@Test
	public void TestManyToManyAdd() {
     
 
		// 创建一个角色
		SysRoles sysRoles = new SysRoles();
		sysRoles.setRolesName("项目经理");
 
		// 创建菜单
		Menus menu = new Menus();
		menu.setMenusName("系统管理");
		menu.setPid(0);
 
		Menus menu2 = new Menus();
		menu2.setMenusName("基础管理");
		menu2.setPid(1);
 
		// 关联
		sysRoles.getMenus().add(menu);
		sysRoles.getMenus().add(menu2);
 
		menu.getRoles().add(sysRoles);
		menu2.getRoles().add(sysRoles);
 
		// 保存
		this.sysUsersJpaRepository.save(sysRoles);
	}
 
	@Test
	public void TestManyToManyQuery() {
     
 
		SysRoles roles = this.sysUsersJpaRepository.findOne(2);
		System.out.println(roles);
 
		Set<Menus> set = roles.getMenus();
		for (Menus m : set) {
     
			System.out.println(m);
		}
	}
}

SpringBoot整合MyBatis

pom.xml


<dependency>
    <groupId>io.springfoxgroupId>
    <artifactId>springfox-swagger2artifactId>
    <version>2.7.0version>
dependency>
<dependency>
    <groupId>io.springfoxgroupId>
    <artifactId>springfox-swagger-uiartifactId>
    <version>2.7.0version>
dependency>


<dependency>
    <groupId>com.github.pagehelpergroupId>
    <artifactId>pagehelper-spring-boot-starterartifactId>
    <version>1.2.3version>
dependency>

<resources>
    <resource>
        <directory>src/main/javadirectory>
        <includes>
            <include>**/*.xmlinclude>
        includes>
    resource>
    <resource>
        <directory>src/main/resourcesdirectory>
    resource>
resources>
logging:
  level:
    com.changan.stumgr.mapper.*: debug

#公共配置与profiles选择无关
mybatis:
  typeAliasesPackage: com.changan.stumgr.pojo
  mapperLocations: classpath:mapper/*.xml
  configuration:
    auto-mapping-behavior: full
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/studentdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root
  profiles:
    active: pro

pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  params: count=countSql

server:
  port: 8080

StudentMapper.xml




<mapper namespace="com.changan.stumgr.mapper.StudentMapper">

    <resultMap id="studentMap" type="student">
        
        <id property="id" column="id">id>
        <association property="grade" javaType="Grade">
            <id property="gradeId" column="gradeId">id>
            <result property="gradeName" column="gradeName">result>
        association>
    resultMap>

    <select id="selAllStudent" resultMap="studentMap">
      select s.*,g.gradeName from student s,grade g where s.gradeId = g.gradeId
    select>
mapper>
Springboot解决跨域问题
@Configuration
public class WebConfig implements WebMvcConfigurer {
     
    //跨域配置
    @Bean
    public WebMvcConfigurer corsConfigurer() {
     
        return new WebMvcConfigurer() {
     
            @Override
            //重写父类提供的跨域请求处理的接口
            public void addCorsMappings(CorsRegistry registry) {
     
                //添加映射路径
                registry.addMapping("/**")
                        //放行哪些原始域
                        .allowedOrigins("*")
                        //是否发送Cookie信息
                        .allowCredentials(true)
                        //放行哪些原始域(请求方式)
                        .allowedMethods("GET", "POST", "PUT", "DELETE")
                        //放行哪些原始域(头部信息)
                        .allowedHeaders("*")
                        //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
                        .exposedHeaders("Header1", "Header2");
            }
        };
    }
}

StudentService.java

public interface StudentService {
     

    PageInfo selAllStudent(Integer pageNum);

    int addStudent(Student student);

    int delStudent(Integer id);

    int updateStudent(Student student);

    Student findStudentById(Integer id);
}

StudentServiceImpl.java

@Service
public class StudentServiceImpl implements StudentService {
     
    @Autowired
    private StudentMapper studentMapper;

    @Override
    public PageInfo<Student> selAllStudent(Integer pageNum) {
     
        //pageNum  当前的页码     pageSize:每页显示的条数
        if(pageNum==null){
     //刚开始请求的时候默认为1
            pageNum = 1;
        }
        PageHelper.startPage(pageNum,2);
        List<Student> students = studentMapper.selAllStudent();
        PageInfo<Student> pageInfo = new PageInfo<Student>(students);
        return pageInfo;
    }

    @Override
    public int addStudent(Student student) {
     
        return studentMapper.addStudent(student);
    }

    @Override
    public int delStudent(Integer id) {
     
        return studentMapper.delStudent(id);
    }

    @Override
    public int updateStudent(Student student) {
     
        return studentMapper.updateStudent(student);
    }

    @Override
    public Student findStudentById(Integer id) {
     
        return studentMapper.findStudentById(id);
    }
}

StudentController.java

@Controller
@Api(value = "swagger ui 注释 api 级别")
@Slf4j
public class StudentController {
     

    @Autowired
    private StudentService studentService;

    /**
     * 获取数据使用GetMapping()
     * @return  是一个视图名称  /template/*.html
     */
    @GetMapping("/students")
    @ApiOperation(value = "查询所有学生信息",notes = "查询所有学生信息")
    public String list(Integer pageNum,Model model){
     
        /*Logger logger = LoggerFactory.getLogger(StudentController.class);
        logger.info("213");*/
        //ctrl+F9  实现热部署  直接404
        //mybatis  控制台可以看到打印sql语句
        PageInfo pageInfo = studentService.selAllStudent(pageNum);
        model.addAttribute("pageInfo",pageInfo);
        return "list";
    }

    @RequestMapping("/student/{id}")
    public String delete(@PathVariable("id") Integer id){
     
        studentService.delStudent(id);
        return "redirect:/students";
    }

    @RequestMapping("/edit")
    public String edit(Integer id , Model model){
     
        if(id!=null){
     //说明执行修改操作
            Student student = studentService.findStudentById(id);
            model.addAttribute("student",student);
        }
        return "edit";
    }

    @RequestMapping("/add")
    public String add(Student student){
     
        studentService.addStudent(student);
        return "redirect:/students";
    }

    @RequestMapping("/upd")
    public String upd(Student student){
     
        studentService.updateStudent(student);
        return "redirect:/students";
    }
}

SwaggerConfig.java

@Configuration
@EnableSwagger2
public class SwaggerConfig {
     
    @Bean
    public Docket createRestApi() {
     
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.changan.stumgr.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
     
        return new ApiInfoBuilder()
                .title("Spring Boot中使用Swagger2实现前后端分离开发")
                .description("此项目只是练习如何实现前后端分离开发的小Demo")
                .termsOfServiceUrl("https://blog.csdn.net/ca1993422")
                .contact("聂长安")
                .version("1.0")
                .build();
    }
}

访问: http://localhost:8082/swagger-ui.html#/

list.html

@{/edit(id=${student.id})}
@{/edit/}+${student.id}
<body>
<div id="wrap">
    <div id="left">
        <table border="1" cellspacing="0" cellpadding="10">
            <tr>
                <th>编号th>
                <th>姓名th>
                <th>性别th>
                <th>年级编号th>
                <th>操作th>
            tr>
            
            <tr th:each="student:${pageInfo.list}">
                <td th:text="${student.id}">123td>
                <td th:text="${student.name}">Datatd>
                <td th:text="${student.sex}">Datatd>
                <td th:text="${student.grade.gradeName}">Datatd>
                <td>
                    <a href="javascript:void(0)" th:href="@{/student/}+${student.id}">删除a>
                    <a href="javascript:void(0)" th:href="@{/edit(id=${student.id})}">修改a>
                td>
            tr>
        table>
        <a href="" th:href="@{/students(pageNum=1)}">首页a>
        <a href="" th:href="@{/students(pageNum=${pageInfo.pageNum}-1)}">上一页a>
        <a href="" th:href="@{/students(pageNum=${pageInfo.pageNum}+1)}">下一页a>
        <a href="" th:href="@{/students(pageNum=${pageInfo.pages})}">尾页a>
        总页数:<span th:text="${pageInfo.pages}">span>/当前页数:[[${pageInfo.pageNum}]]
        
    div>
    <a href="javascript:void(0)" th:href="@{/edit}">新增a>
div>

<script src="" type="text/javascript" charset="utf-8">script>
<script type="text/javascript">

script>
body>

edit.html


<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
    <form action="" method="post" th:action="${student==null}?@{/add}:@{/upd}">
        <input type="hidden" th:value="${student?.id}" name="id"/>
        <p>姓名:<input type="text" name="name" th:value="${student?.name}"/>p>
        <p>性别:<input type="text" name="sex" th:value="${student?.sex}"/>p>
        <p>年级:<input type="text" name="gradeId" th:value="${student?.gradeId}"/>p>
        <input type="submit" value="提交"/>
    form>
body>
html>

SpringBoot项目

本课程中将带领大家,通过使用SpringBoot快速搭建前后端分离的电商基础秒杀项目。项目中会通过应用领域驱动型的分层模型设计方式去完成用户otp注册、登陆、查看、商品列表、进入商品详情以及倒计时秒杀开始后下单购买的基本流程

1.创建SpringBoot项目测试是否能够启动

2.进入用户模块开发(新建数据库)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oyIrNCKI-1613632564261)(1568023016807.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vK1OlYlr-1613632564263)(assets/1568023049376.png)]

3.整合mybatis得自动生成器

添加插件


<plugin>
    <groupId>org.mybatis.generatorgroupId>
    <artifactId>mybatis-generator-maven-pluginartifactId>
    <version>1.3.5version>
    <configuration>
        
        <configurationFile>src/main/resources/mybatis-generator.xmlconfigurationFile>
        
        <verbose>trueverbose>
        
        <overwrite>trueoverwrite>
    configuration>
    <executions>
        <execution>
            <id>Generate MyBatis Artifactsid>
            <goals>
                <goal>generategoal>
            goals>
            <phase>generate-sourcesphase>
        execution>
    executions>
    
    <dependencies>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.13version>
        dependency>
    dependencies>
plugin>

编写mybatis-generator.xml文件




<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC"
                        userId="root"
                        password="root">
        jdbcConnection>
        
        <javaModelGenerator targetPackage="com.ddmzx.miaosha.dataobject" targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        javaModelGenerator>
        
        <sqlMapGenerator targetPackage="mapping"  targetProject="src/main/resources">
            <property name="enableSubPackages" value="true" />
        sqlMapGenerator>
        
        
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.ddmzx.miaosha.dao"  targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
        javaClientGenerator>
        <table schema="miaosha"
               tableName="user_info" domainObjectName="UserDO"
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               selectByExampleQueryId="false">
        table>
        <table schema="miaosha"
               tableName="user_password" domainObjectName="UserPasswordDO"
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               selectByExampleQueryId="false"
        >
        table>
    context>
generatorConfiguration>

使用maven进行运行

mybatis-generator:generate

编写控制器进行测试,能否生成执行查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z4AN2nnW-1613632564265)(assets/1568194343075.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMZQ6Ceo-1613632564267)(assets/1568194393478.png)]

UserController.java

@Controller("user")
@RequestMapping("/user")
public class UserController extends BaseController{
     

    @Autowired
    private UserService userService;

    @RequestMapping("/get")
    @ResponseBody
    public CommonReturnType getUser(@RequestParam(name = "id")Integer id) throws BusinessException {
     
        //调用service对应id的用户对象并且返回给前端
        UserModel userModel = userService.getUserById(id);
        /*userModel=null;
        userModel.setEncrptPassWprd("123");*/
        //int i = 5/0;
        if(userModel==null){
     
            throw new BusinessException(EmBusinessError.USER_NOT_EXIST);
        }
        UserVO userVO = convertFormModel(userModel);
        return CommonReturnType.create(userVO);
    }

    public UserVO convertFormModel(UserModel userModel){
     
        if(userModel==null){
     
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userModel,userVO);
        return userVO;
    }
}

UserVO.java

@Data
public class UserVO {
     

    private Integer id;
    private String name;
    private Byte gender;
    private Integer age;
    private String telphone;
}

BaseController.java

public class BaseController {
     

    //定义ExceptionHandler处理我们的异常信息,解决未被Controller吸收的异常
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Object handlerException(HttpServletRequest request, Exception ex){
     
        Map<String,Object> map = new HashMap<>();
        if(ex instanceof BusinessException){
     
            BusinessException businessException = (BusinessException)ex;
            map.put("errCode",businessException.getErrCode());
            map.put("errMsg",businessException.getErrMsg());
        }else{
     
            map.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
            map.put("errMsg",EmBusinessError.UNKNOWN_ERROR.getErrMsg());
        }
        CommonReturnType commonReturnType = CommonReturnType.create(map, "fail");
        return commonReturnType;
    }
}

接口 UserService.java

public interface UserService {
     

    UserModel getUserById(Integer id);
}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {
     
    @Autowired
    private UserDOMapper userDOMapper;

    @Autowired
    private UserPasswordDOMapper userPasswordDOMapper;

    @Override
    public UserModel getUserById(Integer id) {
     
        //调用userDOMapper中的方法获取对应的用户信息
        UserDO userDO = userDOMapper.selectByPrimaryKey(id);
        if(userDO==null){
     
            return null;
        }
        UserModel userModel = convertFromDataObject(userDO, userPasswordDOMapper.selectByUserId(userDO.getId()));
        return userModel;
    }

    public UserModel convertFromDataObject(UserDO userDO, UserPasswordDO userPasswordDO) {
     
        if (userDO == null) {
     
            return null;
        }
        UserModel userModel = new UserModel();
        BeanUtils.copyProperties(userDO, userModel);
        if (userPasswordDO != null) {
     
            userModel.setEncrptPassWprd(userPasswordDO.getEncrptPassword());
        }
        return userModel;
    }
}

UserModel.java

@Data
public class UserModel {
     
    private Integer id;
    private String name;
    private Byte gender;
    private Integer age;
    private String telphone;
    private String registerMode;
    private String thridPartyId;
    private String encrptPassWprd;
}

CommonReturnType.java

@Data
public class CommonReturnType {
     

    //表名对应请求的返回处理结果
    private String status;

    //如果返回success则返回对应的数据
    //如果是fail则返回统一的错误码
    private Object data;

    public static CommonReturnType create(Object data){
     
        return CommonReturnType.create(data,"success");
    }

    public static CommonReturnType create(Object data, String status) {
     
        CommonReturnType type = new CommonReturnType();
        type.setStatus(status);
        type.setData(data);
        return type;
    }
}

CommonError.java 接口

public interface CommonError {

    int getErrCode();

    String getErrMsg();

    CommonError setErrMsg(String errMsg);
}

EmBusinessError.java

public enum EmBusinessError implements CommonError {
     
    //通用错误类型
    PARAMETER_VALIDATION_ERROR(10001, "参数不合法"),

    //未知的错误
    UNKNOWN_ERROR(10002, "服务器异常"),

    //10001开头为用户信息不存在
    USER_NOT_EXIST(20001, "用户不存在");

    private EmBusinessError(int errCode, String errMsg) {
     
        this.errCode = errCode;
        this.errMsg = errMsg;
    }

    private int errCode;

    private String errMsg;

    @Override
    public int getErrCode() {
     
        return this.errCode;
    }

    @Override
    public String getErrMsg() {
     
        return this.errMsg;
    }

    //自定义异常
    @Override
    public CommonError setErrMsg(String errMsg) {
     
        this.errMsg = errMsg;
        return this;
    }
}

BusinessException.java

/**
 * @description:
 * @author: 湖南叮当猫教育科技有限公司--聂长安
 * @contact: QQ1351261434、微信17670753912
 * @date: 2019/9/11 16:54
 * @Copyright 版权归叮当猫公司所有, 私自分享视频和源码属于违法行为
 *
 * 包装器业务类型实现
 */
public class BusinessException extends Exception implements CommonError{
     
    private CommonError commonError;

    //直接接受BusinessException的参数构造业务异常
    public BusinessException(CommonError commonError){
     
        super();
        this.commonError = commonError;
    }

    //自定义异常
    public BusinessException(CommonError commonError,String errMsg){
     
        super();
        this.commonError = commonError;
        this.commonError.setErrMsg(errMsg);
    }

    @Override
    public int getErrCode() {
     
        return this.commonError.getErrCode();
    }

    @Override
    public String getErrMsg() {
     
        return this.commonError.getErrMsg();
    }

    @Override
    public CommonError setErrMsg(String errMsg) {
     
        this.commonError.setErrMsg(errMsg);
        return this;
    }
}
@Autowired
    private HttpServletRequest request;

    @RequestMapping("/getotp")
    @ResponseBody
    public CommonReturnType getOtp(String telphone) throws IOException {
     
        //按照一定的规则生成用户的otp验证码
        Random ran = new Random();
        int i = ran.nextInt(900000);
        i = i+100000;//生成100000-999999
        String otpCode = String.valueOf(i);

        //将otp验证码和用户的手机号码绑定
        request.getSession().setAttribute(telphone,otpCode);

        //通过短信的方式发送验证码给用户
        MobileMessageSend.sendMsg("18973195321");

        return CommonReturnType.create(null);
    }

用户登录注册

public static final String CONTENT_TYPE_FORMED="application/x-www-form-urlencoded";
@CrossOrigin(allowCredentials="true",allowedHeaders="*")
@RequestMapping(value = "/register",method = RequestMethod.POST,consumes = {
     CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType register(@RequestParam(name="telphone") String telphone,
                                     @RequestParam(name="otpCode") String otpCode,
                                     @RequestParam(name="name") String name,
                                     @RequestParam(name="gender") Integer gender,
                                     @RequestParam(name="age") Integer age,
                                     @RequestParam(name="password") String password) throws BusinessException, NoSuchAlgorithmException {
     
        //验证手机号和对应的otpCode是否相符合
        String sessionOtpCode = (String)this.request.getSession().getAttribute(telphone);
        if(!StringUtils.equals(sessionOtpCode,otpCode)){
     
            throw new BusinessException(EmBusinessError.PARAMATER_VALIDATION_ERROR,"短信验证码不匹配");
        }
        //
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setGender(new Byte(String.valueOf(gender)));
        userModel.setAge(age);
        userModel.setTelphone(telphone);
        userModel.setRegisterMode("byphone");
        //MD5Encoder.encode(password.getBytes())
        userModel.setEncrptPassWprd(this.encodeByMD5(password));

        userService.register(userModel);

        return CommonReturnType.create(null);
    }

    public String encodeByMD5(String password) throws NoSuchAlgorithmException {
     
        MessageDigest md = MessageDigest.getInstance("MD5");
        BASE64Encoder base64Encoder = new BASE64Encoder();
        return base64Encoder.encode(md.digest(password.getBytes()));
    }

    @RequestMapping(value = "/getotp",method = RequestMethod.POST,consumes = {
     CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType getOtp(String telphone) throws IOException {
     
        //按照一定的规则生成用户的otp验证码
        Random ran = new Random();
        int i = ran.nextInt(900000);
        i = i+100000;//生成100000-999999
        String otpCode = String.valueOf(i);

        //将otp验证码和用户的手机号码绑定
        request.getSession().setAttribute(telphone,otpCode);

        System.out.println("telphone:"+telphone+"----->otpCode:"+otpCode);

        //通过短信的方式发送验证码给用户
        //MobileMessageSend.sendMsg("18973195321");

        return CommonReturnType.create(null);
    }
@Override
    @Transactional
    public void register(UserModel userModel) throws BusinessException {
     
        if(userModel==null){
     
            throw new BusinessException(EmBusinessError.PARAMATER_VALIDATION_ERROR,"用户对象为空");
        }
        if(StringUtils.isEmpty(userModel.getName())
                ||userModel.getGender()==null
                ||userModel.getAge()==null
                ||StringUtils.isEmpty(userModel.getTelphone())){
     
            throw new BusinessException(EmBusinessError.PARAMATER_VALIDATION_ERROR);
        }
        UserDO userDO = convertFromModel(userModel);
        try {
     
            userDOMapper.insertSelective(userDO);
        } catch (DuplicateKeyException e) {
     
            throw new BusinessException(EmBusinessError.DuplicateKeyException);
        }

        UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel);
        userPasswordDOMapper.insertSelective(userPasswordDO);
    }

    private UserDO convertFromModel(UserModel userModel){
     
        if(userModel==null){
     
            throw null;
        }
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(userModel,userDO);
        return userDO;
    }

    private UserPasswordDO convertPasswordFromModel(UserModel userModel){
     
        if(userModel==null){
     
            throw null;
        }
        UserPasswordDO userPasswordDO = new UserPasswordDO();
        userPasswordDO.setEncrptPassword(userModel.getEncrptPassWprd());
        userPasswordDO.setUserId(userModel.getId());
        return userPasswordDO;
    }
keyProperty="id" useGeneratedKeys="true"

	<head>
		<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript" charset="utf-8">script>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>title>
		<link rel="stylesheet" type="text/css" href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css"/>
		<link rel="stylesheet" type="text/css" href="static/assets/global/css/components.css"/>
		<link rel="stylesheet" type="text/css" href="static/admin/pages/css/login.css"/>
	head>
	<body class="login">
		<div class="content">
			<h3 class="form-title">获取otp信息h3>
			<div  class="control-group">
				<label class="control-label">手机号label>
				<div>
					<input class="form-control" type="text" placeholder="手机号" name="telphone" id="telphone"/>
				div>
			div>
			<div class="form-actions">
				<button class="btn blue" id="getotp" type="submit">
					获取otp短信
				button>
			div>
		div>
	body>
	<script type="text/javascript">
		$(function () {
      
			$("#getotp").click(function () {
      
				var telphone = $("#telphone").val();
				if(telphone==null||telphone==''){
      
					alert("手机号不能为空");
					return false;
				}
				$.ajax({
      
					type:"POST",
					contentType:"application/x-www-form-urlencoded",
					url:"http://localhost:8080/getotp",
					data:{
      
						telphone:$("#telphone").val(),
					},
					success:function (data) {
      
						if(data.status=="success"){
      
							alert("otp已经发送到你的手机上,请注意查收");
							window.location.href="http://127.0.0.1:8848/miaosha/register.html";
						}else{
      
							alert("otp发送失败,原因是"+data.data.errMsg);
						}
					},
					error:function (data) {
      
						alert("otp发送失败,原因是"+data.responseText);
					}
				});
				return false;
			})
		})
	script>
html>


	<head>
		<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript" charset="utf-8">script>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>title>
		<link rel="stylesheet" type="text/css" href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css"/>
		<link rel="stylesheet" type="text/css" href="static/assets/global/css/components.css"/>
		<link rel="stylesheet" type="text/css" href="static/admin/pages/css/login.css"/>
	head>
	<body class="login">
		<div class="content">
			<h3 class="form-title">用户注册h3>
			<div  class="control-group">
				<label class="control-label">手机号label>
				<div>
					<input class="form-control" type="text" placeholder="手机号" name="telphone" id="telphone"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">验证码label>
				<div>
					<input class="form-control" type="text" placeholder="验证码" name="otpCode" id="otpCode"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">用户昵称label>
				<div>
					<input class="form-control" type="text" placeholder="用户昵称" name="name" id="name"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">性别label>
				<div>
					<input class="form-control" type="text" placeholder="性别" name="gender" id="gender"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">年龄label>
				<div>
					<input class="form-control" type="text" placeholder="年龄" name="age" id="age"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">密码label>
				<div>
					<input class="form-control" type="password" placeholder="密码" name="password" id="password"/>
				div>
			div>
			<div class="form-actions">
				<button class="btn blue" id="getotp" type="submit">
					提交注册
				button>
			div>
		div>
	body>
	<script type="text/javascript">
		$(function () {
      
			$("#getotp").click(function () {
      
				var telphone = $("#telphone").val();
				var otpCode = $("#otpCode").val();
				var name = $("#name").val();
				var gender = $("#gender").val();
				var age = $("#age").val();
				var password = $("#password").val();
				if(telphone==null||telphone==''){
      
					alert("手机号不能为空");
					return false;
				}
				if(otpCode==null||otpCode==''){
      
					alert("otpCode不能为空");
					return false;
				}
				if(name==null||name==''){
      
					alert("name不能为空");
					return false;
				}
				if(gender==null||gender==''){
      
					alert("gender不能为空");
					return false;
				}
				if(age==null||age==''){
      
					alert("age不能为空");
					return false;
				}
				if(password==null||password==''){
      
					alert("password不能为空");
					return false;
				}
				$.ajax({
      
					type:"POST",
					contentType:"application/x-www-form-urlencoded",
					url:"http://localhost:8080/register",
					data:{
      
						"telphone":telphone,
						"otpCode":otpCode,
						"name":name,
						"gender":gender,
						"age":age,
						"password":password
					},
					xhrFields: {
       withCredentials: true },
					success:function (data) {
      
						if(data.status=="success"){
      
							alert("注册成功");
						}else{
      
							alert("注册失败,原因是"+data.data.errMsg);
						}
					},
					error:function (data) {
      
						alert("注册失败,原因是:"+data.responseText);
					}
				});
				return false;
			})
		})
	script>
html>

用户登录

@RequestMapping(value = "/login",method = RequestMethod.POST,consumes = {
     CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType login(@RequestParam(name="telphone") String telphone,
                                     @RequestParam(name="password") String password) throws BusinessException, NoSuchAlgorithmException {
     
        //入参校验
        if(org.apache.commons.lang3.StringUtils.isEmpty(telphone)|| org.apache.commons.lang3.StringUtils.isEmpty(password)){
     
            throw new BusinessException(EmBusinessError.PARAMATER_VALIDATION_ERROR);
        }

        //用户登录服务,校验用户登录是否合法
        UserModel userModel = userService.validateLogin(telphone, encodeByMD5(password));

        //将登录凭证加入到session中
        this.request.getSession().setAttribute("IS_LOGIN",true);
        this.request.getSession().setAttribute("LOGIN_USER",userModel);

        return CommonReturnType.create(null);
    }

html页面


	<head>
		<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript" charset="utf-8">script>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>title>
		<link rel="stylesheet" type="text/css" href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css"/>
		<link rel="stylesheet" type="text/css" href="static/assets/global/css/components.css"/>
		<link rel="stylesheet" type="text/css" href="static/admin/pages/css/login.css"/>
	head>
	<body class="login">
		<div class="content">
			<h3 class="form-title">用户登录h3>
			<div  class="control-group">
				<label class="control-label">手机号label>
				<div>
					<input class="form-control" type="text" placeholder="手机号" name="telphone" id="telphone"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">密码label>
				<div>
					<input class="form-control" type="password" placeholder="密码" name="password" id="password"/>
				div>
			div>
			<div class="form-actions">
				<button class="btn blue" id="login" type="submit">
					登录
				button>
				<button class="btn green" id="regiest" type="submit">
					注册
				button>
			div>
		div>
	body>
	<script type="text/javascript">
		$(function () {
      
			$("#regiest").click(function () {
      
				window.location.href="register.html";
			});
			
			$("#login").click(function () {
      
				var telphone = $("#telphone").val();
				var password = $("#password").val();
				if(telphone==null||telphone==''){
      
					alert("手机号不能为空");
					return false;
				}
				if(password==null||password==''){
      
					alert("password不能为空");
					return false;
				}
				$.ajax({
      
					type:"POST",
					contentType:"application/x-www-form-urlencoded",
					url:"http://localhost:8080/login",
					data:{
      
						"telphone":telphone,
						"password":password
					},
					xhrFields: {
       withCredentials: true },
					success:function (data) {
      
						if(data.status=="success"){
      
							alert("登录成功");
						}else{
      
							alert("登录失败,原因是"+data.data.errMsg);
						}
					},
					error:function (data) {
      
						alert("登录失败,原因是:"+data.responseText);
					}
				});
				return false;
			})
		})
	script>
html>

service

@Override
    public UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException {
     
        //通过手机号获取用户登录信息
        UserDO userDO = userDOMapper.selectByTelphone(telphone);
        UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
        UserModel userModel = convertFromDataObject(userDO,userPasswordDO);

        //通过用户信息到密码表中或者密码信息
        if(!StringUtils.equals(userPasswordDO.getEncrptPassword(),encrptPassword)){
     
            throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
        }

        return userModel;
    }

数据校验

package com.ddmzx.miaosha.validator;

import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: 湖南叮当猫教育科技有限公司--聂长安
 * @contact: QQ1351261434、微信17670753912
 * @date: 2019/9/19 18:09
 * @Copyright 版权归叮当猫公司所有, 私自分享视频和源码属于违法行为
 */
public class ValidationResult {
     

    private boolean hasErrors = false;

    private Map<String,String> errorMsgMap = new HashMap<>();

    public boolean isHasErrors() {
     
        return hasErrors;
    }

    public void setHasErrors(boolean hasErrors) {
     
        this.hasErrors = hasErrors;
    }

    public Map<String, String> getErrorMsgMap() {
     
        return errorMsgMap;
    }

    public void setErrorMsgMap(Map<String, String> errorMsgMap) {
     
        this.errorMsgMap = errorMsgMap;
    }

    //实现通用的通过格式化字符串信息获取错误结果的msg方法
    public String getErrMsg(){
     
        return StringUtils.join(errorMsgMap.values().toArray(),",");
    }
}

接口实现

package com.ddmzx.miaosha.validator;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

/**
 * @description:
 * @author: 湖南叮当猫教育科技有限公司--聂长安
 * @contact: QQ1351261434、微信17670753912
 * @date: 2019/9/19 18:13
 * @Copyright 版权归叮当猫公司所有, 私自分享视频和源码属于违法行为
 */
@Component
public class ValidatorImpl implements InitializingBean {
     

    private Validator validator;

    //实现校验方法返回校验结果
    public ValidationResult validate(Object bean){
     
        ValidationResult validationResult = new ValidationResult();
        Set<ConstraintViolation<Object>> constraintSet = validator.validate(bean);
        if(constraintSet.size()>0){
     //说明有错误
            validationResult.setHasErrors(true);
            constraintSet.forEach(constraintViolation->{
     
                String message = constraintViolation.getMessage();
                String propertyName = constraintViolation.getPropertyPath().toString();
                validationResult.getErrorMsgMap().put(propertyName,message);
            });
        }
        return validationResult;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
     
        this.validator = Validation.buildDefaultValidatorFactory().getValidator();
    }
}

使用

private Integer id;
    //不能为空的字符串并且不能为null
    @NotBlank(message = "用户名不能为空")
    private String name;
    @NotNull(message = "性别不能不填写")
    private Byte gender;
    @NotNull(message = "性别不能不填写")
    @Min(value = 0,message = "年龄必须大于0")
    @Max(value = 150,message = "年龄不能超过150岁")
    private Integer age;
    @NotBlank(message = "手机号不能为空")
    private String telphone;
    private String registerMode;
    private String thridPartyId;
    @NotBlank(message = "密码不能为空")
    private String encrptPassWprd;


 ValidationResult result = validator.validate(userModel);
        if(result.isHasErrors()){
     
            throw new BusinessException(EmBusinessError.PARAMATER_VALIDATION_ERROR,result.getErrMsg());
        }

商品模块开发

创建ItemModel

//编号
    private Integer id;

    //商品名称
    @NotBlank(message = "商品名称不能为空")
    private String title;

    //商品价格
    @NotNull(message = "商品价格不能为空")
    @Min(value = 0,message = "商品价格必须大于0")
    private BigDecimal price;

    //商品库存
    @NotNull(message = "商品库存不能不填")
    private Integer stock;

    //商品描述
    @NotBlank(message = "商品描述不能为空")
    private String description;

    //商品销量
    private Integer sales;

    //商品图片路径
    @NotBlank(message = "图片信息不能为空")
    private String imgUrl;

创建表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UYjXopMv-1613632564268)(assets/1568975567028.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BUWv33Xw-1613632564269)(assets/1568975583520.png)]

itemService

public interface ItemService {
     

    //创建商品
    ItemModel createItem(ItemModel itemModel) throws BusinessException;

    //商品列表浏览
    List<ItemModel> listItem();

    //商品详情浏览
    ItemModel getItemById(Integer id);
}

itemServiceImpl

@Service
public class ItemServiceImpl implements ItemService {
     

    @Autowired
    private ValidatorImpl validator;

    @Autowired
    private ItemDOMapper itemDOMapper;

    @Autowired
    private ItemStockDOMapper itemStockDOMapper;

    private ItemDO convertFromItemModel(ItemModel itemModel){
     
        if(itemModel==null){
     
            return null;
        }
        ItemDO itemDO = new ItemDO();
        BeanUtils.copyProperties(itemModel,itemDO);
        return itemDO;
    }

    private ItemStockDO convertStockFromItemModel(ItemModel itemModel){
     
        if(itemModel==null){
     
            return null;
        }
        ItemStockDO itemStockDO = new ItemStockDO();
        itemStockDO.setItemId(itemModel.getId());
        itemStockDO.setStock(itemModel.getStock());
        return itemStockDO;
    }

    @Override
    @Transactional
    public ItemModel createItem(ItemModel itemModel) throws BusinessException {
     
        //校验入参
        ValidationResult result = validator.validate(itemModel);
        if(result.isHasErrors()){
     
            throw new BusinessException(EmBusinessError.PARAMATER_VALIDATION_ERROR,result.getErrMsg());
        }

        //转换itemModel---->itemDO
        ItemDO itemDO = convertFromItemModel(itemModel);

        //写入数据库
        itemDOMapper.insertSelective(itemDO);
        itemModel.setId(itemDO.getId());

        ItemStockDO itemStockDO = convertStockFromItemModel(itemModel);

        itemStockDOMapper.insertSelective(itemStockDO);

        //返回创建完成的对象
        return this.getItemById(itemModel.getId());
    }

    @Override
    public List<ItemModel> listItem() {
     
        return null;
    }

    @Override
    public ItemModel getItemById(Integer id) {
     
        ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);

        ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());

        ItemModel itemModel = convertFromDataObject(itemDO, itemStockDO);
        return itemModel;
    }

    public ItemModel convertFromDataObject(ItemDO itemDO,ItemStockDO itemStockDO){
     
        ItemModel itemModel = new ItemModel();
        BeanUtils.copyProperties(itemDO,itemModel);
        itemModel.setStock(itemStockDO.getStock());
        return itemModel;
    }
}

itemVO

public class ItemVO {
     

    //编号
    private Integer id;

    //商品名称
    private String title;

    //商品价格
    private BigDecimal price;

    //商品库存
    private Integer stock;

    //商品描述
    private String description;

    //商品销量
    private Integer sales;

    //商品图片路径
    private String imgUrl;
}

itemController

@Controller("/item")
@CrossOrigin(allowCredentials="true",allowedHeaders="*")
public class ItemController extends BaseController{
     

    @Autowired
    private ItemServiceImpl itemService;

    @RequestMapping(value = "/createItem",method = RequestMethod.POST,consumes = {
     CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType createItem(@RequestParam(name = "title")String title,
                                       @RequestParam(name = "price") BigDecimal price,
                                       @RequestParam(name = "stock")Integer stock,
                                       @RequestParam(name = "description")String description,
                                       @RequestParam(name = "imgUrl")String imgUrl) throws BusinessException {
     

        //封装service创建商品
        ItemModel itemModel = new ItemModel();
        itemModel.setTitle(title);
        itemModel.setStock(stock);
        itemModel.setPrice(price);
        itemModel.setDescription(description);
        itemModel.setImgUrl(imgUrl);

        ItemModel itemModelForReturn = itemService.createItem(itemModel);
        ItemVO itemVO = convertFromItemModel(itemModelForReturn);
        return CommonReturnType.create(itemVO);

    }

    public ItemVO convertFromItemModel(ItemModel itemModel){
     
        if(itemModel==null){
     
            return null;
        }
        ItemVO itemVO = new ItemVO();
        BeanUtils.copyProperties(itemModel,itemVO);
        return itemVO;
    }
}

html页面


	<head>
		<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript" charset="utf-8">script>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>title>
		<link rel="stylesheet" type="text/css" href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css"/>
		<link rel="stylesheet" type="text/css" href="static/assets/global/css/components.css"/>
		<link rel="stylesheet" type="text/css" href="static/admin/pages/css/login.css"/>
	head>
	<body class="login">
		<div class="content">
			<h3 class="form-title">创建商品h3>
			<div  class="control-group">
				<label class="control-label">商品名label>
				<div>
					<input class="form-control" type="text" placeholder="商品名" name="title" id="title"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">价格label>
				<div>
					<input class="form-control" type="text" placeholder="价格" name="price" id="price"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">库存label>
				<div>
					<input class="form-control" type="text" placeholder="库存" name="stock" id="stock"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">描述label>
				<div>
					<input class="form-control" type="text" placeholder="描述" name="description" id="description"/>
				div>
			div>
			<div  class="control-group">
				<label class="control-label">图片label>
				<div>
					<input class="form-control" type="text" placeholder="图片" name="imgUrl" id="imgUrl"/>
				div>
			div>
			<div class="form-actions">
				<button class="btn blue" id="create" type="submit">
					创建商品
				button>
			div>
		div>
	body>
	<script type="text/javascript">
		$(function () {
      
			$("#create").click(function () {
      
				var title = $("#title").val();
				var price = $("#price").val();
				var stock = $("#stock").val();
				var description = $("#description").val();
				var imgUrl = $("#imgUrl").val();
				if(title==null||title==''){
      
					alert("商品名称不能为空");
					return false;
				}
				if(price==null||price==''){
      
					alert("价格不能为空");
					return false;
				}
				if(stock==null||stock==''){
      
					alert("库存不能为空");
					return false;
				}
				if(description==null||description==''){
      
					alert("描述不能为空");
					return false;
				}
				if(imgUrl==null||imgUrl==''){
      
					alert("图片路径不能为空");
					return false;
				}
				$.ajax({
      
					type:"POST",
					contentType:"application/x-www-form-urlencoded",
					url:"http://localhost:8080/createItem",
					data:{
      
						"title":title,
						"price":price,
						"stock":stock,
						"description":description,
						"imgUrl":imgUrl
					},
					xhrFields: {
       withCredentials: true },
					success:function (data) {
      
						if(data.status=="success"){
      
							alert("创建成功");
						}else{
      
							alert("创建失败,原因是"+data.data.errMsg);
						}
					},
					error:function (data) {
      
						alert("创建失败,原因是:"+data.responseText);
					}
				});
				return false;
			})
		})
	script>
html>

总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-86af5MOc-1613632564270)(assets/1568962693137.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZtGzFjr-1613632564271)(assets/1568962903389.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uiQqbJFH-1613632564272)(assets/1568963016968.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qylP82Qs-1613632564273)(assets/1568963038636.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N3awdN97-1613632564274)(assets/1568963205155.png)]

ice创建商品
ItemModel itemModel = new ItemModel();
itemModel.setTitle(title);
itemModel.setStock(stock);
itemModel.setPrice(price);
itemModel.setDescription(description);
itemModel.setImgUrl(imgUrl);

    ItemModel itemModelForReturn = itemService.createItem(itemModel);
    ItemVO itemVO = convertFromItemModel(itemModelForReturn);
    return CommonReturnType.create(itemVO);

}

public ItemVO convertFromItemModel(ItemModel itemModel){
    if(itemModel==null){
        return null;
    }
    ItemVO itemVO = new ItemVO();
    BeanUtils.copyProperties(itemModel,itemVO);
    return itemVO;
}

}


html页面

```html

	
		
		
		
		
		
		
		
	
	
		

创建商品

总结

[外链图片转存中…(img-86af5MOc-1613632564270)]

[外链图片转存中…(img-FZtGzFjr-1613632564271)]

[外链图片转存中…(img-uiQqbJFH-1613632564272)]

[外链图片转存中…(img-qylP82Qs-1613632564273)]

[外链图片转存中…(img-N3awdN97-1613632564274)]

你可能感兴趣的:(spring,boot,spring,boot)