医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)

目录

  • 一、数据字典介绍:
  • 二、数据字典开发:
    • 1、搭建service-cmn模块:
    • 2、数据字典列表:
      • 2.1 数据字典列表接口:
      • 2.2 数据字典列表前端:
    • 3、EasyExcel介绍
      • 3.3 EasyExcel集成
      • 3.4、数据字典导出:
  • 三、Spring Cache + Redis 缓存数据
    • 1、项目集成Spring Cache + Redis:
    • 常用缓存标签
    • 2.数据字典应用缓存
  • 四、nginx配置不同的访问端口:

一、数据字典介绍:

何为数据字典?数据字典就是管理系统常用的分类数据或者一些固定数据,例如:省市区三级联动数据、民族数据、行业数据、学历数据等由于该系统大量使用这种数据,所以我们要做一个数据管理方便管理系统数据,一般系统基本都会做数据管理

1、页面效果:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第1张图片

2、表设计:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第2张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第3张图片

3、数据分析:
数据库中的表:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第4张图片

表中字段说明:

  • parent_id:
    上级id,通过id与parent_id构建上下级关系,例如:我们要获取所有行业数据,那么只需要查询parent_id=20000的数据
  • name:名称,例如:填写用户信息,我们要select标签选择民族,“汉族”就是数据字典的名称
  • value:值,例如:填写用户信息,我们要select标签选择民族,“1”(汉族的标识)就是数据字典的值
  • dict_code:编码,编码是我们自定义的,全局唯一,例如:我们要获取行业数据,我们可以通过parent_id获取,但是parent_id是不确定的,所以我们可以根据编码来获取行业数据

数据字典的表:


CREATE TABLE `dict` (
  `id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'id',
  `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '上级id',
  `name` varchar(100) NOT NULL DEFAULT '' COMMENT '名称',
  `value` bigint(20) DEFAULT NULL COMMENT '值',
  `dict_code` varchar(20) DEFAULT NULL COMMENT '编码',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint(3) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)',
  PRIMARY KEY (`id`),
  KEY `idx_dict_code` (`dict_code`),
  KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='组织架构表';

表中的数据:

太多了,自己资料中复制

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第5张图片

4,根据页面效果分析数据接口:

数据字典是树形展示,由于数据众多,我们使用“树形数据与懒加载”的方式展现数据列表,其他就是对数据的新增、修改与删除操作,因此需要提供的接口如下:

1,根据上级id获取下级数据(构造树形数据),参考文档:https://element.eleme.cn/#/zh-CN/component/table,页面搜索:树形数据与懒加载
2,导入接口
3,导出接口
接下来我们封装服务器端数据接口,接口测试通过后再做页面渲染

二、数据字典开发:

1、搭建service-cmn模块:

1.1 搭建service-cmn模块,也是service父模块的子模块,与service_hosp同级

创建新模块:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第6张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第7张图片

1.2 修改配置

修改pom.xml
此pom:不修改

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第8张图片

添加配置文件application.properties,复制一个hosp的配置文件,
然后修改端口和服务名称spring.application.name

我们将数据字典的表放在mysql中,操作的话用xxxmapper来操作数据库;

# 服务端口
server.port=8202
# 服务名
spring.application.name=service-cmn

# 环境设置:dev、test、prod
spring.profiles.active=dev

# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/yygh_cmn?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root


# springboot2.6.9等高版本继承swagger需要增加这个配置
#spring.mvc.pathmatch.matching-strategy=ant_path_matcher

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

#spring.data.mongodb.uri=mongodb://192.168.44.165:27017/yygh_hosp

# nacos服务地址
#spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#rabbitmq地址
#spring.rabbitmq.host=192.168.44.165
#spring.rabbitmq.port=5672
#spring.rabbitmq.username=guest
#spring.rabbitmq.password=guest

#配置mapper xml文件的路径
#mybatis-plus.mapper-locations=classpath:com/atguigu/yygh/mapper/xml/*.xml
#mybatis-plus.mapper-locations=classpath:com/atguigu/yygh/mapper/xml/*.xml
# nacos服务地址
#spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#开启sentinel
#feign.sentinel.enabled=true
#设置sentinel地址
#spring.cloud.sentinel.transport.dashboard=http://127.0.0.1:8858

#mongodb地址
#spring.data.mongodb.host=192.168.44.163
#spring.data.mongodb.port=27017
#spring.data.mongodb.database=yygh_hosp

#rabbitmq地址
#spring.rabbitmq.host=127.0.0.1
#spring.rabbitmq.port=5672
#spring.rabbitmq.username=guest
#spring.rabbitmq.password=guest

