Jhipster实战中遇到的知识点-开发记录

利用Jhipster开发的网站天赋吉星终于上线啦,本文介绍了在开发过程中遇到的各种小的知识点和技巧,绝对干货,供你参考。大家可以直接点击天赋吉星,看到网站效果。

首先介绍一下项目技术选型,JHipster 版本:8.1.0, 项目类型:monolith. 前端采用react+typescript+redux, 后端采用webflux, 数据库Postgresql
本项目采用circle CI + Helm实现部署流程。算命功能采用的是chatgpt 大模型。

以下是记录的知识点,是一边开发一边记录的,所以有些凌乱,请大家谅解: 大家可以收藏起来,以备开发时遇到相同问题时过来看看。另外本篇文章会持续更新,记录开发中遇到的普遍问题。

Jhipster 如何运行端到端(e2e)测试

npm run e2e

  • 创建实体
    https://start.jhipster.tech/
  • 在当前路径下打开idea
 idea .
  • jdl生成代码
    jhipster jdl blog.jdl

  • 如果你不想生成模拟数据,从application-dev.yml中移出faker

  • 热部署, 改写了java类后,可以用idea 的build编译此类,避免重启整个应用。

在这里插入图片描述

  • 当前系统登录的用户名
    #{authentication.name}
  • 前端允许html效果显示
[innerHTML]="post.content"> - jhipster的组件
  • 部署到Heroku
jhipster heroku
heroku open
heroku logs --tail
  • 可以用idea命令来比较2个文件
 idea diff a.file b.file

升级全局的jhipster版本

npm uninstall -g generator-jhipster
npm install -g generator-jhipster@8.1.0

jdl 如何利用已经存在的jhi_user表, 例如系统的用户表中需要增加一些字段? jhipster官方不建议直接修改user 实体,而是通过组合或集成两种方式。
比如: relationship OneToMany {
MyUser to User with builtInEntity
}
参考:https://www.jhipster.tech/user-entity/
@Transient 注解的 internalUser 属性表示它不会持久化到数据库中,这个属性用来存放与 User 实体的关联,但实际并不在数据库中创建这个字段。

定义jdl时,注意避免用Service,Order这些做实体名称。

实用技巧 https://www.jhipster.tech/tips/

  • 如何设置登录成功后,跳转到指定页面?
    在login.tsx中,修改认证成功后的跳转路径,如下图
    在这里插入图片描述
    jdl语法
  • 双向1对1
entity A
entity B

relationship OneToOne {
  A{a} to B{b}
}

表示关联关系从A 到 B, 导航关系是双向:可以从A导航B,也可以从B导航到A。B会通过a字段对访问A,A会通过b字段访问B.

  • 双向1对多
entity Owner
entity Car

relationship OneToMany {
  Owner{car} to Car{owner}
}

从car对象中可以找到他的owner,从owner对象可以找到他有多少car.

  • 单向多对1
entity Owner
entity Car
relationship ManyToOne {
  Car{owner} to Owner
}

jhipster文档说,这种情况下,你不能从ower中添加或移除car.

  • 单向1对多
    这种情况,目前jhipster不支持。它给了2个方案
  • 用双向1对多代替(推荐)
  • 在生成的代码中,Remove the “mappedBy” attribute on your @OneToMany annotation,Generate the required join table: you can do a mvn liquibase:diff to generate that table
  • 两个1对多 person可以有多个car,也可以驾驶多个car
entity Person
entity Car

relationship OneToMany {
  Person{ownedCar} to Car{owner}
}

relationship OneToMany {
  Person{drivenCar} to Car{driver}
}

entity Citizen
entity Passport
relationship OneToOne {
  Citizen{passport} to @Id Passport
}

@Id 表示Passport的主键是Citizen的外键,它们共享主键

@Entity
public class Citizen {
    @Id
    private Long id;

    @OneToOne
    @MapsId
    private Passport passport;
}

@Entity
public class Passport {
    @Id
    private Long id;

    @OneToOne(mappedBy = "passport")
    private Citizen citizen;
}

抓取数据的策略:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
在webflux体系中,采用r2dbc来操作数据库。在R2DBC中,没有自动的级联保存、更新或删除功能

  • 如果有多个jdl, jhipster jdl my_file1.jdl my_file2.jdl
  • 如果你在jdl对已经创建好了的entity进行新增,修改,删除字段等操作,你需要使用以下命令来告诉liquibase新增log,而不是在原来的log上进行修改 jhipster jdl ./jdl/business.jdl --incremental-changelog

