MP的开发流程

MP(Mybatis Plus)的开发流程

1、添加依赖

基本依赖就是这些,如果后期项目需要,还可以手动添加



4.0.0
com.yan
product
0.0.1-SNAPSHOT
product
Demo project for Spring Boot

1.8
UTF-8
UTF-
8
2.6.13




org.springframework.boot
spring-boot-starter-web



com.baomidou
mybatis-plus-boot-starter
3.5.3.1


com.alibaba
druid-spring-boot-starter
1.2.18


com.mysql
mysql-connector-j
runtime


 
org.springframework.boot
spring-boot-devtools
runtime
true


org.projectlombok
lombok
true



org.springframework.boot
spring-boot-starter-test
test





org.springframework.boot
spring-boot-dependencies
${spring-boot.version}
pom
import






org.apache.maven.plugins
maven-compiler-plugin
3.10.1

1.8
1.8
UTF-8



org.springframework.boot
spring-boot-maven-plugin
${spring-boot.version}

com.yan.ProductApplication
true



repackage

repackage







MP的开发流程_第1张图片

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: 123456
# 和连接池产品紧密相关的配置信息,一般参照旧有项目进行配置,如果没有则使用默认即可
druid:
max-active: 10
min-idle: 3
# MP相关配置
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、定义数据表

实际开发中,从上到下或者从下到上进行开发都可以,一般考虑采用一个方向最佳

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;
create table if not exists tbl_produce(
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;
  • 最近的一个面试热点是auto_increment的处理,例如auto_increment到达上限继续插入数据 MySQL如何处理…
  • 在互联网应用开发中针对类目的处理方法常见的有2种:
    • 如果可以确定三级分类,则创建类目大表、类目小表和产品表
    • 如果类目等级无法提前确定或者分类层数大于3,则考虑使用自关联的方式定义类目表
  • 一般在项目开发中定义表的时候,除了主键约束,其它约束一概不加
  • 在具体的项目实践中一般会预留部分列

4、使用MyBatisX插件针对表进行反向映射,生成对应的mapper接口、业务接口和对应的映射元文件

4.1、首先创建数据源
MP的开发流程_第2张图片4.2、在DataSource窗口中右键点击数据表的名称执行MyBatisX
MP的开发流程_第3张图片
点击Next
MP的开发流程_第4张图片

5、检查反向映射生成的内容,如果有不合理的部分,则需要进行修改

MP的开发流程_第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之间的区别

  • 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应用中需要和前端充分沟通,建议通信的数据规范
    MP的开发流程_第6张图片控制器类的定义
    MP的开发流程_第7张图片7、使用Postman进行测试

MP的开发流程_第8张图片常见问题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.yan.mapper") //注册自动扫描mapper接口,完成mapper接
口的注册
@SpringBootConfiguration //用于声明当前类时一个配置类
public class MybatisPlusConfig {
}

8、针对类目进行分页显示
分页操作可以利用MP中提供的分页插件实现,修改MP的配置类引入分页插件

@MapperScan(basePackages = "com.yan.mapper") //注册自动扫描mapper接口,完成mapper接
口的注册
@SpringBootConfiguration //用于声明当前类时一个配置类
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.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 {
@Autowired //@Resource
private CatalogService catalogService;
@GetMapping("/show")
public JsonResult getByPage(@RequestParam(defaultValue = "1") Integer
page,@RequestParam(defaultValue = "3") Integer size){
PageBean pages=new PageBean();
pages.setPageNum(page);
pages.setRowsPerPage(size);
List catalogList= catalogService.listByPage(pages);
Map map=new HashMap<>();
map.put("pages",pages);
map.put("data",catalogList);
return JsonResult.success("加载成功",map);
}
}

修改业务方法的实现
实际上具体的分页实现很简单,就是在调用查询之前构建IPage类型的对象,然后调用Mapper接口中所提供的支持Page参数的方法即可

@Service
public class CatalogServiceImpl extends ServiceImpl
implements CatalogService{
@Override
public List listByPage(PageBean pages) {
List res=new ArrayList<>();
if(pages==null|| pages.getRowsPerPage()<1){
//查询所有
res=this.getBaseMapper().selectList(null);
}else {
//分页查询
if(pages.getPageNum()<1)
pages.setPageNum(1);
Page 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;
}
}

使用postman测试验证
生成的响应数据为JSON格式的字符串

{
"code": 2000,
"success": true,
"message": null,
"data": {
"pages": {
"pageNum": 1,
"maxPage": 2,
"rowsNum": 3,
"rowsPerPage": 2
},
"data": [
{
"id": 1,
"name": "计算机图书",
"memo": "计算机图书"
},
{
"id": 2,
"name": "烹饪图书",
"memo": "做菜的书"
}
]
}
}

9、针对类目信息发现一般很少修改,但是经常需要执行查询,例如添加商品等操作。比较适合使用redis缓存数据,通过浪费内存以减少数据库的查询此时,从而提供执行性能,应对更高的并发性需求redis缓存开发一般有2种方式,使用注解【推荐】和使用自定义编程实现。缓存可以添加在不同的层面上,一般针对controller缓存,多使用本地缓存Ehcache;如果针对业务层缓存,一般使用支持分布式的Redis;也可以在持久层添加缓存,例如开启的MyBatis的缓存

9.1、首先添加依赖


org.springframework.boot
spring-boot-starter-data-redis


org.apache.commons
commons-pool2

9.2、添加Redis序列化器和反序列化器的配置
@SpringBootConfiguration
public class MyRedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory
factory) {
RedisTemplate template = new RedisTemplate<>(); //创建模板
类对象
RedisSerializer redisSerializer = new StringRedisSerializer();//
创建String序列化类
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer(Object.class); //指定使用jackson工具负责具体的序列化操作
ObjectMapper om = new ObjectMapper(); //创建jackson的核心api的 ObjectMapper
,将bean,list,map,数组等等转成字符串
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 使用objectmapper设置bean的属性,修饰符
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //设置默认
类型
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); //设置将本地时
间转成字符串
jackson2JsonRedisSerializer.setObjectMapper(om); //将核心api objectmapper
设置给jackson
template.setConnectionFactory(factory); // 通过工厂得到连接对象
template.setKeySerializer(redisSerializer); //设置key序列化方式: 转成字符串
template.setValueSerializer(jackson2JsonRedisSerializer); // 设置value序列
化: 字符串
template.setHashValueSerializer(jackson2JsonRedisSerializer); // value
hashmap序列化
return template;
}
}
9.3、添加Redis服务器相关配置
spring:
redis:
host: localhost
port: 6379
lettuce:
pool:
max-active: 8
min-idle: 3
9.4、通过spring框架提供的模板类RedisTemplate调用API操作Redis数据库

在使用redis缓存之前应该充分沟通,定义一个key的标准,或者查询公司旧有标准,注意不能随机起名

@Service
public class CatalogServiceImpl extends ServiceImpl
implements CatalogService {
@Autowired
private RedisTemplate redisTemplate;
private ThreadLocalRandom random = ThreadLocalRandom.current();
@Override
public List listByPage(PageBean pages) {
List res = new ArrayList<>();

if (pages == null || pages.getRowsPerPage() < 1) {
//查询所有
if (redisTemplate.hasKey("catalog::all")) {
//如果在具体开发种比较倾向于使用常量的方式
Object obj = redisTemplate.opsForValue().get("catalog::all");
if (obj != null && obj instanceof List)
res = (List) obj;
} else {
res = this.getBaseMapper().selectList(null);
if (res != null && res.size() > 0) {
//为了避免雪崩问题,所以生存周期引入随机数
int kk = 100 + random.nextInt(100);
redisTemplate.opsForValue().set("catalog::all", res,
Duration.ofSeconds(kk));
}
}
} else {
//分页查询。实际上具体应用中不一定针对分页数据进行缓存 catalog::page::size
if (pages.getPageNum() < 1)
pages.setPageNum(1);
String key = "catalog::" + pages.getPageNum() + "::" +
pages.getRowsPerPage();
if (this.redisTemplate.hasKey(key)) {
Object obj = redisTemplate.opsForValue().get(key);
if (obj != null && obj instanceof List)
res = (List) obj;
if(this.redisTemplate.hasKey(key+"::page")){
obj = redisTemplate.opsForValue().get(key+"::page");
if (obj != null && obj instanceof PageBean) {
PageBean tmp = (PageBean) obj;
BeanUtils.copyProperties(tmp,pages);
}
}
}
if (res == null || res.size() < 1) {
Page 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());
int kk = 100 + random.nextInt(100);
redisTemplate.opsForValue().set(key, res,
Duration.ofSeconds(kk));
redisTemplate.opsForValue().set(key+"::page", pages,
Duration.ofSeconds(kk));
}
}
return res;
}
}
9.5、使用postman测试,可以在控制台上查看是否有对应的SQL语句输出以判断缓存是否生效

你可能感兴趣的:(mybatis)