Java POI (2)—— Excel文件的上传与导出(实例演示)

news2024/12/28 4:24:26

一、单文件的上传功能

         这里是一个demo的流程图,下面按照这个流程图做了一个简单的实现,有部分判断没有加上,实际操作中,可以根据自己的需求进行增加或者修改。并且此处还是在接受文件传入后将文件进行了下载,保存到本地的操作,这个要按照具体情况具体分析,看需求是否要把上传进来的文件进行数据备份,或者直接以流的形式读进来,然后进行解析等逻辑操作后,最后关闭释放流也可以,没有说一定要上传完文件就要下载下来。

controller层 (接受前端传入的文件参数,为单个文件)

@RequestMapping( "/salary/server/excelxz")
public class SalaryExcelOperatController {

    @Autowired
    private SalaryExcelOperatService salaryexcelOperatService;

    @PostMapping("/upload")
    public RespondDto uploadFile(@RequestParam("file") MultipartFile multipartFile) {
    SalaryExcelOperatVo excelOperatVo =salaryexcelOperatService.uploadExcel(multipartFile);
    return new RespondDto(excelOperatVo);
}

service层 (这里使用了easyExcel的方式实现了,一个Excel中包含多个sheet的读取操作,但本质和POI的思想是差不多的 )

@Service
@Slf4j
public class SalaryExcelOperatServiceImpl implements SalaryExcelOperatService {


    @Resource
    private SalaryExcelOperatMapper salaryexcelOperatMapper;
    @Resource
    private LoginMapper loginMapper;