**如果你之前不知道上面的知识点,在jdl修改了字段后,仍然执行了jhipster jdl my_file1 这样的命令。**那么此时,liquibase应该会在原来的changelog文件基础上修改,启动系统时,会报check sum 错误,类似如下。

liquibase.exception.CommandExecutionException: liquibase.exception.ValidationFailedException: Validation Failed:
     3 changesets check sum
          config/liquibase/changelog/20240103135710_added_entity_Profile.xml::20240103135710-1::jhipster was: 9:91b5c4f73fbdf83cf6fb005cd45b356a but is now: 9:ae96db2ae1ff65c4d43b795f541e7399
          config/liquibase/changelog/20240103135710_added_entity_Profile.xml::20240103135710-1-data::jhipster was: 9:943b13b81ce107f44d593e4eb8117b9d but is now: 9:2ad33bba71a8dd0116f82b702e2e22a9
          config/liquibase/changelog/20240103135710_added_entity_constraints_Profile.xml::20240103135710-2::jhipster was: 9:60ce48c5bc8eb1829c4ddf65292ee11e but is now: 9:d9472654721100224162c2ee24ac4c6d

此时,需要细心修改一下changelog文件,把他回退到错误执行执行了jhipster jdl my_file1 之前的changelog,保证数据库记录的文件和它是一个文件,checksum才不会出错。如果,你确实认为已经回到了之前的文件,但是还是报checksum错误,此时你可以按照checksum的错误提示,强行修改数据库中checksum的值,保持一致就可。

然后你再执行hipster jdl ./jdl/business.jdl --incremental-changelog 让它回到正确轨道。
此时,有可能会需要你手动在master.xml中体现liquibase新的变化,你手动把增的changelog添加进master.xml就行,添加时注意如图所示的顺序问题。Good luck!
在这里插入图片描述

  • 别的选项:命令行形式修改entity.
    jhipster import-jdl myapp.jdl --json-only 使用 --json-only 选项允许开发者在生成实体代码之前审查和确认 JSON 配置文件。
    jhipster entity MyChangedEntity1 --single-entity
    jhipster entity MyChangedEntity2 --single-entity

在开发过程中,可能会在新增一些字段后,启动应用后报错:

liquibase.exception.DatabaseException: org.postgresql.util.PSQLException: ERROR: column "product_start_date" of relation "profile" does not exist_  Position: 58 

虽然这些字段是在另外的changelog管理,但是liqubase仍然报找不到这些新增字段的错误,这很有可能是尝试插入fakedata时的报错,fakedata有所有这些字段的值,所以它去数据库中检查新增的字段,从而报错。临时解决办法是在application-dev.yaml中禁用fake.查找关键字=># Remove ‘faker’ if you do not want the sample data to be loaded automatically

根据权限,控制菜单的显示,参考如下menu.tsx的修改,只有admin权限的用户才可以看到全部的实体
在这里插入图片描述

  • 如何解决SSE(Server Sent Event) 不能在head中携带验证信息的问题
    问题描述:
    前端EventSoure不支持在head中携带Authorization信息,导致后台在用户已经登录系统的情况下,无法识别是哪个用户发出的sse请求,从而无法实现权限控制。
    简单解决方案:(还可以通过cookie解决,有点麻烦)
    1. 前端发出sse请求的时候,从本地拿到已登录用户的jwt token,把它放到url参数中传给后端
const token = Storage.local.get('jhi-authenticationToken') || Storage.session.get('jhi-authenticationToken');
        if (!token) {
            toast.error('请先登录');
            return;
        }
      let api_question = `/api/chat?question=${question}&threadId=${threadIdInLocal}&token=${token}`;
      const eventSource = new EventSource(api_question);
 2.后台增加一个过滤器,将从url得到的token,添加到head中,这样可以直接沿用系统原来的验证机制,实现最小改动
