何为数据字典?数据字典就是管理系统常用的分类数据或者一些固定数据,例如:省市区三级联动数据、民族数据、行业数据、学历数据等,由于该系统大量使用这种数据,所以我们要做一个数据管理方便管理系统数据,一般系统基本都会做数据管理。
数据字典的表:
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='组织架构表';
表中的数据:
太多了,自己资料中复制
1,搭建service-cmn模块
创建新模块;
依赖:不修改
添加配置文件application.properties,复制一个hosp的修改端口和spring.application.name
# 服务端口
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
创建包:
启动类:
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);
}
}
构建项目包:
配置类:
service接口:
DictService中有一个泛型别忘记修改
添加数据字典controller:
为了配合这个ui层级关系;
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);
}
}
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;
}
}
漏由设置:
/* 数据字典的漏由 */
{
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创建:
接口定义:dict.js
import request from '@/utils/request'
//定义接口路径的
export default {
//数据字典列表
dictList(id) {
return request ({
url: `/admin/cmn/dict/findChildData/${id}`,
method: 'get'
})
}
}
页面调用:
{{ scope.row.name }}
{{ row.dictCode }}
{{ scope.row.value }}
{{ scope.row.createTime }}
将这的接口临时改成8202:
修改完端口号后,重启前端,查看:
由于前一个cmd终端没关掉,直接开启新的,9528端口被占用了:
换成高版本进行数据字典的下拉显示:
然后 npm install 从新下载依赖,并npm run dev ,测试:
lazy属性是我们点下拉按钮的时候才进行查询后台
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
1,添加依赖,将依赖放到cmn模块中:
com.alibaba
easyexcel
2.1.1
2.创建包和实体类:
测试:
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);
}
}
启动项目测试:运行main方法进行测试:
注意了:刚才写入操作中高版本jdk可能会报错导致数据加载不到xlsx中 换成低版本的就可以了
读操作:
创建监听器:
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();
}
}
和项目的集成实操:
在model模块添加导出实体:
在model模块查看实体:com.atguigu.yygh.vo.cmn.DictEeVo
然后:
controller:
//导出数据字典接口
@GetMapping("exportData")
public Result exportDict(HttpServletResponse response){
dictService.exportDictData(response);
return Result.ok();
}
实现类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
导出按钮标签:
js添加导出方法:
exportData() {
window.location.href = 'http://localhost:8202/admin/cmn/dict/exportData'
}
测试:谷歌自动连接到迅雷下载了:
查看下载的内容:
点击之后直接下载没有弹窗提示下载路径的,和浏览器设置有关,默认下载到浏览器下载地址
改造下载页面:
直接下载是和浏览器有关, 在设置里面有个:下载前询问每个文件的保存位置,选上就会有弹框了
导入数据字典的controller:
//导入数据字典的controller
@PostMapping("importData")
public Result importDict(MultipartFile file){
dictService.importDictData(file);
return Result.ok();
}
监听器:
spring构造器注入、还可以setting注入
实现类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();
}
}
前端:
列表页面添加导入按钮,说明:按钮位置与导出并列
src/views/cmn/dict/list.vue
导入
添加导入弹出层:
点击上传
只能上传xls文件,且不超过500kb
dialog:
添加弹出可见模型:
// 定义数据
data() {
return {
list: [],
listLoading: true,
dialogImportVisible: false //设置弹框是否弹出
}
}
测试:
id 上级id 名称 值 编码
100 1 电脑 100 cc
1001 100 华为电脑 1001 huawei
页面测试:重启后台:
看数据库:已经上传上去;将逻辑删除改成0看页面显示不:
缓存:
Spring Cache 是一个非常优秀的缓存组件。自Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache支持,且提供了Cache抽象,方便切换各种底层Cache(如:redis)
使用Spring Cache的好处:
1,提供基本的Cache抽象,方便切换各种底层Cache;
2,通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;
3,提供事务回滚时也自动回滚缓存;
4,支持比较复杂的缓存逻辑;
项目集成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添加配置类
创建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
说明:
@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,则在方法执行前就会清空缓存 |
修改impl:
查询需要的注解:
@Cacheable(value = “dict”,keyGenerator = “keyGenerator”)
导入需要的注解:
@CacheEvict(value = “dict”, allEntries=true)
value: 缓存管理器中配置的缓存的名称,这里可以理解为一个组的概念,缓存管理器中可以有多套缓存配置,每套都有一个名称,类似于组名,这个可以配置这个值,选择使用哪个缓存的名称,配置后就会应用那个缓存名称对应的配置。
下面介绍一下 @Cacheable 这个注解常用的几个属性:
cacheNames/value :用来指定缓存组件的名字
key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)
keyGenerator :key 的生成器。 key 和 keyGenerator 二选一使用
cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
condition :可以用来指定符合条件的情况下才缓存
unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)
sync :是否使用异步模式。
① cacheNames
用来指定缓存组件的名字,将方法的返回结果放在哪个缓存中,可以是数组的方式,支持指定多个缓存。
新增:
启动后端和redis:测试:
前端页面查询后:
获取此key 对应的value:
为了简单化,使用windows的nginx:
下载到本地解压:
启动:nginx
注意:启动报错的话,把nginx文件夹单独放出来到根目录。重新启动;
‘’
修改:
测试: