为什么要使用SpringBoot
什么是SpringBoot(重点面试题)
快速整合第三方
的框架maven继承的方式
实现的约定大于配置
的理念完全注解化
内置有Http服务器
(Netty和Tomcat),帮助开发者能够实现快速开发默认集成的是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的优缺点
优点
缺点
系统要求
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及以上
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项目解读
<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 {
以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效;
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
工程的目录结构
│ .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
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>
spring-boot-starter-web
:包含了 spring-boot-starter
还自动帮我们开启了 Web 支持功能演示
@RequestMapping("/hello")
public String sayHello(){
return "Hello SpringBoot";
}
神奇之处
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
通过@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";
}
1.Yaml的介绍
SpringBoot使用一个全局的配置文件,配置文件名是固定的;
因为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 是一款 模板引擎: 即一种基于模板和要改变的数据,并用来生成输出文本(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 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点
引入依赖
主要增加 spring-boot-starter-thymeleaf
和 nekohtml
这两个依赖
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>
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;
}
}
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接口的使用
该接口给我们提供了两种查询方法:方法名称命名查询方式,基于@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);
}
}
}
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快速搭建前后端分离的电商基础秒杀项目。项目中会通过应用领域驱动型的分层模型设计方式去完成用户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
创建商品