JDK21對(duì)虛擬線程的幾種用法實(shí)踐指南
一、參考官方文檔
Virtual Threads
二、什么是虛擬線程
目標(biāo):用少量平臺(tái)線程(Platform Threads)支撐海量并發(fā)任務(wù)(如 100 萬(wàn)請(qǐng)求),提升吞吐量。
| 類(lèi)型 | 說(shuō)明 |
|---|---|
| 平臺(tái)線程(PlatformThread) | JVM 映射到操作系統(tǒng)線程(OS Thread),創(chuàng)建成本高(默認(rèn)幾百 KB ??臻g),數(shù)量受限(通常幾千) |
| 虛擬線程(VirtualThread) | JVM 內(nèi)部輕量線程,由 Thread.ofVirtual() 創(chuàng)建,不直接綁定 OS 線程,可創(chuàng)建百萬(wàn)級(jí) |
平臺(tái)線程 = 真實(shí)員工(數(shù)量少、成本高) 虛擬線程 = 臨時(shí)工(數(shù)量多、任務(wù)來(lái)了再分配真實(shí)員工干活)
適用場(chǎng)景:
| 場(chǎng)景 | 說(shuō)明 |
|---|---|
| ?? 高并發(fā) I/O 任務(wù) | 如 HTTP 請(qǐng)求、數(shù)據(jù)庫(kù)查詢(xún)、Redis 調(diào)用(阻塞多、CPU 少) |
| ?? Web 服務(wù)器處理請(qǐng)求 | Tomcat、Netty、Spring WebFlux 等每請(qǐng)求一線程模型 |
| ?? 批量處理任務(wù) | 如 10 萬(wàn)條數(shù)據(jù)同步、日志處理 |
| ?? 調(diào)用多個(gè)外部服務(wù) | 并行調(diào)用訂單、用戶(hù)、庫(kù)存服務(wù)并聚合結(jié)果 |
不適用場(chǎng)景:
| 場(chǎng)景 | 說(shuō)明 |
|---|---|
| ?? CPU 密集型任務(wù) | 如圖像處理、加密解密、大數(shù)據(jù)計(jì)算 → 用平臺(tái)線程池(ForkJoinPool) |
| ?? 長(zhǎng)時(shí)間運(yùn)行的無(wú)限循環(huán) | 虛擬線程調(diào)度器可能“餓死”其他任務(wù) |
| ?? JNI / Native 代碼 | 虛擬線程會(huì)被掛起,直到 native 方法返回(阻塞平臺(tái)線程) |
| ?? 持有 synchronized 塊太久 | 會(huì)阻塞平臺(tái)線程,降低并發(fā)能力 |
三、幾種用法
| 你的需求 | 推薦用法 |
|---|---|
| 學(xué)習(xí)虛擬線程 | Thread.ofVirtual().start() |
| 高并發(fā) Web 請(qǐng)求、批量 I/O | ? newVirtualThreadPerTaskExecutor() |
| CPU 密集型任務(wù)(如計(jì)算) | ? 不要用虛擬線程,用 ForkJoinPool |
1、Thread.ofVirtual().start()
—— 最基礎(chǔ)的創(chuàng)建方式
Thread vt = Thread.ofVirtual()
.name("worker")
.start(() -> {
System.out.println("Hello from " + Thread.currentThread());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("Done");
});
vt.join(); // 等待完成適用場(chǎng)景:
- 學(xué)習(xí)虛擬線程原理
- 單個(gè)、簡(jiǎn)單的并發(fā)任務(wù)
- 不需要任務(wù)管理或資源回收
不適用場(chǎng)景:
- 批量任務(wù)(如 10 萬(wàn)個(gè)請(qǐng)求)
- 需要統(tǒng)一管理生命周期
- 生產(chǎn)環(huán)境高并發(fā)服務(wù)
建議:
不推薦用于生產(chǎn)環(huán)境。它沒(méi)有資源池管理,無(wú)法限制并發(fā),容易造成內(nèi)存溢出。
2、Executors.newVirtualThreadPerTaskExecutor()
—— 最推薦的生產(chǎn)級(jí)用法
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
System.out.println("Task " + Thread.currentThread());
return "result";
});
}
} // 自動(dòng)等待所有任務(wù)完成參考官方:
try (ExecutorService myExecutor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<?> future = myExecutor.submit(() -> System.out.println("Running thread"));
future.get();
System.out.println("Task completed");
// ...適用場(chǎng)景 :
| 場(chǎng)景 | 說(shuō)明 |
|---|---|
| ?? Web 請(qǐng)求處理 | 每個(gè) HTTP 請(qǐng)求啟動(dòng)一個(gè)虛擬線程(Spring Boot 6+ 默認(rèn)) |
| ?? 批量 I/O 操作 | 如讀取 10 萬(wàn)個(gè)文件、調(diào)用外部 API |
| ?? 消息隊(duì)列消費(fèi) | 每條消息一個(gè)虛擬線程處理 |
| ?? Redis / DB 批量查詢(xún) | 配合 multiGet 或 pipeline 使用 |
優(yōu)勢(shì) :
- 自動(dòng)管理虛擬線程生命周期
try-with-resources自動(dòng)close()并等待所有任務(wù)完成- 無(wú)需擔(dān)心資源泄漏
用官方文檔的話來(lái)說(shuō)總結(jié)幾點(diǎn):

這個(gè)線程池會(huì)為每個(gè)提交的任務(wù)新建一個(gè)虛擬線程。
特別適合 服務(wù)器場(chǎng)景 或 批量并發(fā)任務(wù)。
你可以輕松提交成千上萬(wàn)個(gè)任務(wù),不會(huì)像傳統(tǒng)線程那樣資源緊張。
建議:
在生產(chǎn)環(huán)境中最應(yīng)該使用的虛擬線程方式!尤其適合:高并發(fā) + I/O 密集 + 短任務(wù)
3、信號(hào)量進(jìn)行限制并發(fā)
其實(shí)有同學(xué)會(huì)想到,虛擬線程能不能像平臺(tái)線程那樣子進(jìn)行池化,關(guān)于這個(gè)的官方解釋?zhuān)诠俜轿臋n中提到了很多次,多次提及兩者并不應(yīng)該是一個(gè)概念,你可以將平臺(tái)線程池視為從隊(duì)列中提取任務(wù)并處理它們的“工人”,將虛擬線程視為任務(wù)本身。

