0
点赞
收藏
分享

微信扫一扫

从一例状态引发的性能问题谈Flink状态序列化

文章目录


一、需求&开发流程

Github项目:Springboot3+Mybatis 基础管理系统

  1. 部门管理
    • 查询部门列表
    • 删除部门
    • 新增部门
    • 修改部门
  2. 员工管理
    • 查询员工列表(分页、条件)
    • 删除员工
    • 新增员工
    • 修改员工
  3. 文件上传

1

  1. 查看页面原型明确需求

    • 根据页面原型和需求,进行表结构设计、编写接口文档
  2. 阅读接口文档

  3. 思路分析

  4. 功能接口开发

    • 就是开发后台的业务功能,一个业务功能,我们称为一个接口
  5. 功能接口测试

    • 功能开发完毕后,先通过Postman进行功能接口测试,测试通过后,再和前端进行联调测试
  6. 前后端联调测试

    • 和前端开发人员开发好的前端工程一起测试

二、环境搭建&数据库准备

1
实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {
    private Integer id;
    private String name;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    private static final long serialVersionUID = 1L;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Integer gender;
    private String image;
    private Integer job;
    private LocalDate entrydate; //LocalDate
    private Integer deptId;
    private LocalDateTime createTime;//LocalDateTime 
    private LocalDateTime updateTime;//LocalDateTime 
    private static final long serialVersionUID = 1L;
}

步骤: 【SpringBoot3+Mybatis】框架快速搭建

  1. 准备数据库表(dept、emp)
  2. 创建springboot工程,引入对应的起步依赖(web、mybatis、mysql驱动、lombok)
  3. 准备基本工具类 utils.
  4. 配置文件application.properties中引入mybatis的配置信息,准备对应的实体类
  5. 准备对应的Mapper、Service(接口、实现类)、Controller基础结构

接口使用REST风格:【SpringMVC】RESTFul风格设计和实战 第三期

http://localhost:8080/users/1  GET:查询id为1的用户
http://localhost:8080/users    POST:新增用户
http://localhost:8080/users    PUT:修改用户
http://localhost:8080/users/1  DELETE:删除id为1的用户

通过URL定位要操作的资源,通过HTTP动词(请求方式)来描述具体的操作。

三、部门管理

原型和需求:
1
1
LocalDateTime

四、员工管理

4.1 分页(条件)查询

Vo:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class EmpVo {
    private String name;
    private Short gender;
    private LocalDate begin;//LocalDate  2024-01-01
    private LocalDate end;//LocalDate
    private Integer page;
    private Integer pageSize;
}

controller

@RestController
@RequestMapping("emps")
public class EmpController {

    @Autowired
    private EmpService empService;

    @GetMapping
    public Result queryPage(EmpVo empVo){
        Result result = empService.queryPage(empVo);
        return result;
    }
}

service:

@Service
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;

    @Override
    public Result queryPage(EmpVo empVo) {
        if (empVo.getPage() == null || empVo.getPageSize()== null) {
            PageHelper.startPage(1, 10);
        }else{
            PageHelper.startPage(empVo.getPage(),empVo.getPageSize());
        }

        List<Emp> empList = empMapper.selectBySelective(empVo);

        PageInfo<Emp> empPageInfo = new PageInfo<>(empList);

        Map map = new HashMap();
        map.put("total",empPageInfo.getTotal());
        map.put("rows",empList);

        if (empList.isEmpty()){
            return Result.error("无");
        }

        return Result.success(map);
    }
}