#logging.level.root=DEBUG

创建包:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第9张图片

1.3 启动类:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第10张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第11张图片

启动类:

package com.fan.yygh.cmn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.fan")
public class ServiceCmnApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceCmnApplication.class,args);
    }
}


2、数据字典列表:

根据element组件要求,返回列表数据必须包含hasChildren字典,如图:

https://element.eleme.cn/#/zh-CN/component/table

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第12张图片

2.1 数据字典列表接口:

2.1.1 model模块添加数据字典实体
在model模块查看实体:com.fan.yygh.model.cmn.Dict

构建项目包:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第13张图片

配置类:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第14张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第15张图片

2.1.2 添加数据字典mapper接口

//添加com.fan.yygh.cmn.mapper.DictMapper
public interface DictMapper extends BaseMapper {
}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第16张图片

2.1.3 添加数据字典service接口

//1、添加com.fan.yygh.cmn.service.DictService
public interface DictService extends IService {
    //根据数据id查询子数据列表
    List findChlidData(Long id);
}

DictService中有一个泛型别忘记修改

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第17张图片

2、添加com.fan.yygh.cmn.service.impl.DictServiceImpl接口实现:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第18张图片

2.1.4 添加数据字典controller:

添加com.fan.yygh.cmn.controller.DictController:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第19张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第20张图片

为了配合这个ui层级关系;

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第21张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第22张图片

controller:

package com.fan.yygh.cmn.controller;

import com.fan.yygh.cmn.service.DictService;
import com.fan.yygh.common.result.Result;
import com.fan.yygh.model.cmn.Dict;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@Api(value = "数据字典接口")
@CrossOrigin
@RequestMapping("/admin/cmn/dict")
public class DictController {

    @Resource
    private DictService dictService;
    //
    @ApiOperation("根据数据id查询子数据列表")
    @GetMapping("findChildData/{id}")
    public Result findChildData(@PathVariable long id){
        List list = dictService.findChildData(id);
        return Result.ok(list);
    }

}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第23张图片

=注意:数据字典的表存在于mysql数据库中,所以我们用xxxMapper进行查询

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第24张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第25张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第26张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第27张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第28张图片

package com.fan.yygh.cmn.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fan.yygh.cmn.mapper.DictMapper;
import com.fan.yygh.cmn.service.DictService;
import com.fan.yygh.model.cmn.Dict;
import org.springframework.stereotype.Service;

import java.util.List;
@Service
public class DictServiceImpl extends ServiceImpl implements DictService {
    @Override
    public List findChildData(long id) {
        QueryWrapper wrapper = new QueryWrapper<>();
        //即条件是parent_id==id,即字典的父级id等于参数id时,返回那些数据
        wrapper.eq("parent_id",id);
        List dictList = baseMapper.selectList(wrapper);
        //向list集合每个dict对象中设置hasChildren字段值
        for (Dict dict : dictList) {
            Long dictId = dict.getId();//查出每个二级字典自己的主id
            boolean isChild = this.isChildren(dictId);
            dict.setHasChildren(isChild);
        }
        return dictList;
    }

    //判断id下面是否有子节点
    private boolean isChildren(Long id){
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id",id);
        Integer count = baseMapper.selectCount(queryWrapper);
        return count>0;
    }
}


医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第29张图片

用swagger测试后台数据:启动service_cmn服务和redis等:

id输入1 进行测试:
注意"hasChildren": true,字段;

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "id": 86,
      "createTime": "2019-06-10 18:43:35",
      "updateTime": "2020-06-23 23:43:48",
      "isDeleted": 0,
      "param": {},
      "parentId": 1,
      "name": "省",
      "value": "86",
      "dictCode": "Province",
      "hasChildren": true
    },
    {
      "id": 100,
      "createTime": "2022-08-02 03:17:57",
      "updateTime": "2022-08-02 03:22:46",
      "isDeleted": 0,
      "param": {},
      "parentId": 1,
      "name": "电脑",
      "value": "100",
      "dictCode": "cc",
      "hasChildren": true
    },
    {
      "id": 10000,
      "createTime": "2019-06-10 18:43:32",
      "updateTime": "2020-06-15 19:26:44",
      "isDeleted": 0,
      "param": {},
      "parentId": 1,
      "name": "医院等级",
      "value": null,
      "dictCode": "Hostype",
      "hasChildren": true
    },
    {
      "id": 20000,
      "createTime": "2019-06-10 18:43:32",
      "updateTime": "2020-06-16 22:52:57",
      "isDeleted": 0,
      "param": {},
      "parentId": 1,
      "name": "证件类型",
      "value": "20000",
      "dictCode": "CertificatesType",
      "hasChildren": true
    },
    {
      "id": 30000,
      "createTime": "2019-06-10 18:43:32",
      "updateTime": "2020-06-09 01:26:39",
      "isDeleted": 0,
      "param": {},
      "parentId": 1,
      "name": "学历",
      "value": "30000",
      "dictCode": "Education",
      "hasChildren": true
    },
    {
      "id": 99100,
      "createTime": "2019-06-11 18:39:11",
      "updateTime": "2020-06-09 01:26:39",
      "isDeleted": 0,
      "param": {},
      "parentId": 1,
      "name": "民族",
      "value": "99100",
      "dictCode": "Nation",
      "hasChildren": true
    }
  ],
  "ok": true
}