如果想要限制并發(fā),和平臺(tái)線程類(lèi)似的線程池那樣,可以使用信號(hào)量進(jìn)行限制:
Semaphore sem = new Semaphore(10);
...
Result foo() {
sem.acquire();
try {
return callLimitedService();
} finally {
sem.release();
}
}可能同學(xué)會(huì)認(rèn)為用信號(hào)量去限制的話和線程池還是很大差距,下面官方文檔也給了提示:
僅僅用信號(hào)量阻塞一些虛擬線程,可能看起來(lái)與向固定線程池提交任務(wù)有著本質(zhì)的不同,但實(shí)際上并非如此。向線程池提交任務(wù)會(huì)將其排隊(duì)等待稍后執(zhí)行,而信號(hào)量(或任何其他用于此目的的阻塞同步構(gòu)造)在內(nèi)部會(huì)創(chuàng)建一個(gè)線程隊(duì)列,這些線程因信號(hào)量而被阻塞,該隊(duì)列反映了等待池線程執(zhí)行的任務(wù)隊(duì)列。由于虛擬線程就是任務(wù),因此最終結(jié)構(gòu)是等價(jià)的。

4、官網(wǎng)更多其他用法
Thread.Builder builder = Thread.ofVirtual().name("MyThread");
Runnable task = () -> {
System.out.println("Running thread");
};
Thread t = builder.start(task);
System.out.println("Thread t name: " + t.getName());
t.join();The following example creates and starts two virtual threads with:
Thread.Builder builder = Thread.ofVirtual().name("worker-", 0);
Runnable task = () -> {
System.out.println("Thread ID: " + Thread.currentThread().threadId());
};
// name "worker-0"
Thread t1 = builder.start(task);
t1.join();
System.out.println(t1.getName() + " terminated");
// name "worker-1"
Thread t2 = builder.start(task);
t2.join();
System.out.println(t2.getName() + " terminated");輸出:
Thread ID: 21 worker-0 terminated Thread ID: 24 worker-1 terminated
希望同時(shí)向不同的服務(wù)發(fā)起多個(gè)出站調(diào)用:
void handle(Request request, Response response) {
var url1 = ...
var url2 = ...
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var future1 = executor.submit(() -> fetchURL(url1));
var future2 = executor.submit(() -> fetchURL(url2));
response.send(future1.get() + future2.get());
} catch (ExecutionException | InterruptedException e) {
response.fail(e);
}
}
String fetchURL(URL url) throws IOException {
try (var in = url.openStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}四、監(jiān)控對(duì)內(nèi)存的使用情況
模擬10w虛擬線程
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VirtualThreadTest {
// 方法一:查看當(dāng)前 JVM 內(nèi)存使用情況
public static void printMemoryUsage(String phase) {
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemory = memoryMXBean.getHeapMemoryUsage();
System.out.printf("[%s] Heap Memory Used: %d MB / %d MB%n",
phase,
heapMemory.getUsed() / (1024 * 1024),
heapMemory.getMax() / (1024 * 1024));
}
// 方法二:創(chuàng)建虛擬線程執(zhí)行任務(wù)
public static void runVirtualThreads(int taskCount) throws InterruptedException {
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < taskCount; i++) {
int finalI = i;
executor.submit(() -> {
System.out.printf("虛擬線程[%s] 執(zhí)行任務(wù) %d%n",
Thread.currentThread(),
finalI);
try {
Thread.sleep(1000); // 模擬 IO 阻塞
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
while (!executor.isTerminated()) {
Thread.sleep(100);
}
}
// 方法三:循環(huán)查看內(nèi)存(模擬 GC 回收效果)
public static void monitorMemory(int seconds) throws InterruptedException {
for (int i = 0; i < seconds; i++) {
printMemoryUsage("監(jiān)控中");
System.gc(); // 建議 GC,可能不會(huì)馬上執(zhí)行
Thread.sleep(1000);
}
}
// 主方法測(cè)試
public static void main(String[] args) throws Exception {
// 1. 初始內(nèi)存
printMemoryUsage("初始");
// 2. 創(chuàng)建 1 萬(wàn)個(gè)虛擬線程任務(wù)
runVirtualThreads(100_000);
// 3. 任務(wù)執(zhí)行后內(nèi)存
printMemoryUsage("任務(wù)執(zhí)行后");
// 4. 持續(xù)觀察 GC 是否回收虛擬線程
monitorMemory(10);
}
}

可以看到快速創(chuàng)建10萬(wàn)的虛擬線程也能很快的將內(nèi)存進(jìn)行回收,可能大家會(huì)想,不是說(shuō)虛擬線程是創(chuàng)建一個(gè)線程就回收內(nèi)存了嗎,為啥你這里統(tǒng)一進(jìn)行釋放呢,可以看到我這里進(jìn)行阻塞,模擬創(chuàng)建10萬(wàn)線程大概會(huì)占用多少內(nèi)存

五、兼容ThreadLocal
在我們現(xiàn)階段大多數(shù)用戶(hù)信息都是通過(guò)ThreadLocal進(jìn)行傳遞過(guò)來(lái),每個(gè)線程綁定一個(gè)用戶(hù)
1、傳統(tǒng)平臺(tái)線程 + ThreadLocal 的關(guān)系
下面我們回顧一下傳統(tǒng)的ThreadLocal和線程的關(guān)系:
- 每個(gè)
Thread對(duì)象內(nèi)部有一個(gè)ThreadLocal.ThreadLocalMap成員 ThreadLocal.set(value)→ 當(dāng)前線程的map中存入<ThreadLocal 實(shí)例, value>ThreadLocal.get()→ 從當(dāng)前線程的map中取出對(duì)應(yīng)值- 生命周期與線程綁定:線程存活 →
ThreadLocal副本存在 - 實(shí)際并發(fā) 100 請(qǐng)求 → 最多 100 個(gè)副本(但線程池通常只有 200 個(gè)線程,會(huì)復(fù)用)
關(guān)系圖如下:
平臺(tái)線程 T1 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value 平臺(tái)線程 T2 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value 平臺(tái)線程 T3 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value
2、虛擬線程(Virtual Thread) + ThreadLocal 的關(guān)系
- JVM 實(shí)現(xiàn)的輕量級(jí)線程
- 多對(duì)一映射到“載體線程”(Carrier Thread,即平臺(tái)線程)
- 棧在堆上分配,約 1KB
- 可創(chuàng)建 數(shù)十萬(wàn)甚至百萬(wàn)個(gè)
- 每個(gè) HTTP 請(qǐng)求一個(gè)虛擬線程(高并發(fā))
- 虛擬線程也是
java.lang.Thread的子類(lèi) - 所以它也有自己的
ThreadLocalMap set()/get()語(yǔ)法完全兼容
關(guān)系圖如下:
載體線程 C1(平臺(tái)線程) ├─ 虛擬線程 VT1 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value ├─ 虛擬線程 VT2 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value └─ 虛擬線程 VT3 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value 載體線程 C2 ├─ 虛擬線程 VT10001 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value └─ 虛擬線程 VT10002 → 有自己的 ThreadLocalMap → 存儲(chǔ) BaseContext.value
3、兩者的變化
- 傳統(tǒng):200 個(gè)線程 → 最多 200 個(gè)
ThreadLocal副本 - 虛擬線程:10 萬(wàn)個(gè)并發(fā)請(qǐng)求 → 10 萬(wàn)個(gè)
ThreadLocal副本
傳統(tǒng)的線程的話會(huì)進(jìn)行線程池排隊(duì),不會(huì)頻繁創(chuàng)建線程,從而不會(huì)導(dǎo)致線程爆炸。要是某個(gè)虛擬線程執(zhí)行什么任務(wù)。
4、怎么兼容線程模式下ThreadLocal
- 不全局使用虛擬線程(比如不設(shè)置
-Dspring.threads.virtual.enabled=true) - 只在特定地方手動(dòng)創(chuàng)建虛擬線程
- 在這些虛擬線程中使用
ThreadLocal傳遞上下文(如用戶(hù)信息)
// 獲取當(dāng)前用戶(hù)信息(來(lái)自平臺(tái)線程的 ThreadLocal)
String currentUser = BaseContext.getCurrentId(); // 假設(shè)這是從 ThreadLocal 拿的
// 在虛擬線程中使用時(shí),顯式傳入
Thread.startVirtualThread(() -> {
// 顯式使用傳入的上下文
processUserOrder(currentUser, orderId);
});六、一些虛擬線程相關(guān)的檢查代碼
1、檢查自己的spingboot版本
import org.springframework.core.SpringVersion;
public class SpringVersionCheck {
public static void main(String[] args) {
System.out.println("Spring Framework 版本: " + SpringVersion.getVersion());
}
}2、檢查自己是否默認(rèn)開(kāi)啟全局的虛擬線程
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
// 測(cè)試異步方法
AsyncService service = ctx.getBean(AsyncService.class);
service.asyncTask();
}
}
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
class AsyncService {
@Async
public void asyncTask() {
Thread thread = Thread.currentThread();
System.out.println("線程類(lèi)型: " + (thread.isVirtual() ? "虛擬線程" : "平臺(tái)線程"));
System.out.println("線程名稱(chēng): " + thread.getName());
}
}3、監(jiān)控虛擬線程存活數(shù)量
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
public class VirtualThreadMonitor {
public static void main(String[] args) {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
// 獲取當(dāng)前存活的線程總數(shù)(包括平臺(tái)線程和虛擬線程)
long totalThreadCount = threadBean.getThreadCount();
// 初始化虛擬線程計(jì)數(shù)器
long virtualThreadCount = 0;
// 遍歷當(dāng)前所有線程,檢查哪些是虛擬線程
for (Thread thread : Thread.getAllStackTraces().keySet()) {
if (thread.isVirtual()) { // 如果線程是虛擬線程
virtualThreadCount++;
}
}
// 輸出結(jié)果
System.out.println("當(dāng)前存活 虛擬線程數(shù): " + virtualThreadCount);
System.out.println("當(dāng)前存活 總線程數(shù): " + totalThreadCount);
}
}
七、實(shí)戰(zhàn)
新增conf配置類(lèi),讓spring幫忙管理他的生命周期
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
@Configuration
@EnableAsync // 啟用 @Async 支持
public class VirtualThreadConfig implements AsyncConfigurer {
private ExecutorService createVirtualThreadExecutor(String prefix) {
return Executors.newThreadPerTaskExecutor(
Thread.ofVirtual()
.name(prefix, 0)
.uncaughtExceptionHandler((thread, ex) ->
log.error("虛擬線程 [{}] 執(zhí)行異常", thread.getName(), ex))
.factory()
);
}
/**
* 用于單個(gè)用戶(hù)收藏?cái)?shù)據(jù)的異步回寫(xiě)(實(shí)時(shí)場(chǎng)景)
*/
@Bean("favoriteRedisWriterVirtualThreadExecutor")
public Executor favoriteUserWriteVirtualThreadExecutor() {
log.info("初始化虛擬線程池:favoriteUserWriteVirtualThreadExecutor");
return createVirtualThreadExecutor("vt-fav-user-write-");
}
/**
* 用于批量同步收藏?cái)?shù)據(jù)到 Redis(定時(shí)任務(wù)場(chǎng)景)
*/
@Bean("favoriteBatchWriteVirtualThreadExecutor")
public Executor favoriteBatchWriteVirtualThreadExecutor() {
log.info("初始化虛擬線程池:favoriteBatchWriteVirtualThreadExecutor");
return createVirtualThreadExecutor("vt-fav-batch-write-");
}
@Override
public Executor getAsyncExecutor() {
return createVirtualThreadExecutor("vt-default-async-");
}
}在定時(shí)任務(wù)中,我們需要對(duì)收藏?cái)?shù)據(jù)進(jìn)行全量重寫(xiě)到 Redis,以保證緩存一致性。由于數(shù)據(jù)量較大(3200+ 條記錄),采用同步方式逐條寫(xiě)入會(huì)導(dǎo)致任務(wù)執(zhí)行時(shí)間過(guò)長(zhǎng),影響系統(tǒng)響應(yīng)性。為了提升任務(wù)執(zhí)行效率,我們引入 虛擬線程(Virtual Threads),將每條 Redis 寫(xiě)入操作提交到獨(dú)立的虛擬線程中并發(fā)執(zhí)行。這樣可以在不增加平臺(tái)線程負(fù)擔(dān)的前提下,顯著提高 I/O 密集型操作的吞吐量,縮短全量刷新時(shí)間。




