• 对象存储服务之MinIO


    前言

    之前一直把用户上传的图片和文件保存在本地服务器的文件系统中,长而久之会产生以下弊端:

    • 当文件数量过多之后严重消耗Linux文件系统的inode;
    • 当数据量过大之后不易分布式扩展;
    • 数据备份困难,不方便前端展;
    • 文件的目录层级越来越深导致文件查找的速度逐渐变慢;

    于是想搭建1个私有的阿里云-OSS服务,即对象存储服务;

    由专门的对象存储服务来统一管理文件,文件上传之后OSS返回1个唯一的URL地址,后端返回前端,用户可以直接访问;

    二、存储分类

    根据不同的分类方法,存储也会被分成不同的类型,但最终到的用途是都是一致的存放数据;

    1.本地存储、外置存储

    根据存储设备所在的位置不同划分为

    本地存储:电脑内置的存储设备例如硬盘、内存;

    外置存储:U盘、移动硬盘

    2.DAS、SAN、NAS

    根据连接存储设备的介质不同划分为

    DAS(Direct Attached Stroage):直连使存储,使用连接介质直接连接到主机、服务器等设备上,连接无需借助网络;

    SAN(Stroage Area Network):  存储区域网络,使用专有的网络(非以太网)连接存储设备;

    NAS(Network Attached Stroage):网络附加存储,任何1个终端(主机、服务器)都可以通过网络连接到存储设备上;

    3.块存储、文件存储、对象存储

    根据存储设备文件系统所在位置的不同划分为

    块存储:    一切以磁盘形式存在的存储设备都可以称为块存储,块存储使用终端的文件系统,存储服务不利于共享;

    文件存储:  块存储拥有了文件系统之后称为文件存储,文件存储使用服务端的文件系统,存储服务利于共享,文件系统使用传统的目录结构管理文件;

    对象存储:对象存储的文件系统使用二层结构(Bucket+对象ID)来管理文件,适合存储非结构化数据,速度快、存储服务利于共享、可扩展性强

    三、Minio概述

    以上我们得知对象存储是1种全新的存储架构,综合了块存储、和文件存储的优点;

    对象存储颠覆了传统的文件系统以目录结构管理文件的方式,对象存储的文件系统使用2层结构(Bucket+对象ID)来管理1个唯一的文件对象,适合存储非结构化数据;

    对象存储服务读写速度快、易于共享、可扩展性强;

    Minio是一款可以实现分布式对象存储服务的软件,我一般会使用minio存储图片、合同、短视频等非结构化数据;

    四、搭建Minio服务

     Minion是使用Golang开发下载完了二进制可执行文件,直接在当操作系统运行服务;

    [root@localhost /]# minio server --console-address ":8000" ./data/
    WARNING: Detected Linux kernel version older than 4.0.0 release, there are some known potential performance problems with this kernel version. MinIO recommends a minimum of 4.x.x linux kernel version for best performance
    Automatically configured API requests per node based on available memory on the system: 151
    Finished loading IAM sub-system (took 0.0s of 0.0s to load data).
    Status:         1 Online, 0 Offline.
    API: http://192.168.56.18:9000  http://192.168.122.1:9000  http://127.0.0.1:9000
    RootUser: minioadmin
    RootPass: minioadmin
    
    Console: http://192.168.56.18:8000 http://192.168.122.1:8000 http://127.0.0.1:8000
    RootUser: minioadmin
    RootPass: minioadmin
    
    Command-line: https://docs.min.io/docs/minio-client-quickstart-guide
       $ mc alias set myminio http://192.168.56.18:9000 minioadmin minioadmin
    
    Documentation: https://docs.min.io

    1.设置权限

    访问Minio的Console接口修改对象服务的读写权限;

    2.SpringBoot调用minionAPI

    1.pom依赖

        <!--minio -->
                <dependency>
                    <groupId>io.minio</groupId>
                    <artifactId>minio</artifactId>
                    <version>${miniio.version}</version>
                </dependency>

    2.配置文件

    # Miniio配置
    minio:
      endpoint: 192.168.56.18
      port: 9000
      accessKey: minioadmin
      secretKey: minioadmin
      secure: false
      bucketName: "huike-crm"
      configDir: "/data/excel"
    application.yml

    3.配置类

    package com.huike.common.config;
    
    import io.minio.MinioClient;
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    import org.springframework.beans.factory.annotation.Value;
    /**
     * @className: MinioConfig
     * @author Hope
     * @date 2022/7/28 13:43
     * @description: MinioConfig
     */
    
    @Data
    @Component
    @ConfigurationProperties(prefix = "minio")
    public class MinioConfig {
    
        private final static String HTTP = "http://";
    
        //endPoint是一个URL,域名,IPv4或者IPv6地址
        private String endpoint;
    
        //TCP/IP端口号
        private int port;
    
        //accessKey类似于用户ID,用于唯一标识你的账户
        private String accessKey;
    
        //secretKey是你账户的密码
        private String secretKey;
    
        //如果是true,则用的是https而不是http,默认值是true
        private Boolean secure;
    
        //默认存储桶
        private String bucketName;
    
        @Bean
        public MinioClient minioClient() {
            return MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .build();
        }
    }
    MinioConfig.java

    3.上传和下载文件

     @Autowired
        MinioConfig minioConfig;
        @Override
        public AjaxResult upload(MultipartFile file) {
            InputStream inputStream = null;
            //创建Minio的连接对象
            MinioClient minioClient = getClient();
            String bucketName = minioConfig.getBucketName();
            try {
                inputStream = file.getInputStream();
                //基于官网的内容,判断文件存储的桶是否存在 如果桶不存在就创建桶
                boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
                if (!found) {
                    // 创建新桶
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                } else {
                    System.out.println("桶" + bucketName + "已经存在");
                }
                String filename = file.getOriginalFilename();
                String objectName = "/data/excel/" + new SimpleDateFormat("yyyy/MM/dd").format(new Date())
                        + "/" + System.currentTimeMillis() + filename.substring(filename.lastIndexOf("."));
                //上传文件到minio
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .stream(inputStream, -1, 10485760)
                                .contentType("application/pdf")
                                .build());
                System.out.println("上传成功");
                Map<String, Object> msgMap = new HashMap<>();
                msgMap.put("msg", "操作成功");
                msgMap.put("fileName", objectName);
                msgMap.put("url", "http://" + minioConfig.getEndpoint() + ":" + minioConfig.getPort() + objectName);
                msgMap.put("code", 200);
                AjaxResult ajax = AjaxResult.success(msgMap);
    
                /**
                 * 封装需要的数据进行返回
                 */
                return ajax;
            } catch (Exception e) {
                e.printStackTrace();
                return AjaxResult.error("上传失败");
            } finally {
                //防止内存泄漏
                if (inputStream != null) {
                    try {
                        inputStream.close(); // 关闭流
                    } catch (IOException e) {
                        log.debug("inputStream close IOException:" + e.getMessage());
                    }
                }
            }
        }
    SysFileServiceImpl.java

    五、EasyExcel

    Java解析Excel 

    1.Java使用EasyExcel

    1.1.pom依赖

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.zhanggen</groupId>
        <artifactId>easy-excel-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <dependencies>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>3.1.0</version>
                <!--<exclusions>-->
                    <!--<exclusion>-->
                        <!--<artifactId>poi-ooxml-schemas</artifactId>-->
                        <!--<groupId>org.apache.poi</groupId>-->
                    <!--</exclusion>-->
                <!--</exclusions>-->
            </dependency>
            <!-- lombok 管理 -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.22</version>
            </dependency>
    
            <!--单元测试-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
    
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    pom.xml

    1.2.domain

    package com.zhanggen.domain;
    
    import com.alibaba.excel.annotation.ExcelProperty;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    import java.util.Date;
    
    @Data
    @EqualsAndHashCode
    public class ExcelData {
        //@ExcelProperty("字符串标题") 声明当前字段对应的列
        @ExcelProperty("字符串标题")
        private String title;
        @ExcelProperty("日期标题")
        private Date date;
        @ExcelProperty("数字标题")
        private Double number;
    }
    ExcelData.java

    ---------------------------

    package com.zhanggen.domain;
    
    import com.alibaba.excel.annotation.ExcelIgnore;
    import com.alibaba.excel.annotation.ExcelProperty;
    import lombok.Data;
    
    import java.util.Date;
    
    @Data
    public class User {
        @ExcelProperty("姓名")
        private String name;
    
        @ExcelProperty("年龄")
        private Integer age;
    
        @ExcelProperty("创建时间")
        private Date createTime;
    
        @ExcelIgnore//忽略这个字段
        private String ignore;
    }
    User.java

    1.3.listener监听器

    package com.zhanggen.listener;
    
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.read.listener.ReadListener;
    import com.alibaba.excel.util.ListUtils;
    import com.zhanggen.domain.ExcelData;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.List;
    
    //准备1个监听器
    @Slf4j
    // 有个很重要的点ExcelDataReadListener不能被spring管理,就不能从Spring容器中获取dao对象
    // 如果想用Spring中的dao对象,需要在当前Listener提供一个构造函数,然后将dao对象接进来
    public class ExcelDataReadListener implements ReadListener<ExcelData> {
        //控制临时缓存集合的大小(收集到100条之后,往数据库中保存)
        private static final int BATCH_COUNT = 100;
    
        //创建缓存集合
        private List<ExcelData> cacheDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    
    //    假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
    //    private DemoDAO demoDAO;
    //    public ExcelDataReadListener(ExcelData demoDAO) {
    //        this.demoDAO = demoDAO;
    //    }
    
    
        //1.每解析1条Excel记录都会调用的回调函数,把记录保存中临时集合缓存中,一旦临时集合缓存容量达到了上限,就将缓存的数据保存到数据库,然后情况缓存继续收集Excel中的记录
        @Override
        public void invoke(ExcelData row, AnalysisContext analysisContext) {
            System.out.println(row);
            cacheDataList.add(row);
            //如果达到了临时集合缓存容量的上限,就将缓存的数据保存到数据库
            if (cacheDataList.size() >= BATCH_COUNT) {
                System.out.println(row);
                saveData();
                //存储数据库完成之后,清理集合
                ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
            }
        }
    
    
        //2.所有数据解析完成了之后调用一次,进行一次存库操作;收尾工作!
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            // 这里也要保存数据,确保最后遗留的数据也存储到数据库
            saveData();
            System.out.println("所有数据解析完成!");
        }
    
        //模拟向数据库中保存数据
        private void saveData() {
            System.out.println(cacheDataList.size() + "条数据,开始存储到数据库!");
            //demoDao.save(cacheDataList)
            System.out.println("存储数据库完成");
        }
    
    
    }
    ExcelDataReadListener.java

    ------------------------

    2.4.单元测试

    package com.zhanggen.test;
    
    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.ExcelReader;
    import com.alibaba.excel.read.listener.PageReadListener;
    import com.zhanggen.domain.ExcelData;
    import com.zhanggen.listener.ExcelDataReadListener;
    import org.junit.Test;
    import com.alibaba.excel.read.metadata.ReadSheet;
    
    public class ReadExcelTest {
    
        //方式一:
        @Test
        public void test1() {
            String fileName = "D:/upload/excel读取测试.xlsx";
            EasyExcel.read(fileName, ExcelData.class, new PageReadListener<ExcelData>(dataList -> {
                for (ExcelData row : dataList) {
                    System.out.println(row);
                }
            })).sheet().doRead();
        }
    
    
        //方式二:
        @Test
        public void test2() {
            String fileName = "D:/upload/excel读取测试.xlsx";
            // 一个文件一个reader
            try (ExcelReader excelReader = EasyExcel.read(fileName, ExcelData.class, new ExcelDataReadListener()).build()) {
                // 构建一个sheet 这里可以指定名字或者no
                ReadSheet readSheet = EasyExcel.readSheet(0).build();
                // 读取一个sheet
                excelReader.read(readSheet);
            }
        }
    }
    ReadExcelTest.java

    ------------------------

    package com.zhanggen.test;
    
    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.ExcelWriter;
    import com.alibaba.excel.util.ListUtils;
    import com.alibaba.excel.write.metadata.WriteSheet;
    import com.zhanggen.domain.ExcelData;
    import com.zhanggen.domain.User;
    import org.junit.Test;
    
    import java.util.Date;
    import java.util.List;
    
    public class WriteExcelTest {
    
        private static List<User> list = ListUtils.newArrayList();
    
        //准备输出的数据
        static {
            for (int i = 0; i < 10; i++) {
                User data = new User();
                data.setName("字符串" + i);
                data.setAge(10 + i);
                data.setCreateTime(new Date());
                list.add(data);
            }
        }
    
        @Test
        public void test1() {
            String fileName = "D:/upload/excel输出测试.xlsx";
            EasyExcel.write(fileName, User.class).sheet("模板").doWrite(list);
        }
    
        @Test
        public void simpleWrite2() {
            String fileName = "D:/upload/excel输出测试.xlsx";
            // 这里 需要指定写用哪个class去写
            ExcelWriter excelWriter = null;
            try {
                excelWriter = EasyExcel.write(fileName, ExcelData.class).build();
                //向1个Excel的Sheet中填充数据
                WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
                excelWriter.write(list, writeSheet);
            } finally {
                // 千万别忘记finish 会帮忙关闭流
                if (excelWriter != null) {
                    excelWriter.finish();
                }
            }
        }
    }
    WriteExcelTest.java

    2.springBoot使用EasyExcel

    1.pom依赖

     <!-- easy Excel -->
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>easyexcel</artifactId>
                    <version>${esay_excel.version}</version>
                </dependency>
    pom.xml

    2.监听器

    package com.huike.clues.utils.easyExcel;
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.huike.clues.domain.dto.ImportResultDTO;
    import com.huike.clues.domain.vo.TbClueExcelVo;
    import com.huike.clues.service.ITbClueService;
    
    /**
     * EasyExcel监听器,用于解析数据并执行操作
     */
    public class ExcelListener extends AnalysisEventListener<TbClueExcelVo> {
    
        /**
         * 利用构造方法获取对应的service
         */
        public ITbClueService clueService;
    
        private ImportResultDTO resultDTO;
    
        /**
         * 提供带参构造方法,在这里需要通过构造方法的方式获取对应的service层
         * 谁调用这个监听器谁提供需要的service
         * @param clueService
         */
        public ExcelListener(ITbClueService clueService) {
            this.clueService = clueService;
            this.resultDTO = new ImportResultDTO();
        }
    
        /**
         * 每解析一行数据都要执行一次
         * 每条都执行一次插入操作
         * @param row
         * @param context
         */
        @Override
        public void invoke(TbClueExcelVo row, AnalysisContext context) {
            ImportResultDTO addTbClue = clueService.importCluesData(row);
            resultDTO.addAll(addTbClue);
        }
    
        /**
         * 当所有数据都解析完成后会执行
         * @param context
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
        }
    
        /**
         * 返回结果集对象
         * @return
         */
        public ImportResultDTO getResult(){
            return resultDTO;
        }
    
    }
    ExcelListener.java

    3.Controller

    有个很重要的点ExcelDataReadListener不能被Spring容器管理,就不能从Spring容器中获取dao对象;

    如果想用Spring中的dao对象,需要在当前Listener提供一个构造函数,然后将dao对象接进来;

    import com.alibaba.excel.EasyExcel;
    import com.huike.clues.utils.easyExcel.ExcelListener;

    @Log(title = "上传线索", businessType = BusinessType.IMPORT) @PostMapping("/importData") public AjaxResult importData(MultipartFile file) throws Exception { ExcelListener excelListener = new ExcelListener(tbClueService); EasyExcel.read(file.getInputStream(), TbClueExcelVo.class, excelListener).sheet().doRead(); return AjaxResult.success(excelListener.getResult()); }

    4.Service

     @Override
        public ImportResultDTO importCluesData(TbClueExcelVo row) {
            //创建数据库记录对象
            TbClue clue_entry = new TbClue();
            //把前端的数据 封装到数据库记录对象属性
            BeanUtils.copyProperties(row, clue_entry);
            //设置当前线索的创建人
            clue_entry.setCreateBy(SecurityUtils.getUsername());
            //设置当前线索的创建时间
            clue_entry.setCreateTime(DateUtils.getNowDate());
            //获取当前活动编号
            String activityCode = row.getActivityCode();
            //判断活动编号对应的活动是否存在?  isNoneBlank如果不为空
            if (StringUtils.isNoneBlank(activityCode)) {
                TbActivity activity = activityService.selectTbActivityByCode(activityCode);
                if (activity == null) {
                    return ImportResultDTO.error();
                } else {
                    clue_entry.setActivityId(activity.getId());
                }
            }
            //校验当前线索的手机号是否为空?如果为空证明是错误数据,不进行添加 返回error
            if (StringUtils.isBlank(clue_entry.getPhone())) {
                return ImportResultDTO.error();
            }
            //判断当前线索的渠道来源是否正确?
            if (StringUtils.isNoneBlank(clue_entry.getChannel())) {
                String channel = sysDictDataMapper.selectDictValue(TbClue.ImportDictType.CHANNEL.getDictType(), clue_entry.getChannel());
                clue_entry.setChannel(channel);
            }
    
            //判断当前线索的意向学科是否正确?
            if (StringUtils.isNoneBlank(clue_entry.getSubject())) {
                String subject = sysDictDataMapper.selectDictValue(TbClue.ImportDictType.SUBJECT.getDictType(), clue_entry.getSubject());
                clue_entry.setSubject(subject);
            }
            //判断当前线索的意向级别是否正确?
            if (StringUtils.isNoneBlank(clue_entry.getLevel())) {
                String level = sysDictDataMapper.selectDictValue(TbClue.ImportDictType.LEVEL.getDictType(), clue_entry.getLevel());
                clue_entry.setLevel(level);//意向级别
            }
            //判断当前线索的联系人的性别是否正确?
            if (StringUtils.isNoneBlank(clue_entry.getSex())) {
                String sex = sysDictDataMapper.selectDictValue(TbClue.ImportDictType.SEX.getDictType(),
                        clue_entry.getSex());//性别
                clue_entry.setSex(sex);
            }
            //数据校验最后1步,设置当前线索的状态为待跟进
            clue_entry.setStatus(TbClue.StatusType.UNFOLLOWED.getValue());
            //将当前线索写入库
            tbClueMapper.insertTbClue(clue_entry);
            //根据规则动态分配线索给具体的销售人员,利用策略模式来进行实现
            rule.loadRule(clue_entry);
            return ImportResultDTO.success();
        }
    TbClueServiceImpl.java

    六、Spring项目常见的实体类对象

    Java网站后台通常划分为3层即Controller、Service、Dao层,当用户请求到达服务端是,后台就开始进行层层调用,完成客户请求的响应;

    为了更好的区别以上不同层使用到的实例对象,我们可以对实体类对象进行以下规则的命名,方便我们区分该对象当前的作用;

    1.POJO

    全称为:Plain Ordinary Java Object,即简单普通的java对象。

    一般用在数据层映射到数据库表的类,类的属性与表字段一一对应。

    2.PO

    全称为:Persistant Object,即持久化对象。

    可以理解为数据库中的一条数据即一个BO对象,也可以理解为POJO经过持久化后的对象。

    3.DTO

    全称为:Data Transfer Object,即数据传输对象。

    一般用于向数据层外围提供仅需的数据,如查询一个表有50个字段,界面或服务只需要用到其中的某些字段,DTO就包装出去的对象。可用于隐藏数据层字段定义,也可以提高系统性能,减少不必要字段的传输损耗。

    5.BO

    全称为:Business Object,即业务对象。

    一般用在业务层,当业务比较复杂,用到比较多的业务对象时,可用BO类组合封装所有的对象一并传递。

    6.VO

    全称为:Value Object,有的也称为View Object,即值对象或页面对象。一般用于web层向view层封装并提供需要展现的数据。

    7.代码示例

    以下是1个代码示例,将诠释xxVO对象和XXBO对象的使用场景;

    其中商机表和商机表和商机跟进记录表为1对多的关系;

    新增商机1条跟进记录,需要更新对应商机记录的的跟进状态;

    1.Web层

    businessTrackVo 实例对象仅在Web层使用

    /**
         * 新增商机跟进记录
         */
        @PreAuthorize("@ss.hasPermi('business:record:add')")
        @Log(title = "商机跟进记录", businessType = BusinessType.INSERT)
        @PostMapping
        public AjaxResult add(@RequestBody BusinessTrackVo businessTrackVo){
            //businessTrackVo 实例对象仅在Controller表示层使用
            
            //1. 创建商机跟进记录对象,并赋值
            TbBusinessTrackRecord tbBusinessTrackRecord = new TbBusinessTrackRecord();
            //属性拷贝
            BeanUtils.copyProperties(businessTrackVo,tbBusinessTrackRecord);
            //设置商机跟进记录的创建时间
            tbBusinessTrackRecord.setCreateTime(DateUtils.getNowDate());
            //设置商机跟进记录的创建人
            tbBusinessTrackRecord.setCreateBy(SecurityUtils.getUsername());
    
            //2.创建商机对象并赋值
            TbBusiness tbBusiness = new TbBusiness();
            //属性拷贝
            BeanUtils.copyProperties(businessTrackVo,tbBusiness);
            //设置商机的状态
            tbBusiness.setStatus(TbBusiness.StatusType.FOLLOWING.getValue());
            //设置商机的id
            tbBusiness.setId(businessTrackVo.getBusinessId());
    
            //3.调用Service层,传入商机根据记录和商机2个对象便于,更新商机状态、新增商机跟进记录的事务操作;
            tbBusinessTrackRecordService.add(tbBusinessTrackRecord,tbBusiness);
            return AjaxResult.success();
        }

    2.Service业务层

    从业务层开始,对更新商机记录和新增商机跟进记录这2个业务进行分流;

    便于2个子业务(更新商机状态和新增商机跟进记录)成为1组整体的事务操作;

    2.1.业务层接口

        //新增商机根据记录
        void add(TbBusinessTrackRecord businessTrackRecord, TbBusiness tbBusiness);

    2.2.业务层实现类

        //新增商机根据记录
        @Override
        @Transactional
        public void add(TbBusinessTrackRecord tbBusinessTrackRecord, TbBusiness tbBusiness) {
            //业务层更新商机状态
            tbBusinessMapper.updateTbBusiness(tbBusiness);
            //业务层新增商机跟进记录
            tbBusinessTrackRecordMapper.addRecord(tbBusinessTrackRecord);
    
        }

    2.3.Mapper持久层

    更新商机的Mapper层

       public int updateTbBusiness(TbBusiness tbBusiness);

    更新商机的MyBatis配置

     <update id="updateTbBusiness" parameterType="TbBusiness">
            update tb_business
            <trim prefix="SET" suffixOverrides=",">
                <if test="name != null">name = #{name},</if>
                <if test="phone != null">phone = #{phone},</if>
                <if test="channel != null">channel = #{channel},</if>
                <if test="activityId != null">activity_id = #{activityId},</if>
                <if test="provinces != null">provinces = #{provinces},</if>
                <if test="city != null">city = #{city},</if>
                <if test="sex != null and sex != ''">sex = #{sex},</if>
                <if test="age != null">age = #{age},</if>
                <if test="weixin != null">weixin = #{weixin},</if>
                <if test="qq != null">qq = #{qq},</if>
                <if test="level != null">level = #{level},</if>
                <if test="subject != null">subject = #{subject},</if>
                <if test="courseId != null">course_id = #{courseId},</if>
                <if test="createBy != null">create_by = #{createBy},</if>
                <if test="createTime != null">create_time = #{createTime},</if>
                <if test="occupation != null">occupation = #{occupation},</if>
                <if test="education != null">education = #{education},</if>
                <if test="job != null">job = #{job},</if>
                <if test="salary != null">salary = #{salary},</if>
                <if test="major != null">major = #{major},</if>
                <if test="expectedSalary != null">expected_salary = #{expectedSalary},</if>
                <if test="reasons != null">reasons = #{reasons},</if>
                <if test="plan != null">plan = #{plan},</if>
                <if test="planTime != null">plan_time = #{planTime},</if>
                <if test="otherIntention != null">other_intention = #{otherIntention},</if>
                <if test="nextTime != null">next_time = #{nextTime},</if>
                <if test="status != null">status = #{status},</if>
            </trim>
            where id = #{id}
        </update>

    新增商机记录的Mapper层

     //新增商机根据记录
        void addRecord(TbBusinessTrackRecord tbBusinessTrackRecord);

    新增商机记录的MyBatis配置

     <!--新增商机根据记录-->
        <insert id="addRecord" useGeneratedKeys="true" keyProperty="id">
            insert into tb_business_track_record
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="businessId != null and businessId != ''">business_id,</if>
                <if test="createBy != null and createBy != ''">create_by,</if>
                <if test="keyItems != null">key_items,</if>
                <if test="record != null">record,</if>
                <if test="createTime != null">create_time,</if>
                <if test="trackStatus != null">track_status,</if>
                <if test="nextTime != null">next_time,</if>
                <if test="type != null">type,</if>
                <if test="falseReason != null">false_reason,</if>
            </trim>
            <trim prefix="values (" suffix=")" suffixOverrides=",">
                <if test="businessId != null and businessId != ''">#{businessId},</if>
                <if test="createBy != null and createBy != ''">#{createBy},</if>
                <if test="keyItems != null">#{keyItems},</if>
                <if test="record != null">#{record},</if>
                <if test="createTime != null">#{createTime},</if>
                <if test="trackStatus != null">#{trackStatus},</if>
                <if test="nextTime != null">#{nextTime},</if>
                <if test="type != null">#{type},</if>
                <if test="falseReason != null">#{falseReason},</if>
            </trim>
        </insert>

      

    参考

  • 相关阅读:
    第五章 Mybatis注解
    第四章 Mbatis高级查询
    第三章 Mybatis动态Sql
    第二章 Mybatis映射文件
    第一章 初识Mybatis
    Mybatis大纲设计
    项目总结
    第二周项目功能实现
    第一周项目功能实现
    客车网上售票系统需求分析
  • 原文地址:https://www.cnblogs.com/sss4/p/16399287.html
Copyright © 2020-2023  润新知