2.2 数据字典列表前端:

数据字典采用elementui的树形数据与懒加载:
医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第30张图片

这里本来后端数据需要一个层级显示的,但是ui帮我们进行了一个封装;

支持树类型的数据的显示。当 row 中包含 children 字段时,被视为树形数据。渲染树形数据时,必须要指定 row-key。支持子节点数据异步加载。设置 Table 的 lazy 属性为 true 与加载函数 load 。通过指定 row 中的 hasChildren 字段来指定哪些行是包含子节点。children 与 hasChildren 都可以通过 tree-props 配置

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第31张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第32张图片

漏由设置:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第33张图片

  /* 数据字典的漏由 */
  {
    path: '/cmn',
    component: Layout,
    redirect: '/cmn/list',
    name: '数据字典管理',
    alwaysShow:true, //总是显示
    meta: { title: '数据字典管理', icon: 'example' },
    children: [
      {
        path: 'list',
        name: '数据字典列表',
        component: () => import('@/views/dict/list'),
        meta: { title: '数据字典列表', icon: 'table' }
      }
    
    ]
  },

views创建:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第34张图片

接口定义:dict.js

import request from  '@/utils/request'

//定义接口路径的

export default {
  //数据字典列表
  dictList(id) {
   return request ({
     url: `/admin/cmn/dict/findChildData/${id}`,
     method: 'get'
   })
 }

 
}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第35张图片

页面调用:





医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第36张图片

此方法很重要

 //1.数据字典列表
        getDictList(id) {
            dict.dictList(id)  //调用api接口方法,==》调用后端查询方法
                .then(response => {
                    this.list = response.data
                })
        },
        //2、层级的递归遍历,前端ui帮我们做好了
        //定义此方法作用,ui帮我们做好的递归查询
        //getChildrens方法是关联的表格的这个:load="getChildrens"
        getChildrens(tree, treeNode, resolve) {
            //这里调用了api前端接口中的查询方法dictList,参数是下层节点的id,然后继续查询,返回的结果进行解析展示
            dict.dictList(tree.id).then(response => {
                resolve(response.data)
            })
        }

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第37张图片

将这的接口临时改成8202:
医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第38张图片

修改完端口号后,重启前端,查看:

由于前一个cmd终端没关掉,直接开启新的,9528端口被占用了:
医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第39张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第40张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第41张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第42张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第43张图片

换成高版本进行数据字典的下拉显示:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第44张图片

然后 npm install 从新下载依赖,并npm run dev ,测试:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第45张图片

lazy属性是我们点下拉按钮的时候才进行查询后台

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第46张图片

3、EasyExcel介绍

Java解析、生成Excel比较有名的框架有Apache poi、jxl但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。

文档地址:https://alibaba-easyexcel.github.io/index.html
github地址:https://github.com/alibaba/easyexcel

3.3 EasyExcel集成

3.3.1 添加依赖

1,添加依赖,将依赖放到cmn模块中:


    
    
        com.alibaba
        easyexcel
        2.1.1
    

2,导入导出需要定义对象,对象上需要引用easyexcel标签,所以model模块中的pom需要引入easyexcel依赖:如下:


com.alibaba
easyexcel
provided 

3,导入导出我们会把它封装成工具类,放在common-util中,所有模块公用,所以该模块common-util也得引入easyexcel依赖:


com.alibaba
easyexcel

3.4、数据字典导出:

2.创建包和实体类:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第47张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第48张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第49张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第50张图片

测试:

在这里插入图片描述

package com.fan.easyexcel;

import com.alibaba.excel.EasyExcel;

import java.util.ArrayList;

