MP的开发流程
1、添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ma</groupId>
<artifactId>product</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>product</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<!-- 表现层 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 业务层缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--持久层 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--- 其它工具类 -->
<dependency> <!-- 支持热启动 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency><!-- 简化值bean的编码 -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.ma.ProductApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
创建项目时添加一些要用到的配置
2、添加核心配置,一般建议使用yaml格式,对应的核心配置文件为resources/application.yml
# 默认使用服务器为tomcat
server:
port: 8080
# 数据库连接池的配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///test?serverTimezone=UTC
username: root
password: 10086
# 和连接池产品紧密相关的配置信息,一般参照旧有项目进行配置,如果没有则使用默认即可
druid:
max-active: 10
min-idle: 3
#MP相关配置
mybatis-plus:
mapper-location: classpath:mapper/*.com
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、定义数据表
实际开发中,从上到下或者从下到上进行开发都可以,一般考虑采用一个方向最佳
在resource文件夹中创建包database创建schame.sql定义数据表:
create table if not exists tbl_product(
id bigint primary key auto_increment,
name varchar(32) not null,
catalog_id bigint,
price numeric(8,2),
store_num int
)engine = innodb default charset utf8;
创建目录:
create table if not exists tbl_catalog(
id bigint primary key auto_increment,
name varchar(32) not null unique,
memo varchar(100)
)engine = innodb default charset utf8;
-
最近的一个面试热点是auto_increment的处理,例如auto_increment到达上限继续插入数据MySQL如何处理?
在MySQL中,当自增字段(如使用auto_increment修饰的字段)达到上限后继续插入数据会发生以下情况:
-
达到上限后插入新数据会引发一个错误。MySQL会抛出异常并报告违反"Duplicate entry"约束错误,因为插入的值与已存在的记录具有相同的自增字段值。
-
自增字段的上限取决于其数据类型。例如,如果使用
int
数据类型,自增字段的上限为232-1(约为21亿)。如果使用`bigint`数据类型,自增字段的上限为264-1(非常大)。
针对达到自增字段上限的情况,你可以考虑以下解决方案:
- 使用更大的数据类型:如果你预计可能到达自增字段上限,可以使用更大的数据类型来存储自增字段的值。例如,将
int
类型更改为bigint
类型,这将使得自增字段的上限非常大,几乎可以无限地插入新的记录。
ALTER TABLE your_table MODIFY column_name BIGINT AUTO_INCREMENT;
- 重新设置自增字段起始值:如果你的自增字段已经达到上限并且你不希望更改其数据类型,你可以通过重新设置自增字段的起始值来继续插入新数据。这样会自动将自增字段的值从指定的值开始递增。
ALTER TABLE your_table AUTO_INCREMENT = new_starting_value;
请注意,重新设置自增字段的起始值时,确保指定的新值不会与已存在的记录冲突。
总之,在MySQL中,当自增字段达到上限时,会引发错误并报告违反重复约束。你可以通过更改数据类型或重新设置自增字段的起始值来解决这个问题。
-
-
在互联网应用开发中针对类目的处理方法常见的有2种:
- 如果可以确定三级分类,则创建类目大表、类目小表和产品表
- 如果类目等级无法提前确定或者分类层数大于3,则考虑使用自关联的方式定义类目表
-
一般在项目开发中定义表的时候,除了主键约束,其它约束一概不加
-
在具体的项目实践中一般会预留部分列
4、使用MyBatisX插件针对表进行反向映射,生成对应的mapper接口、业务接口和对应的映射元文件
4.1、首先创建数据源
4.2、在DataSource窗口中右键点击数据表的名称执行MyBatisX
点击Next
5、检查反向映射生成的内容,如果有不合理的部分,则需要进行修改
MP针对注解提供的主键生成策略
- AUTO:id自增
- NONE: 不设置id生成策略
- INPUT:用户手工输入id
- ASSIGN_ID:雪花算法生成id
- ASSIGN_UUID:以UUID生成算法作为id生成策略
雪花算法生成的是一个64bit大小的整数,一般用于分布式应用中,如果针对数据库进行了横切,如何保证id值的唯一性
- 41bit的时间戳:用来记录时间戳,毫秒级
- 10bit的工作机器id:用来记录工作机器id
- 12bit的序列号:占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID
ASSIGN_UUID
- ASSIGN_UUID是自动生成一个不重复的、长度为32字符长的字符串,也能在分布式系统中使用
6、如果采用的是按需编程的方式进行开发,则下一步建议编写控制器,由控制类的定义过程中总结出业务类所需要的方法
RESTful是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。
-
如果查询则使用GET、如果新创建则POST、如果删除的DELETE、如果修改则PUT
-
get和post之间的区别
GET和POST是HTTP协议中最常见的两种请求方法,它们在以下方面有所区别:
-
请求位置和数据传递:GET方法将请求参数附加在URL的末尾(查询字符串中),而POST方法将请求参数包含在请求的消息体中。GET请求的参数会显示在URL中,对于POST请求,参数不会直接显示在URL中。
-
安全性:GET请求是幂等的,即多次请求对于服务器的状态没有影响,不会产生副作用。POST请求可能会产生副作用,例如向服务器提交表单数据,更新数据库等。因此,POST请求相对于GET请求来说更具有安全性。
-
数据长度限制:由于GET请求的参数附加在URL上,URL的最大长度是有限制的。根据不同的浏览器和服务器配置,通常限制在几千个字符。POST请求的数据放在消息体中,没有像GET请求那样的长度限制。
-
可见性:GET请求的参数在URL中可见,作为查询字符串的一部分。POST请求的参数不会直接在URL中可见,不会被搜索引擎等索引。
-
缓存:GET请求默认可被缓存,而POST请求不可被缓存。GET请求适用于获取资源,且不会改变服务器状态,所以可以允许缓存。POST请求用于向服务器发送数据,可能会更新资源,不适合被缓存。
-
使用场景:GET请求适用于从服务器获取数据,例如获取网页、图片等资源。POST请求适用于向服务器提交数据,例如提交表单、上传文件等操作。
需要注意的是,以上是一些常见的区别,但实际使用中可能还有其他因素需要考虑,例如某些安全性要求、服务器限制等。选择使用GET或POST方法时,要根据具体的场景和需求来进行决策。
-
-
http协议的请求流程以及不同版本的区别
HTTP(Hypertext Transfer Protocol)是一种用于在Web上进行数据传输的协议。下面是HTTP协议的请求流程以及不同版本之间的主要区别:
- 请求流程:
- 客户端发起一个HTTP请求到服务器的特定地址(URL)。
- 请求包括一个方法(GET、POST、PUT等)、请求头(如Accept、Content-Type)和请求体(如POST请求中的表单数据)。
- 服务器接收到请求后进行处理,可以返回相应的数据和状态码。
- 客户端接收到服务器的响应后进行处理,可能是显示响应内容或者执行其他操作。
- HTTP/1.0和HTTP/1.1的区别:
- 持久连接:HTTP/1.0默认使用非持久连接,即每个请求/响应后关闭连接,而HTTP/1.1默认使用持久连接,在同一连接上可以发送多个请求。这减少了每次请求的延迟和额外的连接开销。
- 请求管道化:HTTP/1.1支持请求管道化,即在同一持久连接上可以同时发送多个请求,而HTTP/1.0不支持。这提高了请求响应的效率。
- Host头字段:HTTP/1.1引入了Host头字段,用于指定请求的目标主机,可以支持一个服务器上的多个虚拟主机。
- 错误处理:HTTP/1.1使用更多的状态码,如100 Continue、307 Temporary Redirect等。
- 缓存和代理:HTTP/1.1引入了更多的缓存控制指令和代理机制,以提高缓存的效率和交互的安全性。
- HTTP/2的改进:
- 二进制协议:HTTP/2使用二进制格式传输数据,取代了HTTP/1.x的文本格式,提高了传输效率和解析速度。
- 多路复用:HTTP/2通过在同一连接上使用多个虚拟流来并发传输请求和响应,消除了HTTP/1.x中的队头阻塞问题,提高了并发性能。
- 头部压缩:HTTP/2使用了HPACK算法对请求和响应的头部进行压缩,减少了传输的数据量。
- 服务器推送:HTTP/2支持服务器主动推送,可以在客户端请求之前将相关资源一起发送给客户端,提高加载速度。
- 优化了HTTP语义:HTTP/2优化了某些请求的语义,如将多个CSS文件合并为一个请求。
总结起来,HTTP协议的请求流程包括客户端发起请求、服务器接收并处理请求、服务器返回响应、客户端接收响应。不同版本的HTTP协议在持久连接、请求管道化、Host头字段、错误处理、缓存和代理、二进制协议、多路复用、头部压缩和服务器推送等方面有不同的特点和改进。这些改进旨在提高性能、效率和安全性,以适应不断增长的Web应用需求。
-
http协议和https协议的区别
HTTP(Hypertext Transfer Protocol)和HTTPS(Hypertext Transfer Protocol Secure)是两种用于在Web上进行数据传输的协议,它们之间存在以下主要区别:
- 安全性:
- HTTP是明文传输协议,所有的通信内容都是以明文形式传输,容易被攻击者窃听、篡改或偷取敏感信息。
- HTTPS使用SSL/TLS协议对通信进行加密和身份验证,保证通信的机密性和完整性,使得被传输的数据更加安全。
- 默认端口:
- HTTP的默认端口是80,客户端发送请求时不需要明确指定端口号。
- HTTPS的默认端口是443,客户端发送请求时需要明确指定端口号。
- 证书:
- HTTP不需要证书验证,无法验证服务器的真实身份。
- HTTPS使用数字证书对服务器进行身份验证,客户端可以验证服务器的真实性和合法性,确保通信与合法服务器建立连接。
- 加密:
- HTTP不加密传输数据,容易被第三方截获和篡改,信息安全性无法得到保障。
- HTTPS使用SSL/TLS协议进行数据加密和解密,确保传输过程中的敏感信息不被窃取或篡改。
- 性能开销:
- HTTP没有加密和验证的过程,传输速度较快,性能开销相对较低。
- HTTPS在通信过程中需要进行加密和解密的操作,会增加通信的开销,相对于HTTP而言性能稍低。
总结起来,HTTP是一种不安全的明文传输协议,适用于一些对数据安全性要求不高的场景。而HTTPS通过加密和身份验证,保证了通信的机密性和完整性,适用于需要对敏感信息进行保护的场景。HTTPS在安全性上对比HTTP有明显优势,但在性能方面会存在一些开销。因此,在具体应用场景中需要根据安全性需求和性能要求来选择使用HTTP还是HTTPS。
RESTful的实现等级
- 0级:传统的RPC,基于SOAP的WS,调用的服务名,参数放在HTTP协议的body里面,同时必须以POST方式提交,问题在于你必须清楚的知道所有服务,子服务,及其参数的信息,并且需要知道各种服务的不同点
- 1级:利用resource概念,把所有服务都抽取成resource概念,从body中提取到header里,这样做的好处就是如果你知道一个服务地址,你可能无需知道具体服务是什么,依照资源的惯例就访问到服务,比如查询id=1的书籍信息时使用路径/books/1
- 2级:利用HTTP动词,HTTP定义了4种动词,GET获取服务器资源,POST在服务器上创建新资源,PUT更改服务器上资源,DELETE删除服务器上资源,任何操作都可以看成增删改查,所以利用标准的http verb加上resource(/book/1)就能准确地操作资源,当你不知道服务具体是什么的时候也可以轻易按照惯例访问到服务,然而服务供应商更改服务也需要遵循惯例,不会像RPC那样轻易更改服务接口
- 3级:最高级别,HATEOS超媒体既应用状态引擎。这个意思是说,对于任何服务都存在很多子服务,你只需要知道第一个服务的入口,便可以依据服务返回结构的自描述性得到下一个服务的入口,这样在服务供应商修改服务的时候,不会影响到客户端的调用
在RESTful应用中需要和前端充分沟通,建议通信的数据规范
@Data
public class JsonResult implements Serializable{
private int code;//自定义的响应状态码,不是http规范中的响应码,一般用于给前端更详细的信息
private String message;//服务器端生成的响应提示信息
private Boolean success;//可有可无,给前端一个简单的响应状态提示
private Object data;//响应数据
public static JsonResult success(String message, Object data){
JsonResult result = new JsonResult();
result.setMessage(message);
result.setData(data);
result.setCode(2000);
result.setSuccess(true);
return result;
}
}
控制器类的定义
@RestController //@Controller + @ResponseBody
@RequestMapping("/catalogs")
public class CatalogController{
@Autowired //@Resource
private CatalogService catalogService;
@GetMapping
public JsonResult getAllCatalogs(){
List<Catalog> catalogList = catalogService.list();
return JsonResult.success("所有类目列表",catalogList);
}
}
7、使用Postman进行测试
在mysql数据库中插入测试数据
insert into tbl_catalogs values
(1,"计算机图书","计算机图书"),
(2,"烹饪图书","做菜图谱"),
(3,"音乐图书","教你音乐入门");
postman的用法
常见问题1:应用启动报错UnsatisfiedDependencyException,查看报错信息的详细提示
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'com.yan.mapper.CatalogMapper' available: expected at
least 1 bean which qualifies as autowire candidate. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatc
hingBeanFound(DefaultListableBeanFactory.java:1801) ~[spring-beans-
5.3.23.jar:5.3.23]
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDe
pendency(DefaultListableBeanFactory.java:1357) ~[spring-beans-5.3.23.jar:5.3.23]
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDepe
ndency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.23.jar:5.3.23]
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcesso
r$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.j
ava:656) ~[spring-beans-5.3.23.jar:5.3.23]
... 39 common frames omitted
可以看到报错为com.yan.mapper.CatalogMapper没有注册,所以需要添加MyBatisPlus配置类或者在主类上添加配置
@MapperScan(basePackages = "com.ma.mapper") //注册自动扫描mapper接口,完成mapper接口的注册
@SpringBootConfiguration //用于声明当前类的一个配置类
public class MyBatisPlusConfig{
}
8、针对类目进行分页显示
分页操作可以利用MP中提供的分页插件实现,修改MP的配置类引入分页插件
@MapperScan(basePackages = "com.ma.mapper") //注册自动扫描mapper接口,完成mapper接口的注册
@SpringBootConfiguration //用于声明当前类的一个配置类
public class MyBatisPlusConfig{
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
mybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
intercetpor.addInterceptor(new PaginationInnerInterceptor(ObType.MYSQL);
return interceptor;
}
}
控制器类定义,这里需要传递分页参数。传递分页参数的值bean有多种写法
@Data //主要用于供前端实现分页导航
public class PageBean implements Serializable{
private long pageNum; //当前页码值
private long maxPage; //最大页面值
private long rowsNum; // 总行数
private long rowsPerPage = 15; //每页行数
//另外写法有:添加属性Object data存储查询结果集
}
控制器类
注意:这里新增业务方法listByPage目的在于后前使用redis缓存
@RestController //@Controller + @Responsebody
@RequestMapping("/catalogs")
public class CatalogController{
@Autowird //@Resource
private CatalogService catalogService;
@GetMapping("show")
public JsonResult getByPage(@RrquestParam(defaultValue = "1")Integer
page,@RequestParam(defaultValue="3") Integer size){
PageBean pages = new PageBean();
pages.setPageNum(page);
pages.setRowsPerPage(size);
List<Catalog> catalogList = catalogService.listByPage(pages);
Map<String,Object> map = new HashMap<>();
map.put("pages",pages);
map.put("data",catalogList);
return JsonResult.success("加载成功",map);
}
}
修改业务方法的实现
实际上具体的分页实现很简单,就是在调用查询之前构建IPage类型的对象,然后调用Mapper接口中所提供的支持Page参数的方法即可
@Service
public class CatalogServiceImpl extends ServiceImpl<CatalogMapper, Catalog>
implements CatalogService{
@Override
public List<Catalog> listByPage(PageBean pages){
List<Catalog> res = new ArrayList<>();
if(pages == null || pages.getRowsPerPage() < 1){
//查询所有
res = this.getBaseMapper().selectList(null);
} else{
//分页查询
if(pages.getPageNum() < 1)
pages.setPageNum(1);
Page<Catalog> pageInfo = new Page<>(pages.getPageNum(),pages.getRowsPerPage());
pageInfo = this.getBaseMapper().selectPage(pageInfo,null);
res = pageInfo.getRecords();
pages.setPageNum(pageInfo.getCurrent());
pages.setRowsNum(pageInfo.getTotal());
pages.setMaxPage(pageInfo.getPages());
}
return res;
}
}