当前位置: 首页 > news >正文

记录一次关于spring映射postgresql的jsonb类型的转化器事故,并使用hutool的JSONArray完成映射

事件的起因是这样的,那次事故发生的起因是因为WebFlux和postgreSQL去重新做鱼皮的鱼图图项目(鱼图图作业)。

在做到picture表的时候,发现postgreSQL中有个jsonb的类型可以更好的支持json数组。

出于锻炼新技术的目的,本人过于自信的选择选择了jsonb格式类型,谁知道是长达两天的折磨,不过好在最后是解决,这个问题了,主要是当时出的问题在网上一直没有查询到关键的解法,打算写一篇博客记录一下,为后面的新技术开发提供经验。

jsonb在Java中对应的映射类型

这个在网上非常好查到

io.r2dbc.postgresql.codec.Json

使用此类型作为映射的字段即可,这也是我的第一版实体类(故事开始了)

package com.yupi.yunpicturebackend.model.entity.picture;import com.yupi.yunpicturebackend.common.r2dbc.BaseEntity;
import io.r2dbc.postgresql.codec.Json;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;import java.time.LocalDateTime;@Table("picture")
@Data
@Accessors(chain = true)
public class Pictureextends BaseEntity {@Idprivate String id;//*图片idprivate String url;//*图片urlprivate String name;//*图片名称private String introduction;//图片简介private String category;//图片分类private Json tags;//图片标签private Long picSize;//图片大小private Integer picWidth;//图片宽度private Integer picHeight;//图片高度private Double picScale;//图片比例private String picFormat;//图片格式private String userId;//*用户idprivate LocalDateTime createTime;//*创建时间private LocalDateTime editTime;//*编辑时间private LocalDateTime updateTime;//*更新时间private Boolean isDelete;//是否删除
}

这里使用String作为id的映射类是因为这里的id设置的是uuid。

然后简单的写完spring-data对于R2DBC的支持及项目的服务类,ReactBaseRepository是我自己对于R2dbcRepository的封装,ReactBaseService,ReactBaseServiceImpl,BaseEntity 是对于ReactBaseRepository业务一些默认泛型处理,包括分页条件查询什么的,在踩完jsonb这个坑的时候仅支持id为String类型的泛型分页查询,某条件下的数量统计,尚不完善,如果完善的话就再写一篇博客,没有大概率是鸽了【狗头】。

package com.yupi.yunpicturebackend.repositories;import com.yupi.yunpicturebackend.common.r2dbc.repository.ReactBaseRepository;
import com.yupi.yunpicturebackend.model.entity.picture.Picture;
import org.springframework.data.r2dbc.repository.Modifying;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;@Repository
public interface PictureRepositoryextends ReactBaseRepository<Picture, String> {@Override@Modifying@Query("UPDATE picture SET is_delete = true WHERE id = :#{#id}")Mono<Integer> deleteByLogicId(@Param("id") String id);
}
package com.yupi.yunpicturebackend.service;import com.yupi.yunpicturebackend.common.r2dbc.service.ReactBaseService;
import com.yupi.yunpicturebackend.model.entity.picture.Picture;
import com.yupi.yunpicturebackend.repositories.PictureRepository;public interface PictureServiceextends ReactBaseService<Picture, PictureRepository> {
}
package com.yupi.yunpicturebackend.service.impl;import com.yupi.yunpicturebackend.common.r2dbc.service.impl.ReactBaseServiceImpl;
import com.yupi.yunpicturebackend.model.entity.picture.Picture;
import com.yupi.yunpicturebackend.repositories.PictureRepository;
import com.yupi.yunpicturebackend.service.PictureService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.stereotype.Service;@Service
public class PictureServiceImplextends ReactBaseServiceImpl<Picture, PictureRepository>implements PictureService {private final Picture picture_void = new Picture();private final Logger log = LoggerFactory.getLogger(PictureService.class);@Autowiredpublic PictureServiceImpl(R2dbcEntityTemplate template,PictureRepository r2dbcRepository) {super(template,r2dbcRepository);}
}

写一个简单的测试方法试一下