    /**
     * 上传 Excel 文件
     * 1、接收文件,保存到本地;
     * 2、获取本地文件路径调用 readExcel 读成ArrayList;
     */
    @Override
    public SalaryExcelOperatVo uploadExcel(MultipartFile multipartFile) {

        if (multipartFile==null) {
            log.error("文件不能为空");
             throw new RuntimeException("上传Excel文件内容为空,请重新上传!");
        }
        String fileName = multipartFile.getOriginalFilename();
        //判断文件是否是excel文件
        assert fileName != null;
        if (!fileName.endsWith("xls") && !fileName.endsWith("xlsx")) {
            log.error(fileName + "不是Excel文件!");
            throw new RuntimeException(fileName + "不是Excel文件!");
        }

        //保存文件到本地
        File dir1 = new File("/roots/uploadFile/xzExcel");
        if (!dir1.exists()) {
            dir1.mkdirs();
        }
        //统一日期格式
        LocalDateTime current = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String formatted = current.format(formatter);
        //加上三位随机数
        Random random = new Random();
        int end3 = random.nextInt(999);

        File file1 = new File(dir1.getAbsolutePath() + File.separator + formatted + "-" + end3 + "-" + multipartFile.getOriginalFilename());
        try {
            multipartFile.transferTo(file1);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //创建返回对象SalaryExcelOperatVo的实例化对象: result
        SalaryExcelOperatVo result = new SalaryExcelOperatVo();
        //获取excel文件sheet1 的内容
        ArrayList<InSalary> inSalaries1 = readExcel1(file1.getAbsolutePath());

        ArrayList<SalaryStaffPerOneListVo> vo1 = new ArrayList<>();

        SalaryStaffPerOneListVo oneListVo ;

        for(InSalary inSalary1:inSalaries1){
            oneListVo = new SalaryStaffPerOneListVo();
            BeanUtils.copyProperties(inSalary1,oneListVo);
            vo1.add(oneListVo);
}
        result.setSheetOne(vo1);

        //获取excel文件sheet2 的内容
        ArrayList<InSalary> inSalaries2 = readExcel2(file1.getAbsolutePath());

        ArrayList<SalaryStaffPerTwoListVo> vo2 = new ArrayList<>();

        SalaryStaffPerTwoListVo twoListVo ;
        for(InSalary inSalary2:inSalaries2){
            twoListVo = new SalaryStaffPerTwoListVo();
            BeanUtils.copyProperties(inSalary2,twoListVo);
            vo2.add(twoListVo);
        }

        result.setSheetTwo(vo2);
        return result;
    }

    /**
     * 抽离出   【读取excel文件,包含多个sheet内容的方法】
     * 1、工作区间 -> Sheet -> cell;
     * 2、每行为一个 inSalary 对象, 把所有对象用 ArrayList保存;
     * 从第二行开始读取,第一行表头忽略;
     *
     * @param filePath ;本地保存后的文件地址
     * @return ArrayList<InSalary>
     */
    private ArrayList<InSalary> readExcel1(String filePath) {

        ArrayList<InSalary> inSalary = new ArrayList<>();

        // 该监听将excel文件一行一行读入内存(必须有)
        SalaryExcelListener listener = new SalaryExcelListener();
        ExcelReader excelReader = EasyExcel.read(filePath, InSalary.class,
                listener).build();
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        excelReader.finish();
        // readList 文件中的数据,不包括表头
        inSalary .addAll(listener.getList());
        return inSalary;
    }
    private ArrayList<InSalary> readExcel2(String filePath) {

        ArrayList<InSalary> inSalary = new ArrayList<>();

        // 该监听将excel文件一行一行读入内存(必须有)
        SalaryExcelListener listener = new SalaryExcelListener();
        ExcelReader excelReader = EasyExcel.read(filePath, InSalary.class,
                listener).build();
        ReadSheet readSheet = EasyExcel.readSheet(1).build();
        excelReader.read(readSheet);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        excelReader.finish();
        // readList 文件中的数据,不包括表头
        inSalary .addAll(listener.getList());
        return inSalary;
    }

二、多文件的上传功能 

controller层 (接受前端传入的文件参数,为多个文件)

    @PostMapping(value = "/uploadExcels")
    public RespondDto upLoadFiles(@NonNull @RequestParam("multipartFiles") MultipartFile[] multipartFiles,
                                  @NonNull @RequestParam("types") String[] types){
        return fileService.upLoadFiles(multipartFiles,types);
    }

        这里需要说明一点,controller层的实现方式有很多中,没有说当要传入多个文件的时候一定要去使用数组的形式进行 ,在这样写之前我也尝试了其他的两种写法:

    @PostMapping("/upload")
    public String upload(@RequestParam("files") List<MultipartFile> files, HttpServletRequest request) {
        for (MultipartFile file : files) {
            String type1 = request.getParameter("type1");
            String type2 = request.getParameter("type2");
            // 根据type1和type2的值,对上传的文件进行特定处理
            // ...
        }
        return "upload success";
    }
    @PostMapping(value = "/uploadExcels")
    public RespondDto upLoadFiles(@NonNull @RequestParam("multipartFiles") List<MultipartFile> multipartFiles,
                                  @NonNull @RequestParam("types") List<MultipartFile> types){
        return fileService.upLoadFiles(multipartFiles,types);
    }

 不过实际中具体怎么写,还是看跟前端的沟通以及最终的接口文档来进行操作。

service层 (这里实现了将多个Excel一次性上传后,后端将这些Excel文件进行接收并保存到本地,并且将每个文件的名字以文件类型的名字进行重命名)

@Override
public RespondDto upLoadFiles(MultipartFile[] files, String[] types) {

    long MAX_SIZE = 1024 * 1024 * 10;// 文件大小限制为10MB
    LocalDateTime current = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    String formattedDate = current.format(formatter);
    //加上三位随机数
    Random random = new Random();
    int randomNum = random.nextInt(999);
    File dir = new File("/manage/uploadFile/"+formattedDate+randomNum);
    if (!dir.exists()) {
            dir.mkdirs();
    }

    for (int i = 0; i < files.length; i++) {
            MultipartFile file = files[i];
            String type = types[i];
            // 判断文件或类型不能为空
            if (file.isEmpty() || StringUtils.isEmpty(type)) {
                return new RespondDto<>("文件或类型不能为空,请重新上传!");
            }
            // 判断文件大小是否合适
            if (file.getSize() > MAX_SIZE) {
                return new RespondDto<>("文件过大,上传失败!");
            }
            String originalFileName = file.getOriginalFilename();
            // 获取文件名和扩展名
            String fileExt = originalFileName.substring(originalFileName.lastIndexOf("."));
            String fileName = type ;
            File fileToSave = new File(dir.getAbsolutePath() + File.separator + fileName +fileExt);
        try {
                //文件写入 transferTo
                file.transferTo(fileToSave);
                String fileUrl =  fileToSave.getAbsolutePath();
                 fileUrls.add(fileUrl);
       } catch (IOException e) {
                e.printStackTrace();
                return new RespondDto<>("文件上传失败!");
       }
            log.info("【上传文件】"+fileName+" 已保存到本地:{}", originalFileName, fileToSave.getAbsolutePath());
     }
     return new RespondDto<>(fileUrls);
}

 在postman中进行测试,结果为:

三、文件导出功能

①、导出带有图片的Excel

@RestController
@RequestMapping( "/salary/server/excel")
@Slf4j
public class ExportjxExcelsController {

    @Value("#{'${headerTitles}'.split(',')}")
    private String[] headerTitles;

    @Autowired
    StaffMapper staffMapper;

    @GetMapping("/jxdownload")
    public void export( HttpServletResponse response ,Integer year,Integer month) throws IOException {

        //设置响应
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 设置要导出的文件名, 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName= URLEncoder.encode("XXXX数据","UTF-8").replaceAll("\\+","%20");
        response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx");


        // 从数据库ensure表中读取数据
        List<Salary> userList =  staffMapper.getAllStaff(year, month);
        log.info("数据为:\n{}", userList);

        List<Map<String, String>> salaryList = new ArrayList<>();
        for (Salary salary : userList) {

            Map<String, String> salaryMap = new LinkedHashMap<>();

            salaryMap.put("userName", salary.getUserName());
            salaryMap.put("firstDepart", salary.getFirstDepart());
            salaryMap.put("secondDepart", salary.getSecondDepart());
            salaryMap.put("post", salary.getPost());;
            salaryMap.put("careDeduct", salary.getCareDeduct());
            salaryMap.put("personalTax", salary.getPersonalTax());
            salaryMap.put("actualPay", salary.getActualPay());
            salaryMap.put("socialUnitpart", salary.getSocialUnitpart());
            salaryMap.put("amonthlySalary", salary.getAmonthlySalary());
            salaryMap.put("achieveBonus", salary.getAchieveBonus());
            salaryMap.put("status", Integer.valueOf(103).equals(salary.getStatus()) ? "已确认" : "未确认");
            salaryMap.put("evidence", salary.getEvidence());

            salaryList.add(salaryMap);
        }

        //取出map键值对中的value值
        List<String> valueList = new ArrayList<>();

        for (Map<String, String> salaryMap : salaryList) {
            Set<Map.Entry<String, String>> entrySet = salaryMap.entrySet();
            for (Map.Entry<String, String> entry : entrySet) {
                valueList.add(entry.getValue());
            }
        }

        //  保存文件到本地
        File dir = new File("/roots/uploadFile/exportxzExcel");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        //加上三位随机数
        Random random = new Random();
        int end3 = random.nextInt(999);

        String path = dir.getAbsolutePath() + File.separator + end3  +fileName + ".xlsx";
        log.info("path文件路径为:{}",path);

        SSExcel07Workbook ssExcel07Workbook = (new SSExcel07Workbook()).openOrCreate(path);
        SSExcel07Sheet sheet = ssExcel07Workbook.createSheet("sheet1");
        // 创建Excel表格头部(有43个字符串)
        //将表头数据写入到单元格中
        int col = 0;
        for (String title : headerTitles) {
            SSExcel07Cell cell = sheet.getCellOrCreate(0, col);
            cell.setCellValue(title);
            col++;
        }

        // 将数据写入数据到Excel文件中
        // 循环遍历 salaryList 中的值,写入到 Excel 文件中
        int row = 1;
        int col1 = 0;
        for (String strval : valueList) {
            // 判断当前列是否超过了最大列数,如果超过了,则重置列数,同时行数加1
            if (col1 >= headerTitles.length) {
                col1 = 0;
                row++;
            }
            if(col1==42){
                //在最后一列插入图片
                SSExcel07Cell imgCell = sheet.getCellOrCreate(row, 42);

                imgCell.insertImg(strval, 0, 0);
            }else {
                // 获取当前单元格
                SSExcel07Cell cell = sheet.getCellOrCreate(row, col1);
                // 将当前单元格的值设置为当前的 strval 值
                cell.setCellValue(strval);
            }
            // 将列数加1
            col1++;
        }
        ssExcel07Workbook.getXSSFWorkBook().write(response.getOutputStream());
        log.info("文件导出完成!");
    }
}

②、将之前上传的多个Excel按照一定的映射规则生成一个Excel文件(映射规则可以写在配置文件中,也可以写到数据库中,主要看映射关系的改动频率是多少)

@Override
    public RespondDto writeExcel(String[] fileUrls) throws IOException {

        long startTime=System.currentTimeMillis (); //获取开始时间

        String fileUrl = null;
        for (String url : fileUrls) {
            fileUrl = url;
        }

        // 读取配置文件,configFile 是指配置文件的路径和名称。
        Properties config = new Properties();
        ClassPathResource configInputStream = new ClassPathResource("config.properties");
        config.load(configInputStream.getInputStream());

        //  保存文件到本地
        File excelDir = new File("/manage/uploadFile");
        if (!excelDir.exists()) {
            excelDir.mkdirs();
        }
        LocalDateTime current = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String formattedDate = current.format(formatter);
        //加上三位随机数
        Random random = new Random();
        int randomNum = random.nextInt(999);

        // 获取文件名
        String destExcelFileName = formattedDate + "-" + randomNum + "-" ;


        FileInputStream destinationFileInputStream = new FileInputStream(excelDir + destExcelFileName);
        Workbook destinationWorkbook = new XSSFWorkbook(destinationFileInputStream);

        Map<String, Workbook> workbookMap = new HashMap<>();

        for (String key : config.stringPropertyNames()) {
            String[] destArray = org.springframework.util.StringUtils.tokenizeToStringArray(key, ".");

            String[] srcArray = org.springframework.util.StringUtils.tokenizeToStringArray(config.getProperty(key), ".");
            System.out.println(srcArray[0]);
            if (destArray == null || srcArray == null || srcArray.length != 3 || destArray.length != 2) {
                continue;
            }
            Workbook sourceWorkbook = null;
            if (workbookMap.containsKey(srcArray[0])) {
                sourceWorkbook = workbookMap.get(srcArray[0]);
            } else {
                // 读取源文件
                //FileInputStream sourceFileInputStream = new FileInputStream(excelDir + srcArray[0] + ".xlsx");
                FileInputStream sourceFileInputStream = new FileInputStream(fileUrl);
                sourceWorkbook = new XSSFWorkbook(sourceFileInputStream);
                workbookMap.put(srcArray[0], sourceWorkbook);

            }
            Sheet sourceSheet = sourceWorkbook.getSheet(srcArray[1]);
            CellReference sourceCellRef = new CellReference(srcArray[2]);
            Row sourceRow = sourceSheet.getRow(sourceCellRef.getRow());
            Cell sourceCell = null;
            if (sourceRow != null) {
                sourceCell = sourceRow.getCell(sourceCellRef.getCol());
            }

            Sheet destinationSheet = destinationWorkbook.getSheet(destArray[0]);
            CellReference destCellRef = new CellReference(destArray[1]);
            Row destRow = destinationSheet.getRow(destCellRef.getRow());
            if (destRow == null) {
                destRow = destinationSheet.createRow(destCellRef.getRow());
            }
            Cell destCell = destRow.createCell(destCellRef.getCol());
            // 执行 copy 方法
            copyCellValue(sourceCell, destCell);
        }
        // 保存目标文件
        FileOutputStream outputStream = new FileOutputStream(excelDir + destExcelFileName);
        destinationWorkbook.write(outputStream);

        // 关闭资源
        outputStream.close();
        destinationFileInputStream.close();
        destinationWorkbook.close();
        workbookMap.values().forEach(k -> {
            try {
                k.close();
            } catch (IOException e) {
            }
        });

        long endTime=System.currentTimeMillis (); //获取结束时间
        System.out.println ( "程序运行时间: " + (endTime-startTime)/1000 + "s" );
        return new RespondDto<>("Excel导出成功!");
    }


    /**
     * copyCellValue() 方法用于将源单元格的值复制到目标单元格中。
     * @param sourceCell 是源单元格对象
     * @param destCell 是目标单元格对象
     */
    public static void copyCellValue(Cell sourceCell, Cell destCell) {
        // 如果 sourceCell 和 destCell 中任意一个为 null,则不进行操作,方法直接返回。
        if (sourceCell == null || destCell == null) {
            return;
        }
        // 首先,将源单元格的单元格样式克隆到目标单元格上,然后再将源单元格的值赋给目标单元格。
        CellStyle sourceStyle = sourceCell.getCellStyle();
        CellStyle destinationStyle = destCell.getSheet().getWorkbook().createCellStyle();
        destinationStyle.cloneStyleFrom(sourceStyle);
        destCell.setCellStyle(destinationStyle);
        // 如果源单元格的数据类型为字符串类型,则复制字符串值;如果为布尔类型,则复制布尔值;如果为公式类型,则复制公式字符串;如果为数字类型,则复制数字值。
        if (sourceCell.getCellTypeEnum().equals(CellType.STRING)) {

            destCell.setCellValue(sourceCell.getStringCellValue());

        } else if (sourceCell.getCellTypeEnum().equals(CellType.BOOLEAN)) {

            destCell.setCellValue(sourceCell.getBooleanCellValue());

        } else if (sourceCell.getCellTypeEnum().equals(CellType.FORMULA)) {

            if (DateUtil.isCellDateFormatted(sourceCell)) {
                destCell.setCellValue(sourceCell.getDateCellValue());
            } else {
                destCell.setCellValue(sourceCell.getNumericCellValue());
            }

        } else if (sourceCell.getCellTypeEnum().equals(CellType.NUMERIC)) {
            if (DateUtil.isCellDateFormatted(sourceCell)) {
                destCell.setCellValue(sourceCell.getDateCellValue());
            } else {
                destCell.setCellValue(sourceCell.getNumericCellValue());
            }
            // 如果源单元格是空的,则在目标单元格中写入一个空串("")。
        } else if (sourceCell.getCellTypeEnum().equals(CellType.BLANK)) {
            destCell.setCellValue("");
        }
    }

四、注意事项

①、在使用poi的时候一定要注意,选对依赖的版本

不能想用什么依赖的版本就使用什么依赖版本,有些方法,不同的版本之间相差还是比较大的,此处我使用的版本就是3.17的,如果你使用3.9版本的依赖,就不能运行成功。

        <!--xlsx(07)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>

②、当在上传多个文件的时候,出现 the request was rejected because its size (10821303) exceeds the configured maximum (10485760)的报错

        这是因为springboot默认配置 multipart.max-file-size大小是1M,max-request-size默认大小是10M  故可以在yml文件中添加:

spring:
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 500MB

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/698560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

哪个爬虫库用的最多?

在Python中&#xff0c;最常用的爬虫库是requests和BeautifulSoup。requests库用于发送HTTP请求和处理响应&#xff0c;而BeautifulSoup库用于解析HTML文档。这两个库通常结合使用&#xff0c;用于爬取网页内容并提取所需的数据。其他常用的爬虫库还包括Scrapy、Selenium等。 常…

我的创作纪念日(256)

目录 机缘 收获 日常 成就 憧憬 机缘 要说为什么我会选择IT这条路&#xff0c;我觉得是因为从小对电脑的兴趣&#xff0c;从小学2年级开始的电脑生涯&#xff0c;但是那时候也只是玩电脑游戏&#xff0c;而由于我接触电脑较早的缘故&#xff0c;开始有许多的朋友遇到问题…

vue+heatmapjs-vue实现热力图,点击/滑动可以监听并更新

实现效果如下&#xff1a; 点击的次数或者滑动越多&#xff0c;区域的颜色越深 1.下载最新版热力图插件 npm install heatmapjs-vue 2.main.js中全局引用 注意&#xff01;&#xff01;&#xff01;只能全局引用&#xff0c;不能局部引用&#xff0c;局部引用就报错显示不出…

「2024」预备研究生mem-形式逻辑强化:入选名额

一、入选名额 从后往前推 二、课后题

第3章-数组

1. 数组的概述 数组(Array)&#xff0c; 是多个相同类型数据按一定顺序排列 的集合&#xff0c; 并使用一个名字命名&#xff0c; 并通过编号的方式 对这些数据进行统一管理数组的常见概念 数组名下标(或索引)元素数组的长度 数组本身是引用数据类型&#xff0c; 而数组中的元…

spring boot admin服务监控报错

使用spring boot admin监控服务启动出现报错&#xff1a;adminHandlerMapping对象创建失败 org.springframework.beans.factory.BeanCreationException: Error creating bean with name adminHandlerMappingdefined in class path resource [de/codecentric/boot/admin/server…

C++Qt 入门

目录 Qt 是什么 Qt 与 Qt Creator 的关系 暗黑主题音乐播放器界面 UI 设计。 简约的视频界面设计。 炫酷车载音乐 APP 主界面开发。 Qt/C与 QML 如何选择 Qt 版本 Linux 下安装 Qt 配置中文输入法 Qt Creator 简单使用 第一个 Qt 程序 新建一个项目 ​编辑 项目文…

Java JDBC

目录 JDBC定义 为什么需要JDBC JDBC的工作原理 JDBC API JDBC编程模板 JDBC编程步骤 JDBC实战 1.JDBC定义 Java连接数据库的一种能力或是技术 2.为什么需要JDBC 总结&#xff1a;利用JDBC才可以使服务器端和数据库进行数据的传递和交互 场景1&#xff1a; 客户端&am…

chatgpt赋能python:Python选择排序算法图解

Python选择排序算法图解 选择排序是计算机科学中经典的排序算法之一&#xff0c;它的原理是从待排序的数据中选择最小的元素然后排在最前面&#xff0c;接着从剩下未排序的数据中继续这个过程&#xff0c;直到所有的数据都排好序。 在本篇文章中&#xff0c;我将深入探讨Pyth…

微服务架构之网关详解

前言 由于互联网的高速发展&#xff0c;网络数据请求数激增&#xff0c;使得服务器承受的压力越来越大。在早期的系统架构中&#xff0c;为减轻单台服务器的压力&#xff0c;通常使用 Load Balancer 来将网络流量平摊到多个服务器中。如今后端服务的种类和数量在不断变多&…

Django DRF - 【Token】认证基本使用

一. 前言 Django Rest Framework Token是Django Rest Framework中的一个扩展&#xff0c;用于实现用户认证和授权。它为每个用户生成一个唯一的Token&#xff0c;并将其存储在数据库中。在用户进行API请求时&#xff0c;用户需要在请求的HTTP Header中包含Token&#xff0c;这…

【广州华锐互动】机械设备事故VR模拟体验系统

随着虚拟现实技术的不断发展&#xff0c;越来越多的行业开始尝试将VR技术应用到实际场景中&#xff0c;以提供更加真实的体验。其中&#xff0c;机械伤害事故VR警示教育系统的出现&#xff0c;为机械工程师、安全培训人员等行业提供了一种全新的培训方式。在实现上&#xff0c;…

odoo16 中ondelete的用法

odoo 中的many2one类型字段有 ondelete设置&#xff0c;对应数据表中关系处理 a fields.Many2one(‘b’, string‘b’, ondelete‘set null’) 则当对模型b进行delete操作时&#xff0c;会引发如下操作 set null: 当b中删除记录时&#xff0c;modelA中相关记录的anull casc…

Redis实战案例8-缓存击穿及其解决方案和案例说明

1. 缓存击穿 缓存击穿是指一个被频繁访问&#xff08;高并发访问并且缓存重建业务较复杂&#xff09;的缓存键因为过期失效&#xff0c;同时又有大量并发请求访问此键&#xff0c;导致请求直接落到数据库或后端服务上&#xff0c;增加了系统的负载并可能导致系统崩溃 常见的解决…

开源虚拟化工具VirtualBox安装部署

什么是Virtualbox VirtualBox是一款由Oracle开发和维护的免费开源虚拟化软件&#xff0c;用于在一台计算机上创建和管理多个虚拟机。它允许用户在单个物理计算机上运行多个操作系统&#xff0c;例如Windows、Linux、macOS等。VirtualBox提供了一个虚拟化环境&#xff0c;使用户…

详细介绍如何使用 OpenCV 实现自动文档扫描仪--附实现源码

文末附相关源代码实现的免费下载链接 文档扫描是将物理文档转换为数字形式的过程。可以通过扫描仪或手机摄像头拍摄图像来完成。我们将在本教程中讨论如何使用计算机视觉和图像处理技术有效地实现这一目标。 在当今的计算机时代,几乎不需要任何物理文书工作。尽管如此,在仍…

现代操作系统第一章学习笔记

先附上一个原版的课后习题答案的链接。 课后习题答案 1.1 什么是操作系统 操作系统&#xff1a;他的任务是为应用程序提供一个更好、更简单、更清晰的计算机模型&#xff0c;并管理贮存、磁盘等所有设备。 计算机系统总的来说分为软件和硬件&#xff0c;如下图所示。多数计算…

华为孟晚舟:拥抱5G变革

2023 MWC上海开幕&#xff0c;华为副董事长、轮值董事长、CFO孟晚舟在大会上发表了“拥抱5G变革”的主题演讲。她表示&#xff1a;全球5G商用4年来&#xff0c;正持续引领价值创造&#xff0c;而5.5G是5G网络演进的必然之路&#xff1b;面向未来&#xff0c;科技走向复杂大系统…

突破未来:SAP助力新能源产业腾飞!

新能源行业概况 在互联网的时代浪潮下&#xff0c;多数行业都开始进行了调整与整合&#xff0c;竞争无处不在。作为新兴的新能源行业&#xff0c;如果不想从竞争中被淘汰&#xff0c;就需要把握时代的脉搏&#xff0c;找到突破口&#xff0c;带领企业在市场竞争中越走越远。我…

学习:PSM倾向得分匹配基于stata实现

PSM倾向性匹配得分 定义 就是一个用户属于实验组的倾向性&#xff0c;也就是在特定特征的情况下属于实验组的概率(条件概率) 其他定义&#xff1a; PSM 通过统计学模型计算每个观测的每个协变量的综合倾向性得分&#xff0c;再按照倾向性得分是否接近进⾏匹配。 用直白的话来…