lvxianchao / notes Goto Github PK
View Code? Open in Web Editor NEW狗屁不通瞎写的博客
Home Page: https://coderlxc.com
狗屁不通瞎写的博客
Home Page: https://coderlxc.com
Jenkins 使用流水线来构建任务时,从 Gitlab 拉取代码失败,报错如标题,原因是因为 Gitlab 上没有 master
分支,已经变成了 main
分支,把脚本里的分支名称修改过来即可。
为了方便,直接使用 SDKMAN 安装。
curl -s "https://get.sdkman.io" | bash
完事儿以后,把这句加入到 .bashrc
中:
source "$HOME/.sdkman/bin/sdkman-init.sh"
我这里直接安装微软的 JDK 17,执行此命令查找相关版本:
sdk ls java | grep ms | grep 17
显示如下:
Microsoft | >>> | 17.0.7 | ms | installed | 17.0.7-ms
| | 17.0.6 | ms | | 17.0.6-ms
| | 17.0.5 | ms | | 17.0.5-ms
| | 11.0.17 | ms | | 11.0.17-ms
这个 17.0.7-ms
就是版本号,可以用于 SDKMAN 安装:
sdk install java 17.0.7-ms
安装完成以后,SDKMAN 会自动把这个版本设置为默认版本。
sdk install gradle
最近在接触一个前后端分离项目时,涉及到 XSS 安全问题,采用了 Laravel Purifier 这个扩展包。
我个人的思路是写了一个 XSS 的中间件,在这个中间件里统一对所有参数进行过滤处理。
实际上,我这是一种错误的用法,如果条件允许的话不应该这么做,应该只对富文本字段进行过滤。
上到测试环境以后,发现了一个偶发的 BUG,会提示 mkdir(): File exists
错误。排查了一翻下来,锁定到确实是这个扩展包导致的。
扩展包在 Laravel 项目里,storage/app
目录下会创建一个名为 purifier
文件夹,其下会包含三个文件夹(目录我还没有发现有别的文件夹)用来存储扩展包的缓存文件,分别是:CSS
、HTML
、URI
,。
在项目容器重新建立以后,访问一个面板的页面会同时有多个 Ajax 请求到后台,而后台因为全局用了这个中间件来过滤参数,所以就会导致同时有多个请求去检查 purifier
文件夹是否存在。如果缓存文件夹不存在的话就需要创建,这时候,假设有 A
和 B
两个请求同时到达后台,也就同时得到结果:缓存文件夹不存在
,则他们就都去创建,再假设 A
请求先创建完了缓存文件夹,这时候 B
请求还想创建缓存文件夹,也就造成了 mkdir(): File exists
错误。
既然已经知道了(虽然是推测的)问题产生的原因,那么解决起来就比较容易了。
我的思路是把所有的缓存文件夹都事先创建好,并纳入版本控制,这样就不存在再去创建缓存文件夹的需要了,也就解决了重复创建的问题。
在 storage/app/.gitignore
中加入 !purifier/
。
创建 storage/app/purifier
文件夹。
创建 storage/app/purifier/.gitignore
文件,并在其中写入以下内容:
*
!CSS/
!HTML/
!URI/
!.gitignore
在 storage/app/purifier
文件夹中创建 CSS
、HTML
、URI
文件夹。
分别在刚刚创建的 CSS
、HTML
、URI
文件夹中创建 .gitignore
文件,并在其中写入以下内容:
*
!.gitignore
最后,为了方便下次使用,我将这份文件夹上传到了 Github 上,使用的时候直接下载下来放到 storage/app
目录下就可以了。
sudo add-apt-repository ppa:git-core/ppa
sudo apt update
sudo apt upgrade
系统管理 -> 系统配置
添加全局变量
MAVEN_HOME
/data/apache-maven-3.3.9(你的maven的解压路径)
PATH
$(PATH):$(MAVEN_HOME)/bin
在 after.sh
里加入以下:
sudo sed -i 's/try_files $uri $uri\/ \/index.php?$query_string;/autoindex on;\n\tautoindex_exact_size off;\n\tautoindex_localtime on;\n\tcharset utf-8;/g' /etc/nginx/sites-available/files.test
sudo nginx -s reload
docker run --name nginx --restart always -p 80:80 -v /root/satis/build:/usr/share/nginx/html:ro -v /etc/nginx/conf.d/:/etc/nginx/conf.d/ -d nginx
https://github.com/composer/satis
https://learnku.com/articles/14856/composersatis-speed-up-composer-private-active
# 拷贝nginx 镜像中的 默认的 nginx 部分配置文件
docker run --name tmp-nginx-container -d nginx
docker cp tmp-nginx-container:/etc/nginx/conf.d/ /etc/nginx/conf.d/
docker rm -f tmp-nginx-container
npm config set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Author: lvxianchao
* Date: 2023-03-03 14:32
* Description: Redis Config
*/
@Configuration
public class RedisConfig {
@Bean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
// 连接池
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 序列化器
redisTemplate.setDefaultSerializer(new StringRedisSerializer());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
// 支持事务
redisTemplate.setEnableTransactionSupport(true);
return redisTemplate;
}
/**
* Redis Key 过期监听
*
* @param redisConnectionFactory RedisConnectionFactory
*
* @return RedisMessageListenerContainer
*/
@Bean
RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
return container;
}
}
class
时的顺序class
;package
是否存在这个 class
;import
的包是否包含这个 class
;java.lang
包是否包含这个 class
;如果按照上面的规则还无法确定类名,则编译报错。
在 CentOS 7.6 上,使用 Laravel 扩展包 SimpleSoftwareIO/simple-qrcode 时需要 imagick
PHP 扩展,直接使用 pecl install imagick
安装的时候遇到错误提示:Please provide a path to MagickWand-config or Wand-config program.
打开 Windows PowerShell 输入以下命令:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
遇到以下选择项选择 A
:
Execution Policy Change
The execution policy helps protect you from scripts that you do not trust. Changing the execution policy might expose
you to the security risks described in the about_Execution_Policies help topic at
https:/go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the execution policy?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): A
继续执行 irm get.scoop.sh | iex
得到以下:
Initializing...
Downloading...
Extracting...
Creating shim...
Adding ~\scoop\shims to your path.
Scoop was installed successfully!
Type 'scoop help' for instructions.
执行 scoop -v
查看版本:
Current Scoop version:
v0.2.3 - Released at 2022-07-07
使用 南京大学镜像 加速,提供有 main
、extras
、nonportable
、versions
4 个镜像仓库。
首先执行 scoop bucket rm main
来删除 main
仓库,不然执行以下命令时,会提示:
WARN The 'main' bucket already exists. To add this bucket again, first remove it by running 'scoop bucket rm main'.
添加镜像源:
# main
scoop bucket add main https://mirror.nju.edu.cn/git/scoop-main.git/
# extras
scoop bucket add main https://mirror.nju.edu.cn/git/scoop-extras.git/
# nonportable
scoop bucket add main https://mirror.nju.edu.cn/git/scoop-nonportable.git/
# versions
scoop bucket add main https://mirror.nju.edu.cn/git/scoop-versions.git/
完成后执行 scoop update
,这一步我本地处于报错状态,提示连接到 github.com 超时:
➜ ~ scoop update
Updating Scoop...
fatal: unable to access 'https://github.com/ScoopInstaller/Scoop/': Failed to connect to github.com port 443 after 21039 ms: Timed out
Update failed.
于是决定直接使用代理。
scoop config proxy [username:password]host:port
执行 scoop update
会输出以下内容:
➜ ~ scoop update
Updating Scoop...
Updating 'extras' bucket...
Updating 'main' bucket...
Updating 'nonportable' bucket...
Updating 'versions' bucket...
Scoop was updated successfully!
在 Jenkins 官网首页,点击 下载
按钮,在左侧列出的都是 LTS 版本,点击 CentOS/Fedora/Red Hat
进入:
跟随官网步骤:
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
yum install jenkins
相关文件所在位置:
# Jenkins 安装目录
/var/lib/jenkins
# Jenkins 日志目录
/var/log/jenkins
# Jenkins 缓存目录
/var/cache/jenkins
# 配置文件
/etc/sysconfig/jenkins
在 /etc/sysconfig/jenkins
配置文件中,可以调整相关配置信息,我这里只调整 JENKINS_JAVA_CMD
和 JENKINS_USER
JENKINS_JAVA_CMD="/usr/local/sdkman/candidates/java/current/bin/java"
JENKINS_USER="root"
在启动 Jenkins 启动之前先调整一下配置,让 Jenkins 安装插件变快,同时第一次启动也会变快。
编辑 /var/lib/jenkins/hudson.model.UpdateCenter.xml
把里面的 url
字段值改为**的镜像,我这里使用了腾讯的:
<?xml version='1.1' encoding='UTF-8'?>
<sites>
<site>
<id>default</id>
<url>https://mirrors.cloud.tencent.com/jenkins/updates/update-center.json</url>
</site>
</sites>
编辑完成后,执行 systemctl start jenkins
启动 Jenkins。
访问 主机:端口
便可以访问,执行 cat /var/lib/jenkins/secrets/initialAdminPassword
便可以查看密码
执行 systemctl enable jenkins
添加到开机启动。
在使用 Jenkins 配置的从节点进行构建时,遇到如下错误:
The recommended git tool is: NONE
using credential 7d1762c9-7d53-4021-84f7-9dea4c8f3306
Cloning the remote Git repository
ERROR: Error cloning remote repo 'origin'
hudson.plugins.git.GitException: Command "git fetch --tags --force --progress -- http://192.168.56.152/lagou/jenkinsdemo.git +refs/heads/*:refs/remotes/origin/*" returned status code 128:
stdout:
stderr: git: 'remote-http' is not a git command. See 'git --help'.
fatal: full write to remote helper failed: Broken pipe
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandIn(CliGitAPIImpl.java:2675)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandWithCredentials(CliGitAPIImpl.java:2099)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.access$500(CliGitAPIImpl.java:85)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl$1.execute(CliGitAPIImpl.java:619)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl$2.execute(CliGitAPIImpl.java:848)
at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler$GitCommandMasterToSlaveCallable.call(RemoteGitImpl.java:158)
at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler$GitCommandMasterToSlaveCallable.call(RemoteGitImpl.java:151)
at hudson.remoting.UserRequest.perform(UserRequest.java:211)
at hudson.remoting.UserRequest.perform(UserRequest.java:54)
at hudson.remoting.Request$2.run(Request.java:376)
at hudson.remoting.InterceptingExecutorService.lambda$wrap$0(InterceptingExecutorService.java:78)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Suppressed: hudson.remoting.Channel$CallSiteStackTrace: Remote call to jenkins-agent-154
at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1784)
at hudson.remoting.UserRequest$ExceptionResponse.retrieve(UserRequest.java:356)
at hudson.remoting.Channel.call(Channel.java:1000)
at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler.execute(RemoteGitImpl.java:143)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler.invoke(RemoteGitImpl.java:129)
at com.sun.proxy.$Proxy95.execute(Unknown Source)
at hudson.plugins.git.GitSCM.retrieveChanges(GitSCM.java:1229)
at hudson.plugins.git.GitSCM.checkout(GitSCM.java:1312)
at org.jenkinsci.plugins.workflow.steps.scm.SCMStep.checkout(SCMStep.java:129)
at org.jenkinsci.plugins.workflow.steps.scm.SCMStep$StepExecutionImpl.run(SCMStep.java:97)
at org.jenkinsci.plugins.workflow.steps.scm.SCMStep$StepExecutionImpl.run(SCMStep.java:84)
at org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution.lambda$start$0(SynchronousNonBlockingStepExecution.java:47)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
从节点的 Git 是下载的源码本地编译安装的,需要在安装 libcurl-devel
和 curl-devel
以后,重新编译安装一次。
yum install -y libcurl-devel curl-devel
/**
* 获取远程图片的 base64
*
* @param remoteImageUrl 远程图片地址
*
* @return 图片的 base64
*/
public static String getRemoteImageBase64(String remoteImageUrl) {
try {
StringBuilder builder = new StringBuilder("data:");
URL url = new URL(remoteImageUrl);
String type = url.openConnection().getContentType();
builder.append(type).append(";base64,");
BufferedImage bufferedImage = ImageIO.read(url.openStream());
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", stream);
String base64 = Base64.getEncoder().encodeToString(stream.toByteArray());
builder.append(base64);
return builder.toString();
} catch (IOException e) {
log.warn("读取远程图片信息失败: {}", e.getMessage());
return null;
}
}
npm install -g pnpm
File | Settings | Editor | File and Code Templates
后端处理的思路是配置 Jackson,在序列化时将 Long
类型和 long
类型序列化成 String
类型:
package BusinessHelper.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.math.BigInteger;
/**
* Author: hcryeo
* Date: 2023-05-11 13:44
* Description:
*/
@Slf4j
@Configuration
public class JacksonConfig {
/**
* 注册 Jackson 全局 Long 类型转为 String 类型,解决前端 Long 类型精度丢失的问题
*
* @return Jackson2ObjectMapperBuilderCustomizer
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
jacksonObjectMapperBuilder.serializerByType(BigInteger.class, ToStringSerializer.instance);
jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
};
}
}
以上代码只处理了 Long
类型,并没有处理 long
类型,如果想要处理 long
类型,再加一行:
jacksonObjectMapperBuilder.serializerByType(Long.TYPE, ToStringSerializer.instance);
前端处理的思路是,利用 Axios 的 transformResponse
在 JSON 数据尚未传递给 then/catch
之前,对内容进行拦截处理,使用 json-bigint
库进行解析 JSON:
import JSONbig from "json-bigint";
axios({
transformResponse: [
data => JSONbig.parse(data),
],
})
操作系统: CentOS 7
Jenkins 安装方式: 官方文档 yum 安装
安装完了 Jenkins 以后,我随便去创建了一个自由风格的项目测试一下,然后就构建失败了,漫长的折腾之路开始了。
我在构建的执行 Shell 里输入如下:
echo "构建 - 开始"
whoami
echo "构建 - 结束"
结果构建失败,查看控制台输出发现如下文:
Checking out Revision d147f439ea5cf57e460e48886db15a3971828499 (refs/remotes/origin/main)
> git config core.sparsecheckout # timeout=10
> git checkout -f d147f439ea5cf57e460e48886db15a3971828499 # timeout=10
Commit message: "???? 01"
> git rev-list --no-walk d147f439ea5cf57e460e48886db15a3971828499 # timeout=10
FATAL: Unable to produce a script file
java.nio.charset.UnmappableCharacterException: Input length = 1
at java.base/java.nio.charset.CoderResult.throwException(CoderResult.java:275)
at java.base/sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:307)
at java.base/sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:282)
at java.base/sun.nio.cs.StreamEncoder.write(StreamEncoder.java:132)
at java.base/java.io.OutputStreamWriter.write(OutputStreamWriter.java:205)
at java.base/java.io.BufferedWriter.flushBuffer(BufferedWriter.java:120)
at java.base/java.io.BufferedWriter.close(BufferedWriter.java:268)
at hudson.FilePath$CreateTextTempFile.invoke(FilePath.java:1658)
at hudson.FilePath$CreateTextTempFile.invoke(FilePath.java:1628)
at hudson.FilePath.act(FilePath.java:1198)
at hudson.FilePath.act(FilePath.java:1181)
at hudson.FilePath.createTextTempFile(FilePath.java:1622)
Caused: java.io.IOException: Failed to create a temp file on /var/lib/jenkins/workspace/XXXXXXXX
at hudson.FilePath.createTextTempFile(FilePath.java:1624)
at hudson.tasks.CommandInterpreter.createScriptFile(CommandInterpreter.java:202)
at hudson.tasks.CommandInterpreter.perform(CommandInterpreter.java:120)
at hudson.tasks.CommandInterpreter.perform(CommandInterpreter.java:92)
at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:818)
at hudson.model.Build$BuildExecution.build(Build.java:199)
at hudson.model.Build$BuildExecution.doRun(Build.java:164)
at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:526)
at hudson.model.Run.execute(Run.java:1900)
at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:44)
at hudson.model.ResourceController.execute(ResourceController.java:101)
at hudson.model.Executor.run(Executor.java:442)
Build step 'Execute shell' marked build as failure
Finished: FAILURE
在这段信息之前还有一些信息,都是 Git 拉取代码相关的,没什么用。
这段报错信息中,有 3 段有用的信息可以提供线索。
Commit message: "???? 01"
这里明显是 Git Commit 信息乱码了,在 01
前面还有 4 个中文。
想便大概是和字符集有关了,但我当时认为,乱码就先乱码吧,应该不至于影响后面的构建吧。
接着往下看,发现 Java 的异常信息:
java.nio.charset.UnmappableCharacterException: Input length = 1
这个比较关键,去查了一下说是字符集设置不正确导致,于是上网搜索了一堆答案,都没有效果,暂时放弃。
Caused: java.io.IOException: Failed to create a temp file on /var/lib/jenkins/workspace/XXXXXXXX
看起来意思是 Jenkins 在编译构建的过程中,拉取了 Git 代码以后,创建临时目录失败了,但是我去服务器上看了,目录都还在,什么鬼?
把目录删除了,重新构建也不行。
从编译失败的信息中, 一共获取到了三段有用的信息,两个尝试了未果,也许有可能都是串联的吧?
那就先从第一个下手好了,搜索出来的结果操作起来都比较简单,都是些什么在这个位置添加环境变量,设置 LANG,在那个位置添加环境变量设置 Lang,结果没有一个好用的,就一个小小的字符集问题,搞起来这么难?心态崩了。
在上面的 搜索问题 - 得到答案 - 尝试 - 失败
的循环中,发现了一条比较有用的线索,那就是可以通过 Jenkins 的系统信息中看到一些 Java 相关的运行参数,其中便有字符集相关的,最主要的一个,就是 file.encoding
。
在我的 Jenkins 上可以看到是 ANSI_X3.4-1968
,网上的老司机们都说这个不对,搞成 UTF-8
,他们的问题就解决了,但是我按照他们的该当一顿操作下来,死活改不掉这个字符集,继续崩溃。
就在我苦苦地一遍又一遍地问候着 Jenkins 这个玩意儿怎么这么多坑的时候,突然想起来,就在刚刚安装好 Jenkins 的时候,遇到过类似的情况,改 Jenkins 配置结果死活都不生效,最后是去修改的 service
文件才解决!
立马执行 systemctl status jenkins
查看到 Jenkins 的 service
文件位置 /usr/lib/systemd/system/jenkins.service
,Vim 它!
在里面翻到了一个 Environment="JAVA_OPTS=-Djava.awt.headless=true"
的配置项,想必就是你了吧,把 -Dfile.encoding=UTF-8
加进去,就成了:
Environment="JAVA_OPTS=-Djava.awt.headless=true -Dfile.encoding=UTF-8"
systemctl daemon-reload && systemctl restart jenkins
以后,再次查看 Jenkins 系统信息,终于变过来了!
执行构建,查看结果也没有问题了,在编译过程中的中文的 Git Commit 也能正常显示了,哦特么噎死~
修改配置文件:/etc/ssh/sshd_config
# 是否允许 root 远程登录
PermitRootLogin yes
# 开启公钥认证
PubkeyAuthentication yes
# 存放登录用户公钥的文件位置
AuthorizedKeysFile .ssh/authorized_keys
# 一路回车
ssh-keygen -t rsa
把 客户端 的 ~/.ssh/id_rsa.pub
里的内容粘贴到 服务器 的 ~/.ssh/authorized_keys
里。
# 登录的服务器别名 ssh server 就可以了
Host server
# 要登录的服务器ip
HostName 0.0.0.0
# 端口
Port 22
# 登录名
User root
# 私钥路径
IdentityFile ~/.ssh/id_rsa
ServerAliveInterval 30
TCPKeepAlive yes
/etc/selinux/config 将SELINUX=enforcing改为SELINUX=disabled
setsebool -P httpd_can_network_connect 1
进入 chrome://net-internals/#hsts
在 Delete domain security policies
里输入域名并点击 delete
在 Query HSTS/PKP domain
里输入域名点击 query
下面出现提示 Not found
代表成功。
先将本地的 jar 包放到需要的子模块下的 src/main/resources/lib
目录下,lib
是我手动新建的,然后修改子模块的 pom.xml
,添加依赖
<dependencies>
<dependency>
<groupId>com.hikvision</groupId>
<artifactId>artemis-http-client</artifactId>
<version>1.1.11</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/artemis-http-client-1.1.11.RELEASE.jar</systemPath>
</dependency>
</dependencies>
在项目要目录的 pom.xml
里添加上:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
记录一下在 SpringBoot 2 中使用 Gradle 整合 QueryDSL 以配合 JPA 查询。
Java 17
SpringBoot 2.7.7
Gradle 7.6
QueryDSL Version 5.0.0
最终目的是提供两个 Restful API:
/
路由提供用户列表/{id}
路由提供用户详情使用 QueryDSl 的重点就是在定义好了 Entity 以后,通过 Gradle Build 出 QEntity 类。如在以下演示文件中定义的是 User Entity,在 build 以后,在项目中可以看到自动生成的 Q 类:
在 build.gradle
中,添加以下:
// 在最外层场景 QueryDSL 版本
var queryDslVersion = '5.0.0'
// 以下需要添加到 dependencies 里
implementation ("com.querydsl:querydsl-jpa:${queryDslVersion}")
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:jpa")
annotationProcessor("jakarta.persistence:jakarta.persistence-api")
spring:
jpa:
hibernate:
ddl-auto: update
show-sql: true
datasource:
driverClassName: org.mariadb.jdbc.Driver
password: root
username: root
url: jdbc:mariadb://10.15.1.175:43306/demo
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.7'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
var queryDslVersion = '5.0.0'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation ("com.querydsl:querydsl-jpa:${queryDslVersion}")
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:jpa")
annotationProcessor("jakarta.persistence:jakarta.persistence-api")
}
tasks.named('test') {
useJUnitPlatform()
}
package com.example.demo.entity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.Hibernate;
import javax.annotation.PostConstruct;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Objects;
/**
* Author: lvxianchao
* Date: 2023-01-09 11:16
* Description:
*/
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
@Table(name = "user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
User user = (User) o;
return id != null && Objects.equals(id, user.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@PostConstruct
public void init(){
}
}
package com.example.demo.controller;
import com.example.demo.dto.UserIndexDTO;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* Author: lvxianchao
* Date: 2023-01-09 11:16
* Description:
*/
@RestController
public class UserController {
@Resource
UserService service;
@GetMapping("/")
public List<User> index(UserIndexDTO dto) {
System.out.println("dto = " + dto);
return service.index(dto);
}
@GetMapping("/{id}")
public User show(@PathVariable Long id) {
return service.show(id);
}
}
package com.example.demo.dto;
import lombok.Data;
/**
* Author: lvxianchao
* Date: 2023-01-09 13:08
* Description:
*/
@Data
public class UserIndexDTO {
private Long id;
private String name;
private String email;
}
package com.example.demo.service;
import com.example.demo.dto.UserIndexDTO;
import com.example.demo.entity.QUser;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
/**
* Author: lvxianchao
* Date: 2023-01-09 11:17
* Description:
*/
@Service
public class UserService {
@Resource
UserRepository repository;
@PersistenceContext
private EntityManager entityManager;
public List<User> index(UserIndexDTO dto) {
QUser user = QUser.user;
JPAQueryFactory query = new JPAQueryFactory(entityManager);
JPAQuery<User> selectFrom = query.selectFrom(user);
if (dto.getId() != null) {
System.out.println("dto.getId() = " + dto.getId());
selectFrom.where(user.id.eq(dto.getId()));
}
if (dto.getName() != null) {
System.out.println("dto.getName() = " + dto.getName());
selectFrom.where(user.name.eq(dto.getName()));
}
if (dto.getEmail() != null) {
System.out.println("dto.getEmail() = " + dto.getEmail());
selectFrom.where(user.email.eq(dto.getEmail()));
}
return selectFrom.fetch();
}
public User show(Long id) {
QUser user = QUser.user;
JPAQueryFactory query = new JPAQueryFactory(entityManager);
User first = query.selectFrom(user).where(user.id.eq(id)).fetchFirst();
return first;
}
}
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Author: lvxianchao
* Date: 2023-01-09 11:16
* Description:
*/
public interface UserRepository extends JpaRepository<User, Long> {
}
CentOS 7 执行 systemctl stop firewalld
以后,Docker 运行容器报错:
Error response from daemon: driver failed programming external connectivity on endpoint nginx (492fe5bacabeff80220a680de0855668f26982f06346cd532048caf70534e58a): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 80 -j DNAT --to-destination 172.17.0.2:80 ! -i docker0: iptables: No chain/target/match by that name.
(exit status 1))
Error: failed to start containers: nginx
Laravel 中使用 SimpleSoftwareIO/simple-qrcode
扩展包生成的图片是空白的。
在 控制台输出
中查看 workspace
, 然后删除掉工程,重新构建。
在配置文件的 site
项中添加一项:
- map: octane.test
to: 8000 # octane 启动的端口号
type: proxy
在 Gradle 依赖里需要安装
developmentOnly 'org.springframework.boot:spring-boot-devtools'
。
在 Settings > Build, Execution, Deployment > Compiler
中勾选 Build project automatically
,顺便也可以把 Compile independent modules inparalle
也给勾选上。
至此,已经可以通过 Ctrl/Commnd + F9
来手动构建触发自动热部署了(其实就是把活跃的 Java 类重新编译,然后 IDE 替我们自动重新启动项目)。
如果还想再自动化一点,可以将自动编译按如下方式开启:
在 Advanced Settings
里,勾选 Allow auto-make to start even if developed application is currently running
sudo mkdir -p /etc/systemd/system/docker.service.d
/etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"
Environment="NO_PROXY=your-registry.com,10.10.10.10,*.example.com"
sudo systemctl daemon-reload
sudo systemctl restart docker
kex_exchange_identification: Connection closed by remote host
Connection closed by 20.205.243.166 port 22
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
生成:ssh-keygen -t ed25519 -C "备注tliy"
在 ~/.ssh/config
里新增:
Host github.com
HostName ssh.github.com
User git
Port 443
验证:ssh -T [email protected]
ssh -T [email protected]
The authenticity of host '[ssh.github.com]:443 ([20.205.243.160]:443)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[ssh.github.com]:443' (ED25519) to the list of known hosts.
Hi lvxianchao! You've successfully authenticated, but GitHub does not provide shell access.
默认情况下 Spring Data JPA 通过 Entity 创建的数据表的字符集是 latin
, 通常我都是用 utf8mb4
字符集,可以通过以下操作实现。
在 application.yml
里添加一个配置项用于指定一个我们自定义的 Dialect 类(为了方便以下写为 property
的格式,如果是 yml
格式需要自行调整):
spring.jpa.properties.hibernate.dialect: com.lvxianchao.translation.config.MySQL5UTF8mb4Dialect
在 config
包下创建 MySQL5UT8MB4Dialect
,其内容如下:
package com.lvxianchao.translation.config;
import org.hibernate.dialect.MySQLDialect;
/**
* Author: lvxianchao
* Date: 2023-01-12 17:36
* Description:
*/
public class MySQL5UTF8mb4Dialect extends MySQLDialect {
@Override
public String getTableTypeString() {
return "engine = innodb default charset = utf8mb4";
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.