@Component
public class SSEValidateFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if(token!=null){
            exchange.getRequest().mutate().header(HttpHeaders.AUTHORIZATION, "Bearer " + token).build();
        }
        return chain.filter(exchange);
    }
}
  1. 在SecurityConfiguration中配置此过滤器
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
            .securityMatcher(
                new NegatedServerWebExchangeMatcher(
                    new OrServerWebExchangeMatcher(pathMatchers("/app/**", "/i18n/**", "/content/**", "/swagger-ui/**"))
                )
            )
            .cors(withDefaults())
            .csrf(csrf -> csrf.disable())
            .addFilterAfter(new SpaWebFilter(), SecurityWebFiltersOrder.HTTPS_REDIRECT)
            .addFilterBefore(sseValidateFilter, SecurityWebFiltersOrder.AUTHENTICATION)
  • webflux排错,以下代码,如果处理流的过程中有错,通常不会在控制台打印错误,给排错带来困难。
canUseProduct.flatMapMany(canUse -> {
            if (!canUse) {
                return Flux.just(ServerSentEvent.<String>builder().event("noPermission").data("Please purchase usage time!").build());
            } else {
                return assistantService.chatWithJiXing(question, threadId);
            }
        })

为了排错,可以加上doOnError,让控制台打印错误

canUseProduct.flatMapMany(canUse -> {
            if (!canUse) {
                return Flux.just(ServerSentEvent.<String>builder().event("noPermission").data("Please purchase usage time!").build());
            } else {
                return assistantService.chatWithJiXing(question, threadId);
            }
        })
        .doOnError(error -> log.error("Error processing canUseProduct flux: ", error));

