ObjectMapper 泛型 JSON 序列化與反序列化
在 Java 裡面用 ObjectMapper(Jackson)處理 JSON 再正常不過了,但一旦涉及到泛型,事情就開始變得有點複雜。我之前在這邊踩過好幾次坑,最後發現居然是反序列化出來的不是我預期的物件。 為什麼泛型會出問題?先說為什麼會有這個問題。Java 的泛型是編譯時的特性,在 runtime 時會被擦除。也就是說,List<MyObj> 和 List<String> 在 runtime 時看起來都是一樣的 List,沒辦法區分。 所以當你這樣寫的時候: 12String json = "[{\"name\": \"Alice\"}, {\"name\": \"Bob\"}]";List<MyObj> list = objectMapper.readValue(json, List.class); Jackson 不知道你想要 List<MyObj>,反序列化出來的就是 List&l...
Spring MVC AOP 設定與 AspectJ 用法
AOP(面向切面編程)這個概念一開始聽起來很唬爛,但其實就是把某些跨越多個業務邏輯的共同功能(比如 logging、權限驗證、效能監控)提取出來,用一個統一的地方來管理。 我之前有個專案就是沒用 AOP,logging 的程式碼散落在各個 service 裡面,後來改用 AOP 重構了一遍,爽度直接升天。 前置準備首先要加上依賴。如果用 Spring Boot,直接加: 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency> 這個依賴裡面已經包含了 AspectJ 和 Spring AOP 相關的東西。 基本結構用 Spring AOP 的步驟很簡單: 寫一個類別標注 @Aspect 標注 @Component 讓 Spring 掃描到 在方法上標注 @Before、@After 等 定義切入點(point...
Spring Bean Scope 六種作用域完整解析
之前在專案裡因為搞不清楚 Bean scope,導致一個坑埋了好久才發現。乾脆就把 Spring 的六種 scope 整理一下,這樣以後不會再踩到相同的坑。 六種 Scope 速覽Spring Bean 的六種 scope 分別是: singleton - 預設,整個 container 只有一個實例 prototype - 每次 getBean 都新建一個實例 request - 每個 HTTP request 一個實例 session - 每個 HTTP session 一個實例 application - 整個 ServletContext 一個實例 websocket - 每個 WebSocket session 一個實例 後面四種(request、session、application、websocket)只在 web application 中可用。 Singleton 作用域這是預設的 scope。如果你沒有特別指定,Spring 就會把 Bean 當成 singleton: 123456@Componentpublic class UserService ...
Spring @Bean 與 @ComponentScan 的差異
有時候在寫 Spring config 時,新手都會搞不清楚 @Bean 和 @ComponentScan 到底差在哪。我自己也踩過不少坑,今天就來把這兩個概念理清楚。 先說結論簡單講: @Bean 是手動註冊,你明確地在 @Configuration 裡面寫程式碼,告訴 Spring 怎麼建立這個 Bean @ComponentScan 是自動掃描,Spring 自動找出你用 @Component、@Service、@Repository 這些註解標注的類別,然後註冊成 Bean 兩者各有用途,也可以混用。 @Bean 怎麼用@Bean 通常用在 @Configuration class 裡面。你寫一個方法,用 @Bean 標注,Spring 就會把這個方法回傳的物件當成 Bean 註冊進去。 12345678910111213@Configurationpublic class AppConfig { @Bean public DataSource dataSource() { return new DataSourc...
🔁 解決 Java 中的 ABA 問題 — 使用 AtomicStampedReference
❓ 什麼是 ABA 問題?ABA 問題發生在使用 CAS(Compare-And-Swap)時: 執行緒 A 讀到某個變數值為 A 執行緒 B 將該變數從 A ➝ B ➝ A 執行緒 A 再次使用 CAS,認為值沒變,繼續執行,但其實值已變動過! 這會導致程式誤以為狀態未變,但實際上已經被其他執行緒修改過。 🧰 解法:使用 AtomicStampedReferenceJava 提供 AtomicStampedReference<T>,這是一種「附加版本號(stamp)」的原子參考,可以避免 ABA 問題。 它在每次變更時,都會同步更新一個 stamp 值,用來追蹤版本。 🧪 範例程式碼123456789101112131415161718192021222324252627import java.util.concurrent.atomic.AtomicStampedReference;public class AbaProblemSolution { public static void main(String[] args) {...
☕ Java 原子性變數與 CAS 筆記
📌 一、什麼是「原子性變數」與 CAS?在多執行緒環境下,多個執行緒若同時修改同一個變數,容易發生資料競爭。 Java 提供 java.util.concurrent.atomic 套件,裡面有許多「原子操作」的變數類型,例如: AtomicInteger AtomicLong AtomicReference 這些類別提供 非阻塞(non-blocking) 的操作方式,底層就是使用 CAS(Compare-And-Swap,比較並交換) 實作的。 ⚙️ 二、CAS 的核心邏輯CAS 是一種樂觀鎖策略,基本流程如下: 讀取目前值 比對是否與預期值一致 若一致,就更新為新值 若不一致,就放棄(重試或結束) ✅ 優點:不用 synchronized 就能寫出安全的多執行緒操作 ❌ 缺點:高併發時可能需要一直 retry,自旋過多會浪費 CPU 💡 三、實際範例:用 CAS 實作「無鎖計數器」1234567891011121314151617181920212223242526272829303132333435363738import java.util.concu...
從 Identity 欄位極限值看資料庫運作:實戰查詢範例分享
在這篇文章中,我想跟各位分享一個相當有趣又實用的 SQL 查詢範例。這支查詢主要用來檢查資料庫中各個資料表的 Identity 欄位(通常作為主鍵使用)的目前值(last_value),並根據該欄位的資料型態來推算其可能達到的最大極限值。 查詢範例以下就是完整的 SQL 查詢程式碼: 12345678910111213141516SELECT OBJECT_NAME(ic.object_id) AS TableName, ic.name AS IdentityColumn, ic.last_value AS CurrentValue, CASE WHEN ty.name = 'tinyint' THEN 255 WHEN ty.name = 'smallint' THEN 32767 WHEN ty.name = 'int' THEN 2147483647 ...
資料表重新建立及資料遷移 SOP
💡 Purpose: 此 SOP 說明如何從原始資料表進行備份、建立新表、資料遷移以及重新命名,目的是因應原資料表主鍵未設定自動遞增,需先備份資料,再依新的 DDL 語法建立自動遞增主鍵的表結構,最後將資料還原。請務必在每個步驟執行前確認相關操作已備份完成,以避免資料遺失。 前置檢查步驟 0:檢查表結構與資料筆數 取得資料表結構,確認是否已有自動遞增主鍵。 1exec sp_columns WCSTXXX; 檢查主鍵(例如:WCSID)是否有重複筆數: 1234select WCSID, COUNT(*)from WCSTXXXgroup by WCSIDhaving COUNT(*) > 1; 檢查資料表的總筆數: 1select COUNT(*) from WCSTXXX; 注意: 請將檢查結果記錄下來,作為後續參考依據。 備份原始資料步驟 1:備份現有資料表 將現有資料完整備份至另一張暫存表中,避免後續操作造成資料遺失: 1select * into backup_WCSTXXX from WCSTXXX; 建立新資料表步驟 2:產出並修改...
統計資料庫所有表的筆數與完整性校驗(CheckSum)
今天來分享幾個在 SQL Server 2008 裡,如何比對資料表內容的小技巧,讓你可以快速檢查資料是否有異動。以下會介紹三種方法,分別適用於不同的情境,親自實測過,效果滿不錯的喔! 方法 1:用 CHECKSUM_AGG 來計算表格變更這個方法是利用 CHECKSUM_AGG(BINARY_CHECKSUM(*)) 來算出整個資料表的檢查碼。它的優點就是運算速度快,不會遇到像 XML 轉換那種限制,缺點則是有可能會發生碰撞(就是不同資料卻得到一樣的檢查碼),而且對於 BLOB 或浮點數欄位就不太適合。 123456789SELECT s.name AS SchemaName, t.name AS TableName, CHECKSUM_AGG(BINARY_CHECKSUM(*)) AS TableChecksumFROM sys.tables t INNER JOIN sys.schemas s ON t.schema_id = s.schema_idWHERE s.name = 'dbo'GROUP BY s.nam...
多執行緒的相關術語
序列(串行):任務依照先後順序逐一執行,必須等前一個任務完成後,下一個任務才會開始執行。 並發(交替執行):多個任務在同一單位時間內交替執行,從宏觀上看似乎同時進行,但在任何特定時刻,實際上僅有一個任務在執行。這種模式通常發生在單核心或單執行緒環境下。 平行(真正同時執行):多個任務能夠在相同時刻真正地同時運行,這通常依賴於多核心處理器或多處理器系統,使得各任務可分派到不同的核心上同步執行。 1. 序列(串行)所有任務按順序執行,一個完成後才開始下一個。 程式碼12345678910111213141516171819202122public class SequentialExample { public static void main(String[] args) { long startTime = System.currentTimeMillis(); task("Task 1"); task("Task 2"); task("Task...