@SpringBootTest
public class PictureTestApplication {@Testpublic void 图片测试_新增() throws IOException {PictureService pictureService = SpringUtil.getBean(PictureService.class);JSONArray tags = JSONUtil.parseArray("[\"标签3\",\"标签4\"]");System.out.println(Json.of(tags.toString()));pictureService.save(new Picture().setUrl("测试图片地址").setName(RandomUtils.generateUniqueName()).setUserId("1").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now()).setEditTime(LocalDateTime.now()).setTags(Json.of(tags.toString()))).subscribe(System.out::println);System.in.read();}
}

这里的save是我在ReactBaseServiceImpl的一个实现,可以理解为调用了ReactBaseRepository的save方法,RandomUtils.generateUniqueName()是本人制作的一个小玩意,如下所示,效果大概是获取一个伪随机的名字。

package com.yupi.yunpicturebackend.utils;import cn.hutool.core.util.RandomUtil;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class RandomUtils {// 用于存储已经生成的名字,确保不重复private static final List<String> generatedNames = new ArrayList<>();// 修饰词列表private static final String[] adjectives = {"勇敢", "聪明", "神秘", "强大", "古老", "美丽", "温柔", "快速", "坚硬", "灵活","炽热", "冰冷", "光明", "黑暗", "梦幻", "现实", "华丽", "朴素", "高贵", "平凡","稀有", "常见", "珍贵", "普通", "完美", "残缺", "幸运", "不幸", "欢乐", "悲伤","幸福", "痛苦", "寂静", "喧闹", "遥远", "接近", "巨大", "微小", "繁多", "单一","锋利", "钝拙", "深邃", "浅显", "真实", "虚假", "神圣", "邪恶", "忠诚", "背叛",// ... 可以继续添加更多修饰词};// 语气词列表private static final String[] particles = {"的","の"};// 名字来源列表private static final String[] items = {"剑", "盾", "弓", "箭", "书", "画", "琴", "棋", "茶", "酒","花", "草", "木", "石", "玉", "金", "银", "铜", "铁", "锡",// ... 可以继续添加更多物品名};private static final String[] animals = {"龙", "凤", "虎", "豹", "狮", "象", "狼", "熊", "狐", "鹿","鹰", "鸽", "蛇", "龟", "兔", "鼠", "猫", "狗", "马", "牛",// ... 可以继续添加更多动物名};private static final String[] mythologicalFigures = {"奥丁", "宙斯", "赫拉", "阿波罗", "雅典娜", "波塞冬", "哈迪斯", "狄俄尼索斯","赫尔墨斯", "阿瑞斯", "阿佛洛狄忒", "赫菲斯托斯", "阿尔忒弥斯", "潘", "美杜莎",// ... 可以继续添加更多神话人物名};private static final String[] sciFiCharacters = {"机器人", "外星人", "赛博格", "宇航员", "科学家", "探险家", "飞行员", "时空旅行者","未来战士", "星际商人", "克隆人", "虚拟偶像", "AI助手", "基因改造人", "数字幽灵",// ... 可以继续添加更多科幻虚构人物名};public static String generateUniqueName() {String name;do {name = createRandomName();} while (generatedNames.contains(name) && generatedNames.size() < 1000);generatedNames.add(name);return name;}private static String createRandomName() {StringBuilder nameBuilder = new StringBuilder();// 随机选择一个修饰词nameBuilder.append(RandomUtil.randomEle(adjectives));// 随机选择一个语气词if (RandomUtil.randomBoolean()) nameBuilder.append(RandomUtil.randomEle(particles));// 随机选择一个名字来源nameBuilder.append(RandomUtil.randomEle(RandomUtil.randomEle(Arrays.asList(items, animals, mythologicalFigures, sciFiCharacters))));// 判断是否含有语气助词if (!Arrays.stream(particles).anyMatch(nameBuilder.toString()::contains)){if (RandomUtil.randomBoolean()) {nameBuilder.append(RandomUtil.randomEle(particles));nameBuilder.append(RandomUtil.randomEle(RandomUtil.randomEle(Arrays.asList(items, animals, mythologicalFigures, sciFiCharacters))));}}return nameBuilder.toString();}}

点击运行,可以看见运行结果,这里spring的日志配置是如下所示,所以会打印一些r2dbc的日志。

logging:level:org.springframework.data.r2dbc: DEBUGio.r2dbc.postgresql.QUERY: DEBUG # for queries

 运行结果如下所示,写这篇踩坑心得之前已经踩完坑了,所以数据库不是空的。

 那么再按照鱼皮的用法,再封装一个vo中间键就大工完成了。

package com.yupi.yunpicturebackend.model.entity.picture.vo;import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.yupi.yunpicturebackend.model.entity.picture.Picture;
import com.yupi.yunpicturebackend.model.entity.user.vo.SysUserVo;
import io.r2dbc.postgresql.codec.Json;
import lombok.Data;
import org.springframework.beans.BeanUtils;import java.io.Serializable;
import java.time.LocalDateTime;@Data
public class PictureVOimplements Serializable {private static final long serialVersionUID = 1L;/*** id*/private String id;/*** 图片 url*/private String url;/*** 图片名称*/private String name;/*** 简介*/private String introduction;/*** 标签*/private JSONArray tags;/*** 分类*/private String category;/*** 文件体积*/private Long picSize;/*** 图片宽度*/private Integer picWidth;/*** 图片高度*/private Integer picHeight;/*** 图片比例*/private Double picScale;/*** 图片格式*/private String picFormat;/*** 用户 id*/private Long userId;/*** 创建时间*/private LocalDateTime createTime;/*** 编辑时间*/private LocalDateTime editTime;/*** 更新时间*/private LocalDateTime updateTime;/*** 创建用户信息*/private SysUserVo user;/*** 封装类转对象*/public static Picture voToObj(PictureVO pictureVO) {if (pictureVO == null) {return null;}Picture picture = new Picture();BeanUtils.copyProperties(pictureVO,picture);//类型不同,需要转换  picture.setTags(Json.of(JSONUtil.toJsonStr(pictureVO.getTags())));return picture;}/*** 对象转封装类*/public static PictureVO objToVo(Picture picture) {if (picture == null) {return null;}PictureVO pictureVO = new PictureVO();BeanUtils.copyProperties(picture,pictureVO);// 类型不同,需要转换  if (picture.getTags() != null)pictureVO.setTags(JSONUtil.parseArray(picture.getTags().asString()));return pictureVO;}
}

但是对于这样的结果我不是很认可,至少在我向它妥协之前是这样的,在我看来应该避免io.r2dbc.postgresql.codec.Json 在实体类中的出现,以为它不是被java语法或者java 8语法定义的,我看了一下它的底层,是对于一个字节流的封装,这就导致类似hutool工具对于它的支持也很少,在项目完工后它一定只会在类似dao层接受数据的地方出现,从逻辑上来说他不该作为实体类。而且对应每一个含有io.r2dbc.postgresql.codec.Json对象的实体类都做一次转换成vo,调用一次hutool工具的copy bean方法也是有不小的工作量。

根据我在b站学习了一个小时左右的webflux经验来看,我们需要定义一个转换器(天坑)

jsonb转换器

这里网上的写法有很多,出于方便以后我回忆故事,这里我只展示最终版本的代码以及中间踩了什么坑。

package com.yupi.yunpicturebackend.config;import com.yupi.yunpicturebackend.common.r2dbc.converter.json.JsonArrayToJson;
import com.yupi.yunpicturebackend.common.r2dbc.converter.json.JsonToJsonArray;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.convert.R2dbcCustomConversions;
import org.springframework.data.r2dbc.dialect.PostgresDialect;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;import java.util.ArrayList;
import java.util.List;@EnableR2dbcRepositories(basePackages = "com.yupi.yunpicturebackend.repositories")//开启R2DBC的仓库功能
@Configuration
public class R2DBCConfig {@Beanpublic R2dbcCustomConversions customConversions() {List<Object> converters = new ArrayList();converters.add(new JsonArrayToJson());converters.add(new JsonToJsonArray());return R2dbcCustomConversions.of(PostgresDialect.INSTANCE,converters);}
}
@WritingConverter
public class JsonArrayToJson implements Converter<JSONArray, Json> {@Overridepublic Json convert(JSONArray source) {return Json.of(source.toString());}
}
@ReadingConverter
public class JsonToJsonArrayimplements Converter<Json, JSONArray> {@Overridepublic JSONArray convert(Json source) {return JSONUtil.parseArray(source.asString());}
}

最终的代码非常简单,但是却踩了不少坑。

注意下面的配置类用了就会报错,不做参考,仅做警醒

①错误的替换了所有的转换器


@EnableR2dbcRepositories(basePackages = "com.yupi.yunpicturebackend.repositories")//开启R2DBC的仓库功能
@Configuration
public class R2DBCConfig {@Beanpublic R2dbcCustomConversions customConversions() {return new R2dbcCustomConversions(Collections.singletonList(new PictureReadingConvert()));}}

这里直接调用了转换器的new方法,在不熟悉R2dbcCustomConversions源码的情况下忽略了static静态块的资源预热,导致报错

org.springframework.dao.InvalidDataAccessApiUsageException: Nested entities are not supported

缺少基本的类型处理。

②基本类型的转换器错误

@EnableR2dbcRepositories(basePackages = "com.yupi.yunpicturebackend.repositories")//开启R2DBC的仓库功能
@Configuration
public class R2DBCConfig {@Beanpublic R2dbcCustomConversions customConversions() {List<Object> converters = new ArrayList();converters.add(new JsonArrayToJson());converters.add(new JsonToJsonArray());return R2dbcCustomConversions.of(MySqlDialect.INSTANCE,converters);}
}

当时这段代码的主体都是粘过来的,乍一看没有毛病,一运行就报错。

仍旧是缺少类型处理的提示,此时我还以为已经将PostgreSQL的驱动给补上了,于是开始查看源代码。

是的源代码对于第一个对象取走了一个东西,英语底子不好,于是我继续追踪

 它拿走了一个SimpleTypeHolder对象,此时我还没有意识到它是做什么的,于是开始查看R2dbcCustomConversions构造函数的第二个参数以及storeConverters。其实这里就可以猜出这里的问题是用错了转化的支持对象,当然后面的探索更加坚定了我的想法。

再往下深入就知道, R2dbcCustomConversions是根据第一个参数获取提前定义好的适配器转化器,因此如果这里使用的是postgresql应该写入postgresql的支持,而不是MySQL的。后面传递的Collection对象converters便是我们的自定义转化器。

当我们将这些都实现以后,R2dbcCustomConversions便会(本人感觉)根据支持的类型转换生成一个Set<Class<?>>用来进行数据加载时判断是否为系统支持的类型,如果支持再通过重载调用我们定义的converter转化器,因为不是这个项目的重点,因此也不再深究。

不过这种类型代理的思想非常值得学习,这也为我之前制作的协议适配器带来一定的影响,当时没有这种代理的思想,很多的协议报文解析都是按照字节长度逐步拆分,再进行基本类型转换的,按照这种思想我可以重新设计,如设计出协议头类型适配器,协议尾部检验方法适配器,协议主体适配器,以及重新设计,可按照数据库存储的数据进行动态处理的“基本类型”适配器,这样差不多直接把项目重新写一遍了,本篇的目的便是总结经验,方便我学习完鱼图图项目后重新改良协议适配器做的总结。

为什么说R2dbcCustomConversions会根据支持的类型转换生成一个Set<Class<?>>

当然这也是本人的一个猜测,它更深层次的底层我是没有能力和时间进行钻研的,我们来看一下这种写法。

@ReadingConverter
public class JsonToJsonArrayimplements Converter<Json, JSONArray> {@Overridepublic JSONArray convert(Json source) {return JSONUtil.parseArray(source.asString());}
}
@EnableR2dbcRepositories(basePackages = "com.yupi.yunpicturebackend.repositories")//开启R2DBC的仓库功能
@Configuration
public class R2DBCConfig {@Beanpublic R2dbcCustomConversions customConversions() {List<Object> converters = new ArrayList();converters.add((Converter<Json, JSONArray>) source -> JSONUtil.parseArray(source.toString()));converters.add(new JsonArrayToJson());converters.add(new JsonToJsonArray());return R2dbcCustomConversions.of(PostgresDialect.INSTANCE,converters);}
}

我们可以看到JsonToJsonArray对象完全可以使用lambda表达式来进行代替,那我们注释掉JsonArrayToJson跑一下程序。

通过对比可以发现,spring底层确实尝试获取 converters 字段的类型,并且当我们切换成new对象的版本也成功进行了数据的保存。

当然以上只是本人通过实践研究出来的一种解决思路,本人能力有限可能有一些底层的实现并不能很好的发现,如果你有什么新的看法,也欢迎进行评论指出问题,感谢您的合作

相关文章:

记录一次关于spring映射postgresql的jsonb类型的转化器事故,并使用hutool的JSONArray完成映射

事件的起因是这样的&#xff0c;那次事故发生的起因是因为WebFlux和postgreSQL去重新做鱼皮的鱼图图项目&#xff08;鱼图图作业&#xff09;。 在做到picture表的时候&#xff0c;发现postgreSQL中有个jsonb的类型可以更好的支持json数组。 出于锻炼新技术的目的&#xff0c;…...

基于 HTML5 Canvas 制作一个精美的 2048 小游戏--day2

为了使 2048 游戏的设计更加美观和用户友好&#xff0c;我们可以进行以下几项优化&#xff1a; 改善颜色方案&#xff1a;使用更温馨的颜色组合。添加动画效果&#xff1a;为方块的移动和合并添加渐变效果。优化分数显示&#xff1a;在分数增加时使用动画效果。 以下是改进后…...

Django框架:python web开发

1.环境搭建&#xff1a; &#xff08;a&#xff09;开发环境&#xff1a;pycharm &#xff08;b&#xff09;虚拟环境&#xff08;可有可无&#xff0c;优点&#xff1a;使用虚拟环境可以把使用的包自动生成一个文件&#xff0c;其他人需要使用时可以直接选择导入包&#xff…...

MySQL、HBase、ES的特点和区别

MySQL&#xff1a;关系型数据库&#xff0c;主要面向OLTP&#xff0c;支持事务&#xff0c;支持二级索引&#xff0c;支持sql&#xff0c;支持主从、Group Replication架构模型&#xff08;本文全部以Innodb为例&#xff0c;不涉及别的存储引擎&#xff09;。 HBase&#xff1…...

联发科MTK6762/MT6762安卓核心板_4G智能模块应用

MT6762安卓核心板是一款工业级高性能、可运行 android9.0 操作系统的 4G智能模块。MT6762平台打造具备 AI 体验、先进双摄像头拍摄效果且具备丰富连接功能的智能手机主板。 MT6762安卓核心板 是一款髙性能低功耗的 4G 全网通安卓智能模块。此模块支持 2G/3G/4G 移动&#xff0c…...

Windows7系统下载安装Source Code Pro字库

Source Code Pro字库介绍 Source Code Pro是由Adobe推出的一款专为代码展示和编写设计的开源等宽字体‌。它不仅在编程社区中广受好评&#xff0c;还被广泛应用于各种编辑器环境中&#xff0c;以提升代码的可读性和编程体验‌。 Source Code Pro的设计充分考虑了编程符号的呈…...

Navicat 17 功能简介 | 商业智能 BI

Navicat 17 功能简介 | 商业智能BI 随着 17 版本的发布&#xff0c;Navicat 也带来了众多的新特性&#xff0c;包括兼容更多数据库、全新的模型设计、可视化智能 BI、智能数据分析、可视化查询解释、高质量数据字典、增强用户体验、扩展 MongoDB 功能、轻松固定查询结果、便捷U…...

C# winodw TableLayoutPanel 料盒生产状态UI自动生成

料盒生产状态UI自动生成&#xff0c;效果如下 以前公司项目的这些都是手动拖控件做的。每个设备的料盒数量不一样&#xff0c;层数不一样时都要发好几个小时去改相关细节和代码。上次改了一次。这个又来了。上次就有想法做成根据参数自动生成。但项目时间有限有没有去深入思路和…...

提示词的艺术----AI Prompt撰写指南(个人用)

提示词的艺术 写在前面 制定提示词就像是和朋友聊天一样&#xff0c;要求我们能够清楚地表达问题。通过这个过程&#xff0c;一方面要不断练习提高自己地表达能力&#xff0c;另一方面还要锻炼自己使用更准确精炼的语言提出问题的能力。 什么样的提示词有用&#xff1f; 有…...

哪些前端打印插件可以实现监听用户选择了打印还是取消

在前端实现监听用户是否选择了打印还是取消的功能&#xff0c;确实是一个挑战&#xff0c;因为浏览器的打印行为是通过原生对话框处理的&#xff0c;而这些对话框的行为无法直接被 JavaScript 控制或监听。不过&#xff0c;有一些插件和方法可以帮助你更接近这个目标&#xff1…...

【PyCharm】连接Jupyter Notebook

【PyCharm】相关链接 【PyCharm】连接 Git【PyCharm】连接Jupyter Notebook【PyCharm】快捷键使用【PyCharm】远程连接Linux服务器【PyCharm】设置为中文界面 【PyCharm】连接Jupyter Notebook PyCharm连接Jupyter Notebook的过程可以根据不同的需求分为 本地连接 和 远程连…...

【Linux系统编程】—— 深入理解Linux中的环境变量与程序地址空间

文章目录 环境变量常见的环境变量查看环境变量环境变量的修改与使用环境变量的组织⽅式环境变量的命令通过代码如何获取环境变量环境变量的继承 前言&#xff1a;在Linux系统中&#xff0c;环境变量和程序地址空间是系统管理和进程运行的重要组成部分。本文将详细探讨环境变量的…...

Spark常见面试题-部分待更新

1. 简述hadoop 和 spark 的不同点&#xff08;为什么spark更快&#xff09; Hadoop是一个分布式管理、存储、计算的生态系统&#xff0c;包括HDFS&#xff08;分布式文件系统&#xff09;、MapReduce&#xff08;计算引擎&#xff09;和YARN&#xff08;资源调度器&#xff09;…...

Android BitmapShader实现狙击瞄具十字交叉线准星,Kotlin

Android BitmapShader实现狙击瞄具十字交叉线准星&#xff0c;Kotlin <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.…...

linux通过web向mac远程传输字符串,mac收到后在终端中直接打印。

要通过Web从Linux向Mac远程传输字符串&#xff0c;并在Mac的终端中直接打印&#xff0c;可以使用以下方法。这里假设Linux作为服务器&#xff0c;Mac作为客户端。 方法 1&#xff1a;使用Python的HTTP服务器 在Linux上启动一个简单的HTTP服务器&#xff0c;Mac通过curl获取字符…...

海云安开发者安全智能助手D10荣膺 “ AI标杆产品 ” 称号,首席科学家齐大伟博士入选2024年度 “ 十大杰出青年 ”

2024年12月27日&#xff0c;粤港澳大湾区AI领袖峰会在深圳成功举办&#xff0c;大会表彰了在人工智能技术创新、应用实践和产业发展等方面取得优异成绩的企业和个人&#xff0c;深圳海云安网络安全技术有限公司开发者安全智能助手D10荣膺“AI标杆产品”称号。同时&#xff0c;公…...

Spring Boot + Apache POI 实现 Excel 导出:BOM物料清单生成器(支持中文文件名、样式美化、数据合并)

目录 引言 Apache POI操作Excel的实用技巧 1.合并单元格操作 2.设置单元格样式 1. 创建样式对象 2. 设置边框 3. 设置底色 4. 设置对齐方式 5. 设置字体样式 6.设置自动换行 7. 应用样式到单元格 3. 定位和操作指定单元格 4.实现标签-值的形式 5.列宽设置 1. 设…...

ReactiveSwift 简单使用

记录 ReactiveSwift 简单使用 导入 ReactiveSwift 库创建 TestViewModel 文件 enum JKTypeType: Int {case cloudcase devicecase weater }// 通过监听属性变化 class TestViewModel: NSObject {lazy var recordType: Property<JKTypeType> {return Property(recordTy…...

CSS 的基础知识及应用

前言 CSS&#xff08;层叠样式表&#xff09;是网页设计和开发中不可或缺的一部分。它用于描述网页的视觉表现&#xff0c;使页面不仅实现功能&#xff0c;还能提供吸引人的用户体验。本文将介绍 CSS 的基本概念、语法、选择器及其在提升网页美观性方面的重要性。 什么是 CSS&…...

【Web】2025西湖论剑·中国杭州网络安全安全技能大赛题解(全)

目录 Rank-l Rank-U sqli or not Rank-l username存在报错回显&#xff0c;发现可以打SSTI 本地起一个服务&#xff0c;折半查找fuzz黑名单&#xff0c;不断扔给fenjing去迭代改payload from flask import Flask, request, render_template_stringapp Flask(__name__)app…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...

麒麟系统使用-进行.NET开发

文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的&#xff0c;如果需要进行.NET开发&#xff0c;则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET&#xff0c;所以要进…...