在与stripe进行集成时,页面报错
在这里插入图片描述
此时排错过程如下:
a. 打开控制台,直接点击401报错的链接
在这里插入图片描述
b. 点击得到页面,可以看到是publickey错误
在这里插入图片描述

  • 需求:让order里面的user能够导航到user detail页面
    实际情况: jhipster默认生成的订单列表页没有导航到user detail页面的能力,如下图:
    在这里插入图片描述
    原因:order实体是通过user id 关联到 user的,但是目前访问user 是通过login访问的,`/admin/user-management/{login},这大概是没有自动生成导航的原因吧。
    实现: 因为Flux findAllBy(Pageable pageable); 这段代码实际上已经把所有关联的数据都从数据库捞出来了,只要修改映射类就可以,如下图:
    在这里插入图片描述
    前端代码如下:
<td>{sysOrder.user ?<Link to={`/admin/user-management/${sysOrder.user.login}`}>{sysOrder.user.login} </Link>: ''}</td>
  • 实体profile和jhi_user 用的同一个主键,建表语句如下。
    需求:需要在创建user的时候,随后insert一条profile.
create table public.profile (
  id bigint primary key not null,
 ....
  foreign key (id) references public.jhi_user (id)
 ....
);

错误做法:给profile设置id, 这会导致系统认为是update 数据,从而报找不到这个id的错误,保存失败。

flatMap(savedUser ->{
                Profile profile =  new Profile();
                profile.setId(savedUser.getId());
                profile.setInternalUser(savedUser);
                return profileRepository.save(profile).thenReturn(savedUser);
            })

正确做法:profile不需要setId

flatMap(savedUser ->{
                Profile profile =  new Profile();
                profile.setInternalUser(savedUser);
                return profileRepository.save(profile).thenReturn(savedUser);
            })

最后注意,update user也会调用save user 代码,所以创建profile之前先要检查一下porfile是否存在,如下:

flatMap(savedUser ->
                profileRepository
                    .findById(savedUser.getId())
                    .flatMap(existedProfile -> Mono.just(savedUser))
                    .switchIfEmpty(
                        Mono.defer(() -> {
                            Profile profile = new Profile();
                            profile.setInternalUser(savedUser);
                            return profileRepository.save(profile).thenReturn(savedUser);
                        })
                    )
            );

seo优化之title,description,keywords

JHipster是支持多语言的,但是多语言对于seo的支持几乎没有。以下介绍首页home.tsx中是如何多语言以利于搜索引擎搜索的。
目前天赋吉星支持5种语言。

  1. 在index.html种加入相应的动态元数据标签
<title id="dynamic-title">天赋吉星</title>
    <meta id="dynamic-description" name="description" content="天赋吉星:在线算命,解梦,看风水,帮您求神拜佛。" />
    <meta
      id="dynamic-keywords"
      name="keywords"
      content="fortune telling, dream interpretation, feng shui, praying, 算命, 解梦, 看风水, 求神拜佛"
    />
  1. home.tsx,这里面的关键是要在useEffect种对currentLocale进行监控,因为语言是异步加载的。刚开始挂载页面的时候,语言很有可能没有加载好,导致null错误。
const currentLocale = useAppSelector(state => state.locale.currentLocale);
useEffect(() => {
    const title = translate('home.seo.title');
    const description = translate('home.seo.description');
    const keywords = translate('home.seo.keywords');

    document.title = title;
    const descriptionMetaTag = document.getElementById('dynamic-description');
    const keywordsMetaTag = document.getElementById('dynamic-keywords');
    if (descriptionMetaTag) {
      descriptionMetaTag.setAttribute('content', description);
    }
    if (keywordsMetaTag) {
      keywordsMetaTag.setAttribute('content', keywords);
    }
  }, [currentLocale]);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/783596.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

谷粒商城学习笔记-逆向工程错误记录

文章目录 1&#xff0c;Since Maven 3.8.1 http repositories are blocked.1.1 在maven的settings.xml文件中&#xff0c;新增如下配置&#xff1a;1.2&#xff0c;执行clean命令刷新maven配置 2&#xff0c;internal java compiler error3&#xff0c;启动逆向工程报错&#x…

Unity分享一个简单的3D角色漫游脚本

1.新建一个场景&#xff0c;并创建一脚本 2.给场景中的地面添加一个Ground标签 3.给刚刚新建的脚本编写代码 using UnityEngine;public class PlayerMovement : MonoBehaviour {public float moveSpeed 5f; // 移动速度public float jumpForce 5f; // 跳跃力量public float …

家里老人能操作的电视直播软件,目前能用的免费看直播的电视软件app,适合电视和手机使用!

2024年许多能看电视直播的软件都不能用了&#xff0c;家里的老人也不会手机投屏&#xff0c;平时什么娱乐都没有了&#xff0c;这真的太不方便了。 很多老人并不喜欢去买一个广电的机顶盒&#xff0c;或者花钱拉有线电视。 现在的电视大多数都是智能电视&#xff0c;所以许多电…

记录在Windows上安装Docker

在Windows上安装Docker时&#xff0c;可以选择使用不同的后端。 其中两个常见的选择是&#xff1a;WSL 2&#xff08;Windows Subsystem for Linux 2&#xff09;和 Hyper-V 后端。此外&#xff0c;还可以选择使用Windows容器。 三者的区别了解即可&#xff0c;推荐用WSL 2&…

驾校管理系统-计算机毕业设计源码49777

驾校管理系统 摘 要 驾校管理系统是一个基于Spring Boot框架开发的系统&#xff0c;旨在帮助驾校提高管理效率和服务水平。该系统主要实现了用户管理、年月类型管理、区域信息管理、驾校信息管理、车辆信息管理、报名信息管理、缴费信息管理、财务信息管理、教练分配管理、更换…

数字签密:信息安全的新防线

随着互联网的普及和数字技术的飞速发展&#xff0c;信息安全问题日益凸显。在这个背景下&#xff0c;数字签密技术应运而生&#xff0c;为保护信息安全提供了新的解决方案。本文将介绍数字签密的概念、原理及应用&#xff0c;探讨其在信息安全领域的重要性。 数字签密的概念 …

智慧矿山:EasyCVR助力矿井视频多业务融合及视频转发服务建设

一、方案背景 随着矿井安全生产要求的不断提高&#xff0c;视频监控、数据传输、通讯联络等业务的需求日益增长。为满足矿井生产管理的多元化需求&#xff0c;提高矿井作业的安全性和效率&#xff0c;TSINGSEE青犀EasyCVR视频汇聚/安防监控综合管理平台&#xff0c;旨在构建一…

Spring学习05-[AOP学习-AOP原理和事务]

AOP原理和事务 AOPAOP底层原理比如下面的代码案例手动模拟AOP 动态代理详解JDK动态代理具体实现 Cglib动态代理具体实现 jdk动态代理和cglib动态代理的区别 事务 AOP AOP底层原理 当实现了AOP,Spring会根据当前的bean创建动态代理(运行时生成一个代理类) 面试题&#xff1a;为…

JAVA之(static关键字、final关键字)

JAVA之&#xff08;static关键字、final关键字&#xff09; 一、 static关键字1、静态变量2、静态方法3、 静态代码块4、例子 二、final关键字1、final修饰类2、 final修饰方法3、修饰变量 一、 static关键字 1、静态变量 private static String str1“staticProperty”2、静…

适合中小企业的MES管理系统有哪些特点

在当今竞争激烈的商业环境中&#xff0c;中小企业对于高效、灵活的生产管理系统的需求日益凸显。面对这些企业的MES管理系统不仅成为监控生产过程的得力助手&#xff0c;还通过提供关键数据&#xff0c;构建起客户期望与制造车间实时订单状态之间的紧密桥梁&#xff0c;以下是对…

Vue3使用markdown编辑器之Bytemd

官网地址&#xff1a;https://bytemd.js.org/playground GitHub地址&#xff1a;https://github.com/bytedance/bytemd ByteMD 是字节跳动出品的富文本编辑器&#xff0c;功能强大&#xff0c;可以免费使用&#xff0c;而且支持很多掘金内置的主题&#xff0c;写作体验很棒。 …

【Unity2D 2022:Particle System】添加拾取粒子特效

一、创建粒子特效游戏物体 二、修改粒子系统属性 1. 基础属性 &#xff08;1&#xff09;修改发射粒子持续时间&#xff08;Duration&#xff09;为3s &#xff08;2&#xff09;取消勾选循环&#xff08;Looping&#xff09; &#xff08;2&#xff09;修改粒子存在时间&…

星网安全产品线成立 引领卫星互联网解决方案创新

2024年6月12日&#xff0c;盛邦安全&#xff08;688651&#xff09;成立星网安全产品线&#xff0c;这是公司宣布全面进入以场景化安全、网络空间地图和卫星互联网安全三大核心能力驱动的战略2.0时代业务落地的重要举措。 卫星互联网技术的快速发展&#xff0c;正将其塑造为全球…

leetcode:编程基础0到1

文章目录 交替合并字符串str.length();StringBuilder类型 ,toString()append() &#xff0c;chatAt()题目描述 交替合并字符串 str.length(); 输出字符串str的长度 StringBuilder类型 ,toString() append() &#xff0c;chatAt() 题目描述 class Solution {public String …

(译文)IRIG-B对时编码快速入门

原文 PDF&#xff1a;https://ww1.microchip.com/downloads/aemDocuments/documents/FTD/tekron/tekronwhitepapers/221223-A-guide-to-IRIG-B.pdf IRIG-B3 概论 Inter-Range Instrument Group 时间码&#xff08;简称IRIG&#xff09;是一系列标准时间码格式。用于将时间信…

俄罗斯VK Ads开户充值全流程!VK如何开户?VK如何注册?VK广告

在俄罗斯&#xff0c;VK&#xff08;VKontakte&#xff09;是一个广受欢迎的社交媒体平台&#xff0c;对于寻求进入该市场的企业来说&#xff0c;进行VK广告推广是一条有效途径。 首先&#xff0c;你需要明确自己要推广的产品或服务&#xff0c;并且确定目标市场和受众。 由于…

1.8.0-矩阵乘法的反向传播-简单推导

1相关资料 之前分享过一个博客里面写的&#xff0c;我们大致了解并记住结论的博客&#xff1a;【深度学习】7-矩阵乘法运算的反向传播求梯度_矩阵梯度公式-CSDN博客&#xff1b;这里再分享一下自然语言处理书上关于这部分的推导过程&#xff1a;3-矩阵相乘-梯度反向传播的计算…

如何下载jmeter旧版本

如何下载jmeter旧版本 推荐先用旧版本做好测试基本操作&#xff0c;因为高版本不适合做压力测试&#xff0c;需要证书&#xff0c;有点麻烦。 1.百度或直接打开jmeter官网&#xff1a;https://jmeter.apache.org/ 2.向下拖到Archives一栏&#xff0c;点击Apache Jmeter archi…

ICC2:ignore pin的设置

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 相关文章链接:

OS Copilot测评

1.按照第一步管理重置密码时报错了,搞不懂为啥?本来应该跳转到给的那个实例的,我的没跳过去 2.下一步重置密码的很丝滑没问题 3安全组新增入库22没问题 很方便清晰 4.AccessKey 还能进行预警提示 5.远程连接,网速还是很快,一点没卡,下载很棒 6.替换的时候我没有替换<>括…