public class TestWriter {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            UserDate userDate = new UserDate();
            userDate.setUid(i);
            userDate.setUsername("tom"+i);
            list.add(userDate);
        }
        //设置excel文件路径和文件名称
        String fileName = "F:\\excel\\01.xlsx";
        //调用方法实现写操作
        EasyExcel.write(fileName,UserDate.class).sheet("用户信息")
                .doWrite(list);
    }
}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第51张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第52张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第53张图片

启动项目测试:运行main方法进行测试:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第54张图片

注意了:刚才写入操作中高版本jdk可能会报错导致数据加载不到xlsx中 换成低版本的就可以了

读操作:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第55张图片

创建监听器:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第56张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第57张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第58张图片

package com.fan.easyexcel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.util.ConverterUtils;

import java.util.Map;

public class ExcelListener extends AnalysisEventListener {
    @Override
    public void invoke(UserDate userDate, AnalysisContext analysisContext) {
        System.out.println(userDate);
    }

    @Override
    public void invokeHeadMap(Map headMap, AnalysisContext context) {
        System.out.println("表头信息:"+headMap);
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

测试类:

package com.fan.easyexcel;

import com.alibaba.excel.EasyExcel;

public class TestRead {
    public static void main(String[] args) {
        String fileName = "F:\\excel\\01.xlsx";
        EasyExcel.read(fileName,UserDate.class,new ExcelListener())
        .sheet().doRead();
    }
}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第59张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第60张图片

和项目的集成实操:

在model模块添加导出实体:
在model模块查看实体:com.atguigu.yygh.vo.cmn.DictEeVo

然后:

controller:

//导出数据字典接口
    @GetMapping("exportData")
    public Result exportDict(HttpServletResponse response){
        dictService.exportDictData(response);
        return Result.ok();
    }

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第61张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第62张图片

实现类DictServiceImpl:

    @Override //导出字典的接口
    public void exportDictData(HttpServletResponse response) {
        //设置下载信息
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");

        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = null;
        try {
            fileName = URLEncoder.encode("dict", "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");

        List dictList = baseMapper.selectList(null);
        //Dict-->DictEevo
        ArrayList dictVoList = new ArrayList<>();
        for (Dict dict : dictList) {
            DictEeVo dictEeVo = new DictEeVo();
            BeanUtils.copyProperties(dict,dictEeVo);
            dictVoList.add(dictEeVo);
        }
        //调用方法进行写操作
        try {
            EasyExcel.write(response.getOutputStream(),DictEeVo.class)
                    .sheet("dict")
                    .doWrite(dictVoList);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

前端:

列表页面添加导出按钮
src/views/cmn/dict/list.vue

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第63张图片

导出按钮标签:

       
        
导出

js添加导出方法:

exportData() {
	window.location.href = 'http://localhost:8202/admin/cmn/dict/exportData'
}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第64张图片

测试:谷歌自动连接到迅雷下载了:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第65张图片

查看下载的内容:
医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第66张图片

点击之后直接下载没有弹窗提示下载路径的,和浏览器设置有关,默认下载到浏览器下载地址

改造下载页面:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第67张图片

直接下载是和浏览器有关, 在设置里面有个:下载前询问每个文件的保存位置,选上就会有弹框了

导入数据字典的controller:

//导入数据字典的controller
    @PostMapping("importData")
    public Result importDict(MultipartFile file){
        dictService.importDictData(file);
        return Result.ok();
    }

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第68张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第69张图片

监听器:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第70张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第71张图片

spring构造器注入、还可以setting注入

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第72张图片

实现类DictServiceImpl中:

  @Override//导入数据字典的controller
    public void importDictData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(),DictEeVo.class,new DictListener(baseMapper))
                    .sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第73张图片

前端:

列表页面添加导入按钮,说明:按钮位置与导出并列

src/views/cmn/dict/list.vue
 导入

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第74张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第75张图片
添加导入弹出层:

                
                
                    

                    
                    
                    点击上传
                    
只能上传xls文件,且不超过500kb

dialog:
医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第76张图片

添加弹出可见模型:

// 定义数据
data() {
return {
	list: [],
	listLoading: true,
	dialogImportVisible: false  //设置弹框是否弹出
}
}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第77张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第78张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第79张图片

测试:

id 上级id 名称 值 编码
100 1 电脑 100 cc
1001 100 华为电脑 1001 huawei

在这里插入图片描述

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第80张图片

页面测试:重启后台:

看数据库:已经上传上去;将逻辑删除改成0看页面显示不:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第81张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第82张图片

三、Spring Cache + Redis 缓存数据

缓存:

Spring Cache 是一个非常优秀的缓存组件。自Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache支持,且提供了Cache抽象,方便切换各种底层Cache(如:redis)

使用Spring Cache的好处:
1,提供基本的Cache抽象,方便切换各种底层Cache;
2,通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;
3,提供事务回滚时也自动回滚缓存;
4,支持比较复杂的缓存逻辑;

1、项目集成Spring Cache + Redis:

因为缓存也是公共使用,所有的service模块都有可能使用缓存,所以我们把依赖与部分配置加在service-util模块,这样其他service模块都可以使用了

1.1service-util添加依赖

在service-util模块的pom.xml添加依赖



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




org.apache.commons
commons-pool2
2.6.0

1.2 service-util添加配置类

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第83张图片

创建com.atguigu.yygh.common.config.RedisConfig

package com.fan.yygh.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;
import java.time.Duration;

@Configuration
@EnableCaching //开启缓存
public class RedisConfig {
    /**
     * 自定义key规则
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * 设置RedisTemplate规则
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

//解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

//序列号key value
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 设置CacheManager缓存规则
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

//解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

// 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

说明:
@EnableCaching:标记注解 @EnableCaching,开启缓存,并配置Redis缓存管理器。@EnableCaching 注释触发后置处理器, 检查每一个Spring bean 的 public 方法是否存在缓存注解。如果找到这样的一个注释, 自动创建一个代理拦截方法调用和处理相应的缓存行为

1.3service-cmn添加redis配置

spring.redis.host=192.168.44.165
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000

spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

常用缓存标签

2.1.2 缓存@Cacheable

根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key

2.1.2 缓存@CachePut
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key

2.1.3 缓存@CacheEvict

evict 逐出,赶出,收回,清空

使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key
allEntries 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存
beforeInvocation 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存

2.数据字典应用缓存

改造com.fan.yygh.cmn.service.impl.DictServiceImpl类方法:

类似于事务的注解一样的使用:

查询方法需要的注解:
@Cacheable(value = “dict”,keyGenerator = “keyGenerator”)
导入方法需要的注解:

allEntries = true: 方法调用后清空所有缓存

@CacheEvict(value = “dict”, allEntries=true)

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第84张图片

value: 缓存管理器中配置的缓存的名称,这里可以理解为一个组的概念,缓存管理器中可以有多套缓存配置,每套都有一个名称,类似于组名,这个可以配置这个值,选择使用哪个缓存的名称,配置后就会应用那个缓存名称对应的配置。

下面介绍一下 @Cacheable 这个注解常用的几个属性:

cacheNames/value :用来指定缓存组件的名字

key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)

keyGenerator :key 的生成器。 key 和 keyGenerator 二选一使用

cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。

condition :可以用来指定符合条件的情况下才缓存

unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)

sync :是否使用异步模式。

① cacheNames

用来指定缓存组件的名字,将方法的返回结果放在哪个缓存中,可以是数组的方式,支持指定多个缓存。

在这里插入图片描述

新增:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第85张图片

启动后端和redis:测试:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第86张图片

前端页面查询后:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第87张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第88张图片

获取此key 对应的value:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第89张图片

四、nginx配置不同的访问端口:

配置nginx:

由于我们后端有很多服务模块,每个模块都有对应的访问路径与端口,为了提供统一的api接口,所以使用nginx作为反向代理服务器;

反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,在返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。

1,下载安装nginx(window版)

2,配置nginx:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第90张图片

为了简单化,使用windows的nginx:

下载到本地解压:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第91张图片
nginx的配置:

server {
	listen 80;
	server_name localhost;
	location /api/hosp/ {           
		proxy_pass http://localhost:8201;
	}
	location /api/cmn/ {           
		proxy_pass http://localhost:8202;
	}
...
}

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第92张图片

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第93张图片

启动:nginx

注意:启动报错的话,把nginx文件夹单独放出来到根目录。重新启动;

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第94张图片

3,调整/config/dev.env.js中的BASE_API

BASE_API: ‘http://localhost’
说明:
1、后续添加service模块自行添加nginx配置,不做说明
后续我们将了Spring Cloud Gateway网关,将替代nginx网关

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第95张图片

测试:

医院项目-预约挂号-第三部分(数据字典及字典的redis缓存)_第96张图片

提醒:这里只需要开启一个nginx即可,不需要在后端修改任何东西。前端只需要修改一个base路径即可

然后提醒:linux的docker不稳定,linux中的redis服务和mongodb容易崩。建议将整个vm虚拟机重启。然后开启redis等

你可能感兴趣的:(springcloud,springboot,javaweb,数据库,大数据,java)