mapperxml

    <select id="selectBySelective" resultType="com.wake.pojo.Emp">
        select *
            from emp
            <where>
                <if test="empVo.name != null">name like concat('%',#{empVo.name},'%')</if>
                <if test="empVo.gender != null">and gender=#{empVo.gender}</if>
                <if test="empVo.end != null and empVo.begin != null ">and entrydate between #{empVo.begin} and #{empVo.end}</if>
            </where>
    </select>

4.2 批量删除员工

1
controller

    /**
     * 批量删除员工的数据信息
     * @param ids
     * @return
     */
    @DeleteMapping("{ids}")
    public Result deleteByIds(@PathVariable Integer[] ids){
        Result result = empService.deleteByIds(ids);
        return result;
    }

service

    @Override
    public Result deleteByIds(Integer[] ids) {
       int rows = empMapper.deleteByIds(ids);

        if (rows > 0) {
            return Result.success(null);
        }

        return Result.error("为空");
    }

mapperxml

    <delete id="deleteByIds">
        delete from emp where id in
        <foreach collection="ids" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </delete>

五、文件上传

5.1 介绍

Spring中提供了一个API:MultipartFile,使用这个API就可以来接收到上传的文件
1

5.2 本地存储

@Slf4j
@RestController
public class UploadController {

    @PostMapping("/upload")
    public Result upload(String username, Integer age, MultipartFile image) throws IOException {
        log.info("文件上传:{},{},{}",username,age,image);

        //获取原始文件名
        String originalFilename = image.getOriginalFilename();

        //构建新的文件名
        String extname = originalFilename.substring(originalFilename.lastIndexOf("."));//文件扩展名
        String newFileName = UUID.randomUUID().toString()+extname;//随机名+文件扩展名

        //将文件存储在服务器的磁盘目录
        image.transferTo(new File("E:/images/"+newFileName));

        return Result.success();
    }
}

上传大文件时报错:
1
添加文件上传的容量配置:

spring:
  servlet:
    multipart:
      max-file-size: 10MB #配置单个文件最大上传大小
      max-request-size: 100MB #配置单个请求最大上传大小(一次请求可以上传多个文件)

本地存储的问题:
1

如果直接存储在服务器的磁盘目录中,存在以下缺点:

  • 不安全:磁盘如果损坏,所有的文件就会丢失
  • 容量有限:如果存储大量的图片,磁盘空间有限(磁盘不可能无限制扩容)
  • 无法直接访问

为了解决上述问题呢,通常有两种解决方案:

  • 自己搭建存储服务器,如:fastDFS 、MinIO
  • 使用现成的云服务,如:阿里云,腾讯云,华为云

5.3 阿里云OSS

1

1. 开通OSS

阿里云——官网
1
1

2. 创建存储空间Bucket

1
1

5.4 OSS快速入门

阿里云OSS使用官方文档

阿里依赖,具体看官方使用文档:

        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.1</version>
        </dependency>

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- no more than 2.3.3-->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.3</version>
        </dependency>

案例代码:

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import java.io.File;

public class Demo {

    public static void main(String[] args) throws Exception {
        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "examplebucket";
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String objectName = "exampledir/exampleobject.txt";
        // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
        // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
        String filePath= "D:\\localpath\\examplefile.txt";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);

        try {
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new File(filePath));
            // 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
            // ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // metadata.setObjectAcl(CannedAccessControlList.Private);
            // putObjectRequest.setMetadata(metadata);
            
            // 上传文件。
            PutObjectResult result = ossClient.putObject(putObjectRequest);           
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

1
1

5.5 OSS上传显示文件

在新增员工的时候,上传员工的图像,而之所以需要上传员工的图像,是因为将来我们需要在系统页面当中访问并展示员工的图像。而要想完成这个操作,需要做两件事:

  1. 需要上传员工的图像,并把图像保存起来(存储到阿里云OSS)
  2. 访问员工图像(通过图像在阿里云OSS的存储地址访问图像)
    • OSS中的每一个文件都会分配一个访问的url,通过这个url就可以访问到存储在阿里云上的图片。所以需要把url返回给前端,这样前端就可以通过url获取到图像。

工具类:(引入外部文件注入,调用get)

package com.wake.utils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

@Component
public class AliOSSUtils {
    //注入配置参数实体类对象
    @Autowired
    private AliOSSProperties aliOSSProperties;

    /**
     * 实现上传图片到OSS
     */
    public String upload(MultipartFile multipartFile) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = multipartFile.getInputStream();

        // 避免文件覆盖
        String originalFilename = multipartFile.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),
                aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());
        ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);

        //文件访问路径
        String url =aliOSSProperties.getEndpoint().split("//")[0] + "//" + aliOSSProperties.getBucketName() + "." + aliOSSProperties.getEndpoint().split("//")[1] + "/" + fileName;

        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }
}

controller:

@RestController
public class UploadController {

    @Autowired
    private AliOSSUtils aliOSSUtils;

    @PostMapping("/upload")
    public Result upload(MultipartFile image) throws IOException {
        //调用阿里云OSS工具类,将上传上来的文件存入阿里云
       String url = aliOSSUtils.upload(image);
        //将图片上传完成后的url返回,用于浏览器回显展示
       return Result.success(url);
    }
}

1
1

六、配置文件

6.1 yml配置

yml配置文件中:

aliyun: #以下参数全部修改成自己的
  oss:
    endpoint: https://oss-cn-fuzhou.aliyuncs.com
    accessKeyId: LTAI5t6Av5GLDxX  #假的修改
    accessKeySecret: C1IrHzKZKvcotD4d5Tc #假的修改
    bucketName: web-wake-work

创建实体类存放字段属性:
1
直接使用注解 @ConfigurationProperties(prefix = "aliyun.oss")
实体类中的属性名和配置文件当中key名字必须要一致

package com.wake.utils;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss") //这样不用一个一个属性挂载@Value
public class AliOSSProperties {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
}

6.2 @ConfigurationProperties

添加注解出现红色提示,添加依赖即可
1

        <!--   @ConfigurationProperties 注解-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>

总结

在这里插入图片描述

第三方服务-通用思路 SDK

在这里插入图片描述
SDK:Software Development Kit 的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等,都可以叫做SDK。

yml配置文件全

server:
  servlet:
    context-path: /

spring:
  datasource:
    # 连接池类型
    type: com.alibaba.druid.pool.DruidDataSource  # 使用Druid连接池

    # Druid的其他属性配置 springboot3整合情况下,数据库连接信息必须在Druid属性下!
    druid:
      url: jdbc:mysql://localhost:3306/db01_mybatis
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
  servlet:
    multipart:
      max-file-size: 10MB #配置单个文件最大上传大小
      max-request-size: 100MB #配置单个请求最大上传大小(一次请求可以上传多个文件)

mybatis:
  configuration:  # setting配置
    auto-mapping-behavior: full # 开启resultMap自动映射 设置映射等级full 复杂情况也能映射 多表联查相关
    map-underscore-to-camel-case: true # true开启属性字段驼峰命名自动映射,将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
  type-aliases-package: com.wake.pojo # 配置别名 批量将包下的类,设置别名都为首字母小写
  mapper-locations: classpath:/mappers/*.xml # mapperxml位置

aliyun: #以下参数全部修改成自己的
  oss:
    endpoint: https://oss-cn-fuzhou.aliyuncs.com
    accessKeyId: LTAI5t9ZK8iq5T2Av6GLDxX  #假的修改
    accessKeySecret: C0IrHKqU8S8YQcevcotD3Zd5Tc #假的修改
    bucketName: web-wake-work
举报

相关推荐

0 条评论