一.MyBatis-Plus
1.主键生成雪花算法:@TableId(idType.ASSIGN_ID)
@TableId注解用来指定该实体类属性为表主键,默认值为生成雪花算法,AUTO是自增,INSERRT是需要手动添加
2.实体类映射对应的数据库表: @TableName(“table_name”)
当实体类和数据库的表名不同却想映射两者时,使用该注解,参数写上数据库的表名即可完成映射
3.1.字段内容自动填充:@TableField(fill = FieldFill_INSERT)
实现这个功能需要创建一个组件实现MetaObjectHandler接口,实现insertFill() 和 updateFill()方法
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充[insert]...");
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
metaObject.setValue("createUser",BaseContext.getCurrentId());
metaObject.setValue("updateUser",BaseContext.getCurrentId());
}
3.2.乐观锁:@Version
给表新增一个version字段,实体类属性上添加该注解,sql语句中就会添加where version = ?,这样在并发事务时,如果事务A在更新前事务A先执行了,version就会加1,事务A的sql无法匹配条件从而取消本次更新。
//旧版本用法
@Configuration
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor () {
return new OptimisticLockerInterceptor();
}
}
//3.4.0之后
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
//另外:分页器也在这里添加 interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
二.解决Property ‘mapperLocations‘ was not specified or no matching resources found问题
在编写mybatis映射文件时,将XML文件放到了包下,而不是resources文件夹下,maven在打包的时候,默认是不会把xml文件打包编译,因此需要手动在pom文件配置,让maven在编译时把xml文件也进行输出
<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
# 因为xml文件在java目录中,所以要扫描所在目录
mybatis-plus.mapper-locations=classpath:com/atguigu/yygh/hosp/mapper/xml/*.xml
神TM枚举类,Spring封装pojo类型的返回值时是根据setter方法来确定属性的,由于Result里有个isOk方法,所以ok被当作了属性(布尔类型返回值的getter方法默认前缀就是is)
三.SpringBoot相关
1.@ControllerAdvice注解(用于全局异常处理)
@RestControllerAdvice //一种对所有controller增强的组件,能够根据条件拦截下来
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) //出现异常就拦截下来,走自定义的方法
public Result error(Exception e){
e.printStackTrace();
return Result.fail();
}
}
2.@Mapper和@MapperScan的区别
两者都是为指定mapper的接口生成对应的实现类,@Mapper加在一个mapper接口上,但使用@MapperScan可以扫描指定的包路径,为扫描路径的所有mapper接口生成对应的实现类,该注解一般加在配置类上。
3.关于Springboot的测试类操作
正常操作是导入junit依赖,但在springboot中只需要引入springboot-starter-test依赖(内嵌了junit)
就算是测试类也要写主包的启动类
在测试类中添加@SpringBootTest(参数classes指定为主包的启动类)和@RunWith(SpringRunner.class)注解【RunWith是junit和spring整合用的,让测试运行于Spring测试环境】
另外:如果测试中不需要连接数据库,添加@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}),springboot是默认要连接数据库的,这个exclude参数就是说明不需要连接数据库。
四.EasyExcel
将数据集合导出到Excel:
public void exportDictData(HttpServletResponse response) {
try {
response.setContentType("application/vnd.ms-excel");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("数据字典", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
List<Dict> list = this.baseMapper.selectList(null);
//获取浏览器发送的响应输出流
EasyExcel.write(response.getOutputStream(), DictEeVo.class)
.sheet("dict").doWrite(list);
} catch (Exception e) {
e.printStackTrace();
}
@Data
public class DictEeVo {
@ExcelProperty(value = "id" ,index = 0)
private Long id;
@ExcelProperty(value = "上级id" ,index = 1)
private Long parentId;
@ExcelProperty(value = "名称" ,index = 2)
private String name;
@ExcelProperty(value = "值" ,index = 3)
private String value;
@ExcelProperty(value = "编码" ,index = 4)
private String dictCode;
}
//从Excel读取数据字典导入
//Service层
public void importDictData(MultipartFile multipartFile){
try {
EasyExcel.read(multipartFile.getInputStream(),DictEeVo.class,
new DictDataListener(baseMapper)).sheet().doRead();
}catch (Exception e){
e.printStackTrace();
}
}
public class DictDataListener extends AnalysisEventListener<DictEeVo> {
private DictMapper dictMapper;
public DictDataListener(DictMapper dictMapper){
this.dictMapper = dictMapper;
}
//excel一行一行的读取
@Override
public void invoke(DictEeVo dictEeVo, AnalysisContext analysisContext) {
Dict dict = new Dict();
BeanUtils.copyProperties(dictEeVo,dict);
dictMapper.insert(dict);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
五.Spring Cache + Redis
@EnableCaching开启Cache
# Redis配置
#spring.redis.host=192.168.44.165
spring.redis.host=127.0.0.1
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
// 对结果进行缓存,获取结果时如果缓存中没有就将存入缓存,有则从缓存中拿。用于查询方法
// keyGenerator:属性用于自定义键的命名方式
// value:缓存名,必填,它指定了你的缓存存放在哪块命名空间
@Cacheable(value = "dict",keyGenerator = "keyGenerator")
@Bean //redis配置类中
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();
}
};
}
@CachePut:每次都会执行,并将结果存入指定的缓存中,用于新增方法
@CacheEvict: 清空指定的缓存。一般用在更新或者删除方法上
属性:allEntries->方法调用后将立即清空所有的缓存
beforeInvocation->方法执行前就会清空缓存
六.Nacos + Feign
feign能够依照Restful风格帮助快速开发服务间的调用
使用Feign的前提是服务已经在Nacos中注册
// 导依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
</dependency>
// 创建一个接口
//指定要调用的服务名,该服务名需要在对应的服务模块的配置文件中配置
//(spring.application.name=service_cmn)
@FeignClient("service-cmn")
public interface DictFeignClient {
// 医院等级的value可能不唯一,所以还需要dictCode
@GetMapping("/admin/cmn/dict/getName/{dictCode}/{value}")
// 有一点要注意!!!@PathVariable中要指定名称
// 在普通类上定义@PatnVariable注解时value值可以不用声明,直接写值就好,但是在Feign接口下使用该注解,则需要显示声明value,否则会报错
String getName(@PathVariable("dictCode") String dictCode, @PathVariable("value") String value);
@GetMapping("/admin/cmn/dict/getName/{value}")
String getName(@PathVariable("value") String value);
}
七.关于前后端Restful风格的参数传递与接收
1. @RequestParam : 该注解常用于Put修改方式
后端的RequstMapper路径无需写参数,参数会在请求头中出现,跟在请求路径后面,
以 http://xxxxxxx?param1=xxx¶m2=xxx 的显示出现,多个参数时RequestParam需要指明参数名
前端对应的数据请求形式为: params:{ ‘param1’ : xxx, ‘param2’ : xxxx }
2. @RequestBody : 该注解常用于Post方式进行新增
后端的RequstMapper路径无需写参数,RequestBody用于注解对象,接收前端传来的json格式对象
前端对应的数据请求发送形式为 : data : object
3.@ResponseBody:该注解常置于Controller类上方
将方法的返回值写入response的body区,转化为json格式
4. @PathVariable : 该注解常用于Get方式进行查询
后端的RequstMapper路径以参数为{param}形式设置,前后端的请求发送格式为 http://xxxxxxx/{param1}/{param2}
实际发送的请求格式为http://xxxxxxx/param1/param2,参数会跟在请求路径上
八.服务器docker容器连接相关
如果是连接本地虚拟机的只需要设置常规:端口写docker开放的端口,ip写虚拟机的ip
如果是连接云服务器的虚拟机,常规的ip写localhost(127.0.0.1),SSH写云服务器的账号(root)和密码(xxxxxxxxx)(也就是该虚拟机主机的登录用户名和登录密码,可以在阿里云控制台修改),端口默认22,主机ip写公网的ip,私网无法连接
mysql 127.0.0.1 Touko217 redis 127.0.0.1 123456
九.reggie相关知识点
1.ThreadLocal类:
Ⅰ.内置一个ThreadLocalMap属性,是一个Entry(k-v),用来存储当前线程的一个变量。对该变量进行线程间的隔离,每个线程都有一个该变量的副本,每个线程只能访问修改各自的变量副本。一般用private static修饰
Ⅱ.使用场景:
①每个线程需要有自己单独的实例
②实例需要在多个方法中共享,但不希望被多个线程间共享,例如存储用户登录的Session信息
2.异常处理器:
@ExcepionHandler:用于统一处理抛出的异常,置于自定义方法的上方,注解参数为xxxException.class,表示处理指定的异常,并接收异常在自定义方法中对异常进行处理,自定义方法的参数也为异常类型,用于接收异常。
@ControllerAdvice:对Controller的一种增强。用于拦截规则,然后具体你想做更细致的拦截筛选和拦截之后的处理,你自己通过@ExceptionHandler
、@InitBinder
或 @ModelAttribute
这三个注解以及被其注解的方法来自定义。
//参数annotation表示对添加指定注解的Controller进行拦截
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptionHandler {
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
String exMessage = ex.getMessage();
log.info(exMessage);
if (exMessage.contains("Duplicate entry")){
String[] split = exMessage.split(" ");
String s = split[2] + "已存在";
return R.error("s");
}
return R.error("未知错误");
}
}
3.自定义异常的创建
RuntimeException<—-Exception<—-Throwable
创建一个类继承RuntimeException类,重写父类的构造方法,即Throwable的构造方法public Throwable(String message),该方法用于在类内部设置message,detailMessage = message;
定义出的自定义异常使用时可以用getMessage()获取detailMessage
4.泛型方法
①<T> T
用法:
public static
②T 用法:
和①一样的意思,只是不加
另外,静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。即: public static
总结:静态方法由于随着类的加载而加载,不能访问类的泛型(因为在创建对象的时候才确定),因此必须定义自己的泛型类型。
十.JackJson
<!-- 导入jackjson依赖包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
创建一个继承ObjectMapper的类
1.ObjectMapper的常用方法
ObjectMapper.writeValueAsString(Object obj) :能够将java对象转为json字符串
ObjectMapper.readValue(String,Class<>) :能够将json字符串转化为指定类的对象
2.自定义类型转换器
先定义一个自定义类型的转换器,然后再扩展mvc的消息转换器
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
/**
* 扩展mvc的消息转换器,自定义类继承WebMvcConfigurationSupport(spring-boot-starter-web包)
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器");
//创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置对象转换器
messageConverter.setObjectMapper(new JacksonObjectMapper());
//将消息转换器添加到mvc框架的转换器集合中
converters.add(0,messageConverter);
}
// 静态资源映射
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始进行静态资源映射...");
//如果访问路径是addResourceHandler中的filepath 这个路径 那么就 映射到访问本地的addResourceLocations 的参数的这个路径上,这样就可以让别人访问服务器的本地文件了,映射的真实路径末尾必须加 / ,不然映射不到
//classpath指的是类路径,也就是编译之后的target文件夹下的WEB-INF/class文件夹
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
十一.Filter拦截器(Web三大组件之一Servlet、Listener)
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")//表示拦截所有路径
@Order(Integer value) //value越小,优先级越高
1.自定义类继承Filter接口,Filter中包含三个方法:
①init(FilterConfig filterConfig):
②doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3):
进行路径的拦截、处理和放行,调用filterChain.doFilter(request,response);放行
调用response.getWriter().write(XXX);向前端返回数据
③destroy()