SpringBoot六
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.yan</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>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.yan.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: 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;
4、使用MyBatisX插件针对表进行反向映射,生成对应的mapper接口、业务接口和对应的映射元文件
4.1、首先创建数据源
4.2、在DataSource窗口中右键点击数据表的名称执行MyBatisX
5、检查反向映射生成的内容,如果有不合理的部分,则需要进行修改
MP针对注解提供的主键生成策略
AUTO:id自增
NONE: 不设置id生成策略
INPUT:用户手工输入id
ASSIGN_ID:雪花算法生成id
ASSIGN_UUID:以UUID生成算法作为id生成策略
雪花算法生成的是一个64bit大小的整数,一般用于分布式应用中,如果针对数据库进行了横切,如何保证id值的唯一性
41bit的时间戳:用来记录时间戳,毫秒级
10bit的工作机器id:用来记录工作机器id12bit的序列号:占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产
生4096个ID
ASSIGN_UUID
ASSIGN_UUID是自动生成一个不重复的、长度为32字符长的字符串,也能在分布式系统中使用
6、如果采用的是按需编程的方式进行开发,则下一步建议编写控制器,由控制类的定义过程中总结出业务类所需要的方法
RESTful是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,
动作类型为新增、变更、删除所调用资源。如果查询则使用GET、如果新创建则POST、如果删除的DELETE、如果修改则PUT
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应用中需要和前端充分沟通,建议通信的数据规范
在RESTful应用中需要和前端充分沟通,建议通信的数据规范
@Data public class JsonResult implements Serializable { private int code;//自定义的响应状态码,不是http规范中的响应码,一般用于给前端更详细的信 息 private Boolean success; //可有可无,给前端一个简单的响应状态提示 private String message; //服务器端生成的响应提示信息 private Object data;//响应数据 } 控制器类的定义 @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进行测试
插入测试数据
insert into tbl_catalog values(1,'计算机图书','计算机图书'),(2,'烹饪图书','做菜的书'),(3,'音乐图书','教你音乐入门');
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<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; } } 使用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、首先添加依赖 <!-- 业务层缓存 --> <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> 9.2、添加Redis序列化器和反序列化器的配置 @SpringBootConfiguration public class MyRedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); //创建模板 类对象 RedisSerializer<String> 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<CatalogMapper, Catalog> implements CatalogService { @Autowired private RedisTemplate redisTemplate; private ThreadLocalRandom random = ThreadLocalRandom.current(); @Override public List<Catalog> listByPage(PageBean pages) { List<Catalog> res = new ArrayList<>(); 9.5、使用postman测试,可以在控制台上查看是否有对应的SQL语句输出以判断缓存是否生效 注解开发 注解提供了更声明式和方便的方式,而编码方式允许更精细的控制和定制。 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<Catalog>) obj; } else { res = this.getBaseMapper().selectList(null); //为了避免雪崩问题,所以生存周期引入随机数 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<Catalog>) 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<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()); 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; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?