至此就簡(jiǎn)練的實(shí)現(xiàn)異步虛擬線程的改造方案
總結(jié)
到此這篇關(guān)于JDK21對(duì)虛擬線程的幾種用法實(shí)踐指南的文章就介紹到這了,更多相關(guān)JDK21對(duì)虛擬線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實(shí)現(xiàn)自定義Starter的步驟詳解
在SpringBoot中,Starter是一種特殊的依賴(lài),它可以幫助我們快速地集成一些常用的功能,例如數(shù)據(jù)庫(kù)連接、消息隊(duì)列、Web框架等。在本文中,我們將介紹如何使用Spring Boot實(shí)現(xiàn)自定義Starter,需要的朋友可以參考下2023-06-06
Java調(diào)用groovy實(shí)現(xiàn)原理代碼實(shí)例
這篇文章主要介紹了Java調(diào)用groovy實(shí)現(xiàn)原理代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12
利用Java實(shí)現(xiàn)Word文檔自動(dòng)編號(hào)提取的方法詳解
文章介紹如何用Java從Word試卷中提取自動(dòng)編號(hào),通過(guò)創(chuàng)建NumberingContext類(lèi)識(shí)別中文、阿拉伯?dāng)?shù)字及字母等不同編號(hào)格式,并實(shí)現(xiàn)getParagraphNumbering方法完成結(jié)構(gòu)化轉(zhuǎn)換,解決了傳統(tǒng)方法無(wú)法解析編號(hào)的問(wèn)題,感興趣的小伙伴可以參考閱讀下2025-09-09
SpringBoot雪花算法主鍵ID傳到前端后精度丟失問(wèn)題的解決
本文主要介紹了SpringBoot雪花算法主鍵ID傳到前端后精度丟失問(wèn)題的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
java web實(shí)現(xiàn)用戶(hù)權(quán)限管理
這篇文章主要介紹了java web實(shí)現(xiàn)用戶(hù)權(quán)限管理,設(shè)計(jì)并實(shí)現(xiàn)一套簡(jiǎn)單的權(quán)限管理功能,感興趣的小伙伴們可以參考一下2015-11-11
SpringBoot整合MinIO實(shí)現(xiàn)文件上傳的方法詳解
一般涉及到文件上傳,基本上都是保存在項(xiàng)目本地,這種方式比較省事,但是安全性不高。所以今天給大伙詳細(xì)介紹一些如何利用MinIO實(shí)現(xiàn)文件上傳,感興趣的可以了解一下2022-05-05
Java中使用Spring Retry實(shí)現(xiàn)重試機(jī)制的流程步驟
這篇文章主要介紹了我們將探討如何在Java中使用Spring Retry來(lái)實(shí)現(xiàn)重試機(jī)制,重試機(jī)制在處理臨時(shí)性故障和提高系統(tǒng)穩(wěn)定性方面非常有用,文中通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-07-07
springboot如何為web層添加統(tǒng)一請(qǐng)求前綴
這篇文章主要介紹了springboot如何為web層添加統(tǒng)一請(qǐng)求前綴,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Java Hibernate中使用HQL語(yǔ)句進(jìn)行數(shù)據(jù)庫(kù)查詢(xún)的要點(diǎn)解析
HQL是Hibernate框架中提供的關(guān)系型數(shù)據(jù)庫(kù)操作腳本,當(dāng)然我們也可以使用原生的SQL語(yǔ)句,這里我們來(lái)看一下在Java Hibernate中使用HQL語(yǔ)句進(jìn)行數(shù)據(jù)庫(kù)查詢(xún)的要點(diǎn)解析:2016-06-06

