复习
- spring-boot的使用
- 特征
- 独立的应用程序
- 内嵌式的tomcat、jetty或undertow
- 提供starter依赖,简化构建配置
- 自动配置
- 提供生产级功能,比如:运行指标、健康检查、外化配置
- 绝不使用代码生成与xml
- 原理
- starter依赖
- 简化依赖(依赖传递)
- 自动配置
- 外化配置(将散落在不同的配置文件中的配置,集中到外部的配置文件application.yml中)
- 命名约定(官方:spring-boot-starter-xxx 民间:xxx-spring-boot-starter)
- spring-boot-maven插件
- 运行 mvn spring-boot:run
- 打包 mvn clean package (fatjar) java -jar xxxx.jar
- parent —> spring-boot-starter-parent —> spring-boot-dependencies (依赖版本)
- starter依赖
- 自动配置
- @EnableAutoConfiguration 启动自动配置
- spring-boot在启动时,扫描starter依赖根目录下的META-INF/spring.factories,得到自动配置类的路径
- 自动配置类,@Configuration标记为配置类 @Bean 提供了集成到spring所需要的bean,实现自动配置
- 自动配置类关联一个配置属性类@EnableConfigurationProperties(配置类),可以将配置类的属性用于自动配置
- 配置属性类 @ConfigurationProperties(prefix=”配置前缀”) application.yml中配置项格式就是 配置前缀.属性名;将外化配置文件中的配置读入到该属性类,用于自动配置
- 集成springmvc+mybatis spring-boot三板斧
- 加依赖 starter依赖
- 修改配置文件 application.yml
- 加注解
作业
- 学生管理(增删改查)
- 学生姓名 学号 性别 年龄 专业 入学时间 咨询老师 班级
- 学生注册 / 学生毕业 / 学生分页查询
建表规范
- 学生表,有哪些字段?需求
- 隐性的需求 举例: 用户表的 最后一次登录时间
- 字段命名,英文,下划线分词,见名知意,不要用缩写,不要用拼音
- 字段数据类型的选择
- 关联关系
- 1:1 任意,甚至双边
- 1:m/ m:1 存在多的一方
- m:n 中间表
- 主键,使用int,自增
- 给表和字段加注释
- 有默认字段 raw_add_time记录新增时间 raw_update_time记录更新时间
drop database if exists edu_56;
create database edu_56;
drop table if exists edu_student;
create table edu_student(
student_id int auto_increment primary key,
student_name varchar(64) not null comment '学生姓名',
student_no varchar(20) not null comment '学号',
gender char(1) null comment 'M 男 | F 女 | U 未填写',
age tinyint null comment '年龄',
major varchar(64) not null comment '专业',
register_time datetime not null comment '入学时间',
counselor varchar(64) not null comment '咨询师',
class_name varchar(64) not null comment '班级',
raw_add_time datetime not null default CURRENT_TIMESTAMP comment '记录添加时间',
raw_update_time datetime not null DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '记录更新时间'
)
engine=innodb
DEFAULT CHARSET = utf8
COMMENT ='学生信息表';
关联关系存储
要理解数据库是二维表
一对多、多对一,将关联关系存在多的一方
多对多,将关联关系存成另一张中间表
数据库引擎选择innodb,使用业务无关的自增主键
MyISAM一旦出现系统宕机或者进程崩溃情况容易造成存储数据损坏。此外,频繁读写的InnoDB表,一定要使用具有自增/顺序特征的整型作为显式主键。MySQL :使用业务无关的自增主键,详见下文
控制单表数据量
控制列数量
单表字段数上限控制在20到50之内.字段少而精可以提高并发,IO更高效 (优化InnoDB表BLOB列的存储效率)
原因是存储引擎的API工作时需要在服务器层和存储引擎层之间通过行缓冲格式拷贝数据,然后在服务器层将缓冲内容解码成各个列,这个转换过程的代价是非常高的。如果列太多而实际使用的列又很少的话,有可能会导致CPU占用过高。
平衡范式与冗余
禁止在数据库里存图片和文件
禁止在数据库中使用varbinary
、blob
、text
存储图片和文件.
数据库完整性要求
字段选择合适的数据类型
IP字段
如果是使用的IPV4,则使用int
存储不使用char(15).
在MySQL中提供了INER_ATONO()
和INET_NTOA()
函数来对IP和数字之间进行转换. 前者提供IP到数字的转换后者提供数字到IP的转换.
insert into table column(ipvalues(INET_ATONO('127.0.0.1')) ;
如业务需求需要存储IPV6,可采用varchar(40)
类型.
手机字段
如果考虑到varchar
占用空间大影响查询性能,请使用bigint
来存储手机号码.
- 不要使用
int
,因为int
类型的最大长度不能超过11位 - 如果手机号码中含有地区码,则用
varchar
枚举类型可以使用ENUM,ENUM的内部存储机制是采用TINYINT或SMALLINT(并非CHAR/VARCHAR)。enum
,set
和tinyint
类型使用
注意:ENUM类型扩展性较差,如果新增枚举值,需要修改表字段定义,而且在执行ddl时会对性能有影响金额字段
对于金额字段,统一采用decimal(17,0)
类型,金额以“分”为单位保存.时间字段
时间字段优先考虑datetime
.精确浮点数字段必须使用decimal替代float和double
MySQL中的数值类型(不包括整型)IEEE754浮点数:单精度(float
)、双精度(double
和real
)、 定点数(decimal
和numeric
).float
,double
等非标准类型,在DB中保存的是近似值,而decimal
则以字符串的形式保存数值
写代码的注意点
- 实体类与form对象必须区分开,字段不一定一致,考虑参数能少让用户传就少传(能够从系统获取,session中获取,其他字段解析出来比如身份证可以解析性别生日)
- 套路
- 打日志
- 参数校验,通用解析校验结果方法,ControllerBase
c. 调用service(业务校验),参数适配 BeanUtils<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
d. 异常处理(通过全局异常处理器实现)
e. 返回结果 Result(便捷方法)
demo56.zip
javamvc.zip
Restful风格
REST
- 实体通过网址URL来表示: /product/1 @PathVariable
- 通过HTTP协议的请求方法,表达对一个资源的操作
- GET 查询 @GetMapping
- POST 新增 @PostMapping
- PUT 更新
- DELETE 删除
传参
- formData传参
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
$(function () {
$.ajax({
url:"/test1",
type: 'post',
dataType:'json',
data:{
'username':'zhangsan',
'password':'abc123'
},
success:function(resp){
console.log(resp)
}
});
});
</script>
后端用对象接受 ```java @PostMapping(“test1”) public Result doTest(TestForm testForm){
}log.info("TestForm:{}",testForm);
return Result.success();
2. payload 方式
前端:
```javascript
<script>
$(function () {
$.ajax({
url:"/test2",
type: 'post',
dataType:'json',
data:JSON.stringify({
'username':'zhangsan',
'password':'abc123'
}),
contentType:'application/json;charset=utf-8',
success:function(resp){
console.log(resp)
}
});
});
</script>
后端接收
@PostMapping("test2")
public Result doTest2(@RequestBody TestForm testForm){
log.info("TestForm:{}",testForm);
return Result.success();
}
注意点:
- @RequestBody 只能有一个
- Get/Delete请求不能用@RequestBody
- @RequestBody
- @RequestParam
- @PathVaraible
@SpringBootApplication
核心注解 SpringBootConfiguration + EnableAutoConfiguration + ComponentScan ```java
@SpringBootConfiguration 标记为springboot配置类,即为spring的配置类,意味着可以在Application类里写@Bean注册bean @EnableAutoConfiguration 启动自动配置 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) 包扫描 public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
//指定扫描包路径,不指定时,默认为当前类所在的路径
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
<a name="i4EFL"></a>
# Spring-Boot的一些约定
- 配置文件
- XXXApplication必须直接放在根包下
- resources
- static 静态资源 前端渲染(浏览器执行)
- template 模板 后端渲染(jvm)
<a name="sxsgT"></a>
# 集成mybatisplus
1. 导入依赖
```xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
修改配置
mybatis-plus:
type-aliases-package: com.woniuxy.boot.mybatisplusdemo.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
mapper-locations: classpath*:mappers/**/*.xml
加注解 @MapperScan
代码生成器
加依赖
<!-- mybatis-plus代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
<scope>test</scope>
</dependency>
代码 ```java package com.woniuxy.boot.mybatisplusdemo;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import java.util.ArrayList; import java.util.List; import java.util.Scanner;
public class MybatisPlusGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("Lucas");
gc.setOpen(false);
gc.setDateType(DateType.ONLY_DATE);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
//①数据源
dsc.setUrl("jdbc:mysql://localhost:3306/edu_56?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root123");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
//② 包,留意parent设置为上一级,moduleName最后一级
pc.setParent("com.woniuxy.boot");
pc.setModuleName("mybatisplusdemo");
pc.setController("controller");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setMapper("mapper");
pc.setXml("mappers");
pc.setEntity("entity");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
// String templatePath = “/templates/mapper.xml.ftl”; // 如果模板引擎是 velocity String templatePath = “/templates/mapper.xml.vm”;
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mappers" +
"/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录");
return false;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity(“templates/entity2.java”); // templateConfig.setService(); // templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass(“com.baomidou.ant.common.BaseEntity”); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); // 公共父类 // strategy.setSuperControllerClass(“com.baomidou.ant.common.BaseController”); // 写于父类中的公共字段 // strategy.setSuperEntityColumns(“id”); strategy.setInclude(scanner(“表名,多个英文逗号分割”).split(“,”)); strategy.setControllerMappingHyphenStyle(true);
//③ 表前缀
strategy.setTablePrefix("edu");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
}
1. 运行
1. 插件使用
```java
package com.woniuxy.boot.mybatisplusdemo.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
// 旧版
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
分层架构
集成Thymeleaf
c.jsp就是一个后端模板
jsp因为本身的性能以及安全问题,已经被弃用,新兴的后端模板引擎替换了jsp
后端模板引擎
- 老牌:Velocity、Freemarker
- 新兴:Thymeleaf、Mustache、Groovy
Thymeleaf核心理念:自然模板
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/edu_56
username: root
password: root123
thymeleaf:
cache: false
<!DOCTYPE html>
<!--命名空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello thymeleaf
<div>
hello <span th:text="${username}">游客</span>
<span th:if="${age >18}">
年轻小伙
</span>
<span th:unless="${age >18}">
小朋友
</span>
<ul>
<li th:each="s : ${students}">
<span th:text="${s}">小王</span>
</li>
</ul>
</div>
</body>
</html>
https://www.cnblogs.com/jnba/p/10832878.html
日志框架
作业
需求分析
在线教育网站参考:https://demo.edu.roncoos.com/
介绍及技术架构:https://demo.edu.roncoos.com/blog/article/1266656581010952194
讲师协议,约定了分成:https://demo.edu.roncoos.com/apply
- 设计课程course、章chapter、*节lesson,产出建表sql
- 实现一个课程分页搜索,支持根据课程类别,收费方式进行搜索,spring-boot+mybatis-plus