潮流特區

最新文章

Spring Data 關聯型態 01

科技新知
MacauYeah・2024-07-16

筆者身邊的朋友,首次接觸 ORM 的關聯型態時都會覺得很難,筆者自己也是。但在好好地理順它的設計時,就會覺得其實很簡單。 因為篇輻很長,我們先以Code First的角度,先體驗一下ORM程式讀取的便捷性,以及解決一個常見的序列化問題。 雙向存取 例如一個Parent,有好幾個Child @Entity public class Parent { // ... Parent Primay Key @OneToMany(mappedBy="parent") List children = new ArrayList(); // TODO add remove } @Entity public class Child { // ... Child Primay Key @ManyToOne Parent parent; } 上述的寫法很簡潔,ORM會為你自動加入join column,處理關聯的載入。在讀取Parent時,它的所有Children就可以直接在Java層面讀取,在讀取Child時,它的Parent也隨時取得。也就是,開發人員只要經SQL準備其中一方的資料,另一方並不需要手動準備,它就可以自動按需載入。 RESTFul API 坑-雙向存取 Spring Data在Java層面的雙向存取,已經做到很方便。但經常坑到我們的是Spring Data與RESTFul API的混合應用。當我們嘗試經API回傳我們的Parent Json時,API會很聰明地把關聯的Children也變成Json回傳。但他也會把child中的parent不斷重複變成json,變成無限輪迴。 坊間有兩種不同的解決方案,可以防止無限輪迴。 讓Json可以認得已經序列化的元素。@JsonIdentityInfo 讓Json只可以單向序列化(serialization)。@JsonManagedReference, @JsonBackReference, @JsonIgnore 筆者兩個方向都試過,但首個方法並不通用,至少它不能算是一般常見的無腦Json結構。它需要伺服器、客戶端都懂這如何經IdentityInfo認得重複出現的元素。 而單向序列化,是筆者現時的通用解。在設計RESTFul READ API時,筆者就會決定到底是Parent自動回傳Child,還是Child自動回傳Parent。決策的考慮因素,主要在於是否可以簡化Client的API調用次數。通常從Parent出發,自動回傳Child,可以節省API調用。但如果是選項性的結果(List of Value),就倒過來。有時候,遇著API需要雙向設計,就只好自己設計DTO資料傳輸對象 (Data transfer object, DTO)。 例如Parent API,就原封不動回傳原本的元素 @Entity public class Parent{ // ... Parent Primay Key @OneToMany(mappedBy="parent") List children = new ArrayList(); } @Entity public class Child { // ... Child Primay Key @ManyToOne @JsonIgnore Parent parent; } Child API,就反過來引用。 public class ParentDTO { // ... Parent Other fields except children } public class ChildDTO { ParentDTO parent; // ... Child Other fields } 這種DTO,看起來很麻煩。但其實Spring有提供一個簡便的複制DTO功能,它可以把自動複制兩個class中有同一名稱、同一型別的欄位到另一個class上,不需要逐個欄位明文寫出來。 BeanUtils.copy(child, childDTO); BeanUtils.copy(parent, parentDTO); childDTO.setParent(parentDTO) // 因為child、childDTO中的parent欄位型別不同,BeanUtils.copy會自動忽略,其他欄位就會自動複制。 註: 其實古早的網頁系統設計,DTO的概念一直存取。只是現在RESTFul API的流行,很多框架已經提向便捷的Json轉換。若然平時只需Json單向存取,筆者還是省略DTO的建立。

如何提高保歷加通道交易策略的勝算

創富坊
程式交易 www.quants.hk (導師: 財經書藉作家: 麥振威)・2024-07-10

我們已學習了一些pine script的基本語法,相信大家經練習後已能逐漸掌握。這篇則會講解一些比較深入的分析技巧。 有一種策略是很多新手都經常問我的,若要寫股價跌穿保歷加通道底部便買入,升穿保歷加通道頂部便造淡,應該怎樣用pine script 寫出來? 以下的入市準則是,收市價低於保歷加通道底部便買入,然後待收市價跌穿保歷加通道中軸便平倉,相反,收市價高於保歷加通道頂部則造淡,然後待收市價升穿保歷加通道中軸平倉。 //@version=5 strategy("升穿bollinger's band錯誤用法", overlay=true, margin_long=100, margin_short=100) sma20=ta.sma(close,20) mult=ta.stdev(close,20) upper=sma20+2*mult lower=sma20-2*mult noposition=strategy.position_size==0 var bool traded =false buyCond=close<lower and close<sma20 shortCond=close>upper and close>sma20 buycloseCond=ta.crossover(close,sma20) shortcloseCond=ta.crossunder(close,sma20) if buyCond and noposition and not traded strategy.entry("BUY",strategy.long) traded:=true if buycloseCond and not noposition strategy.close("BUY") if shortCond and noposition and not traded strategy.entry("SHORT",strategy.short) traded:=true if shortcloseCond and not noposition strategy.close("SHORT") if ta.change(time("D"))!=0 traded:=false 這是最簡單的寫法,但大家應會想到,若收市價低於保歷加通道底部便買入,那以下情況可能會出現「連續多次買入」,所以在策略中已設定了每天只交易一次,而且入市情況需要「沒有持倉」才會入市。 可以看到這類交易策略,交易一年後仍然要虧損,數據是用了Tesla(US:TSLA)的5分鐘數據,而這個策略在一年裏交易了259次,獲利的有151次,勝率大約是58.3%。 若要修改這類運用保歷加通道頂部及底部的策略,其實比較好的處理方法是,當股價升穿保歷加通道頂部後,等待股價再回落至保歷加通道之內才入市造淡,同樣地,若股價跌穿保歷加通道底部,也是等待股價回升至保歷加通道之內才入市造好。 寫這類策略的方法是運用ta.crossover 及ta.crossunder, 若寫成ta.crossover(close,lower),就是代表了股價由保歷加通道底部以下,升穿保歷加通道底部之時便會入市買入。 可以想想,要出現這種情況必然是股價之前已經跌穿了保歷加通道底部才會發生,那便既符合跌穿保歷加通道底部的要求,同時又符合了股價再回升至保歷加通道內的要求。 而升穿保歷加通道頂部後再待股價回落至通道之內的寫法應大家現在也懂得怎樣寫,那便是ta.crossunder(close,upper)。 以下是整個策略的完整寫法: //@version=5 strategy("升穿bollinger's band及跌穿bollinger's band策略", overlay=true, margin_long=100, margin_short=100) sma20=ta.sma(close,20) mult=ta.stdev(close,20) upper=sma20+2*mult lower=sma20-2*mult noposition=strategy.position_size==0 var bool traded =false buyCond=ta.crossover(close,lower) and close<sma20 shortCond=ta.crossunder(close,upper) and close>sma20 buycloseCond=ta.crossover(close,sma20) shortcloseCond=ta.crossunder(close,sma20) if buyCond and noposition and not traded strategy.entry("BUY",strategy.long) traded:=true if buycloseCond and not noposition strategy.close("BUY") if shortCond and noposition and not traded strategy.entry("SHORT",strategy.short) traded:=true if shortcloseCond and not noposition strategy.close("SHORT") if ta.change(time("D"))!=0 traded:=false 從backtest report可以看到勝率會較第一個的策略為高,一年裏交易了258次,獲利的有167次,勝率提高至64.73%,但最重要的是原本是虧損的策略已變成輕微獲利。 不過,獲利確實不多,那又有沒有方法可以改得更好? 最常見的做法是觀察圖表上的入市訊號,特別是留意出現裂口高開或裂口低開的情況,因為不少人都會認為出現裂口高開或裂口低開會引發上日持倉過夜的炒家的平倉盤,但這並不代表當日即市的走勢,只會在開市初段產生短暫影響。 而我們在圖表上觀察這個策略的入市訊號時,又確實發現有些日子的造淡的訊號會因為當日出現裂口高開,因而升穿了保歷加通道頂部,其後股價重返保歷加通道之內便入市造淡,不過最後卻出現虧損。 那若我們剔除因裂口高開而出現的入市造淡訊號又會怎樣? 但這種修改方法又很可能把原本能獲利的訊號也剔除的,結果是交易表現可能更差。 筆者就建議可以試試與平均線配合作修改,例如運用「Hull Moving Average,HMA」,這是一種了特別重視最近價格變動的加權移動平均線,有點像「Weighted Moving Average,WMA」,但HMA的滯後情況會較WMA少。 我們試試加上一些新的條件,買入時必需HMA比上一支陰陽燭的HMA為高,造淡時則必需HMA比上一支陰陽燭的HMA為低。 以下便是修改後的版本,在主圖上的紅線便是HMA。 //@version=5 strategy("升穿bollinger's band 改良版", overlay=true, margin_long=100, margin_short=100) sma20=ta.sma(close,20) mult=ta.stdev(close,20) upper=sma20+2*mult lower=sma20-2*mult noposition=strategy.position_size==0 var bool traded =false hmaValue=ta.hma(close,10) buyCond=ta.crossover(close,lower) and close<sma20 shortCond=ta.crossunder(close,upper) and close>sma20 buycloseCond=ta.crossover(close,sma20) shortcloseCond=ta.crossunder(close,sma20) buyCond2=hmaValue>hmaValue[1] shortCond2=hmaValue<hmaValue[1] if buyCond and noposition and not traded and buyCond2 strategy.entry("BUY",strategy.long) traded:=true if buycloseCond and not noposition strategy.close("BUY") if shortCond and noposition and not traded and shortCond2 strategy.entry("SHORT",strategy.short) traded:=true if shortcloseCond and not noposition strategy.close("SHORT") if ta.change(time("D"))!=0 traded:=false plot(hmaValue,title="HMA",color=color.red,linewidth=1) 結果可以看到訊號大幅減少,但勝率再提升至75%,但看最大獲利的最大虧損的比例達到3:1,這類策略即使遇上最壞的情況也是虧損有限。但問題就是交易次數真的真的太小的,一年只有8次入市機會,雖然獲利的次數有6次,但交易次數太小也會令最終的回報有限。 但大家可以想想,若你把「10個」本來只有六成中的交易策略,修改至七成中以上,而且Maximum Drawdown不大,盈虧比更大幅提升至3比1,那麼你獲利的機會根本便很大。 然後我們這10個策略同時執行,那你的交易次數就不會少了,而回報也會因而增加。若為了達到有足夠多交易次數的目的而勉強去運用一些勝率較低,盈虧比又較低的交易策略,那最終的回報反而不會太好。 網頁: www.quants.hk Youtube: https://www.youtube.com/@markchunwai Facebook專頁: https://www.facebook.com/quantshk/ Patreon: https://www.patreon.com/quantshk

Docker Image打包建議

科技新知
MacauYeah・2024-07-10

之前筆者有分享過兩個不同的Docker Image打包方式 App直接打包成Image 只把底層程式打包在Image中(例如Tomcat),再用Docker Volume的方式讓Container可以起動App。 筆者就兩種方式做了一個條列式的對比。詳見連結 https://macauyeah.github.io/AProgrammerPrepares/VMDockerNotes/DeployDockerClusterCN.html 因為兩種方式筆者都有實作過,也算用了很段時間,所以也有一些實際經驗可以分享。 如果大家上正式的Docker課程,Docker導師通常會推薦為每個App打包成獨立Image,因為底層程式的Overhead通常不大,例如底層程式是Tomcat、Apache、Nignx這類網頁伺服器,重量級的開銷並不是因為多幾個Web Engine的分身造成,通常都是因為業務本身。但如果你講的底層程式是資料庫等的大型程式,才可能會有明顯的差異。 但實務上的建議,就是必需考慮自身的經驗,到底那個方案自己比較有把握。獨立打包App,在正式環境也需要考慮跟蹤問題的情況,多個不同App要溝通,也是了解Container網絡。如果打包底層程式,所有App都可以當成是本機下運行,更有信心追蹤問題,也是一個很好的出發點,到了有需要彈性改變不同App的需要,才轉向獨立打包的做法。 筆者最初也是走這個打包底層程式的方向,到了自己有信心試用Docker Swarm,才走向獨立打包的做法。筆者親身經驗,因為到了Docker Swarm,網段會變得暴增,這跟公司現有的內部網絡相衝的機會就會變多。在Swarm起立初時,筆者並沒有意識到這件事,所以當初排查問題,也花了一些時間才知道要向網段衝突上著手。 另一個出自Docker導師實務上的建議,就是正式環境中不要做用Docker compose,應該使用Docker Swarm。那怕Swarm只有一個節點,也應該用Swarm,導師的主要理據是Swarm有Rolling Update (滾動更新)的機制。同一個node也可以有多個分身,每個分身輪流更新,就不會出現大中斷的情況。筆者就自身經驗,Tomcat可以同時容納一個App的多個版本,Nginx也有Failover(故障轉移)等,如果你很熟這些功能,不一定要需要靠Swarm去提供。可以按自己步調去慢慢適應。

型別對程式語言的重要性

科技新知
MacauYeah・2024-07-08

JavaScript等程式語言的流行,好大一個原因是因為它很簡潔。而筆者認為,動態語言的特性,即是可以省略型別,是讓它簡潔的一個很大原因。(動態、靜態與強型別、弱型別並一定對等,詳見Ref) 動態語言的特性,就是同一個變數,在不同時候可能代表不同的數據類型,有時候是String,有時候是Integer。所以編寫時,乾脆就不寫數據類型,因為寫了也可能是白寫。 因此初學者並不需要處理大多導入(import)問題,也不用考慮很多compile error問題,至少程式可以運行一半,到了最後出錯的地方才停下,也就是不會因為型別問題而整個程式開不了。 不過筆者在接觸了JavaScript後,始終沒有大量使用。一來因為筆者慣用的Java,有著更大的基礎套件,改用JavaScript未必有優勢。而且動態語言還有一個長久的管理問題,我們該如何知道更新的影響有多大? 測試用例不是萬能藥 有一部份的人認為,動態語言管理難,是因為大家不愛寫測試用例。的確,若然大家寫的測試覆蓋率足夠多,一定可以預先發現問題。但筆者在Java上實踐了寫測試的習慣一段時間,依賴測試報錯,其實也是後知後覺。 IDE的界入 筆者認為,若想好好地管理程式碼,光寫測試是不夠的,我們還需要好好地讓IDE了解我們的程式碼,認它可以很有效地重構我們的程式碼。更強的IDE,還有機會可以提醒我們有一些設計上問題。 老實講,寫Java多的朋友,都可能都知道Intellij Ultimate的名字。筆者試用後,的確很有幫助。相較之下,vscode對於Java的支援,並不十分智能。但這裏筆者還覺得vscode對於java的編寫、重構、測試,在免費的情況下,都已經足夠是足夠佛心。對於網頁應用來講,vscode差的是對javascript的支援。 vscode對javascript的支援有限,其實不能怪它不夠努力。你想多一個免費的IDE怎樣去了解你的javascript程式? 我們連型別都沒有寫出來,它能怎樣推敲? 實時去模擬各種輸入?CPU又會不會耗乾?那麼寫到一半的程式碼又怎樣輸入? 直到最近筆者採用TypeScript之後,筆者看到曙光了 TypeScript - 一個變相的JavaScript的靜態型別 原本的JavaScript其實也有型別的,只是不強制。若想IDE支援,需要以特定型式寫註解。但這樣寫註解,工作量並不比引用靜態型别來得輕鬆。所以最後,筆者還是覺得直接套用TypeScript,讓自己在每一次引用參數,都要好好地先了解函數的輸入輸出型別寫法。 說實在,從JavaScript到TypeScript並不輕鬆。一些原本很無腦的Axios, Promise, Vue語句,TypeScript寫起上來,都變得很複雜。但這個套用,對於IDE來講,真的很大幫忙。它就像突然讀懂了我們的程式一樣,可以跳入跳出,可以知道在多少處被引用。重構也變得更有信心,而不是等待事後測試報錯。 有一點要補充,TypeScript並不像Java那般需要完全預先宣告型別。例如函數的回傳結果,TypeScript就不會強制要求寫出型別,因為它可以有限度地猜得出來。當然,如果大家願意宣告,就更好。 總結 總括來講,型別就像厠所的衛生情況一樣。初期當然什麼都不處理也可以,但越用越久也沒有人理會,大家也不想用下去。若然大家都願意努力維持它的品質,大家會更有意願重複使用。 參考資訊 「靜態型別 vs. 動態型別」與「強型別 vs. 弱型別」 https://blog.tarswork.com/post/programming-language-type-system Typed JavaScript https://depth-first.com/articles/2021/11/03/typed-javascript/

Spring Boot 05 - 為 http json api 加入登入要求

科技新知
MacauYeah・2024-07-02

本節,我們將為之前的http服務,加入認證機制,只有在資料庫現存的用戶可以登入及訪問我們的json api。 下戴模版 慣例,我們用Spring Initializr (Maven) 下載模版,Dependency主要選擇 Spring Web Spring Boot DevTools Spring Security Controller 跟上節一樣,我們起一個Controller,為簡化測試,我們只做http GET api。 由於本blog對於Source Code的顯示不太友好,有需要看source code的,請到Github查看 //src/main/java/io/github/macauyeah/springboot/tutorial/springbootwebapidata/controller/HomeController.java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class HomeController { @GetMapping("/someRecord/{uuid}") public Map readSomeRecord(@PathVariable String uuid) { return Map.of("ret", "your uuid:" + uuid); } } 準備我們的test case,但這次我們預期它應該要出現登入失敗的結果。 //src/test/java/io/github/macauyeah/springboot/tutorial/springbootwebapidata/controller/HomeControllerTest.java @SpringBootTest @AutoConfigureMockMvc public class HomeControllerTest { @Autowired private MockMvc mockMvc; @Test void testNoLogin() throws Exception { RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/api/someRecord/1234") .contentType(MediaType.APPLICATION_JSON); this.mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().is4xxClientError()) .andExpect(MockMvcResultMatchers.jsonPath("$.ret").doesNotExist()) .andDo(MockMvcResultHandlers.print()); } } 在我們執行上述的測試,test case 成功過了。我們的基本設定跟上一節其實沒有多大改動,為何現在http api會回傳狀態 401? 那是因為我們在依賴中加了,Spring Security,它配合了Spring Web,就會自動為所有api加入權限檢測。我們的測試中,沒有任何用戶登入,當然會出現 http 401。為了讓我們可以好好管理誰可以使用api,我們就來設定一定Security。 我們加一個WebSecurityConfig.java,暫時指定所有的訪問路徑都必需有USER權限,並且用 http basic的方式登入。 //src/main/java/io/github/macauyeah/springboot/tutorial/springbootwebapidata/config/WebSecurityConfig.java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authorizeHttpRequests -> { authorizeHttpRequests.requestMatchers("/**").hasRole("USER"); // 所有的訪問路徑都必需有USER權限 }); http.httpBasic(Customizer.withDefaults()); // 使用http basic作為登入認證的方式 return http.build(); } } 上述例子,只是擋了沒有權限的人,我們還需要讓有登入身份的用戶可以成得取限User權限。 我們繼續修改,WebSecurityConfig,加入只在記憶體有效的InMemoryUser import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; public class WebSecurityConfig { //.. @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // 我們的密碼不應該明文儲,比較保險,我們使用BCrypt演算法,為密碼做單向加密。 } @Bean public UserDetailsService userDetailsService() { UserDetails user = User.withUsername("admin") .password(passwordEncoder().encode("pass")) .roles("USER").build(); // 我們在記憶中體,加入一個測試用的User,它的名字為admin,密碼為pass,權限為User return new InMemoryUserDetailsManager(user); } 然後加入新的測試,直接模擬Role。結果是通過的。 //src/test/java/io/github/macauyeah/springboot/tutorial/springbootwebapidata/controller/HomeControllerTest.java @Test void testLoginWithRoles() throws Exception { RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/api/someRecord/1234") .contentType(MediaType.APPLICATION_JSON).with( SecurityMockMvcRequestPostProcessors.user("someone") .roles("USER", "ADMIN")); // 沒有使用密碼,只使用Role this.mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()) .andExpect(MockMvcResultMatchers.jsonPath("$.ret").value("your uuid:1234")) .andDo(MockMvcResultHandlers.print()); } 再來一個測試,改用密碼登入,分別輸入錯的和正確的密碼。 @Test void testLoginWithWrongPasswordAndNoRole() throws Exception { RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/api/someRecord/1234") .header("Authorization", "Basic randompass") // 輸入錯的密碼,應該回傳http 401 Unauthorized .contentType(MediaType.APPLICATION_JSON); this.mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().is4xxClientError()) .andDo(MockMvcResultHandlers.print()); } @Test void testLoginWithPassword() throws Exception { RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/api/someRecord/1234") .header("Authorization", "Basic YWRtaW46cGFzcw==") // http basic 就是把 admin:pass 轉成base64 .contentType(MediaType.APPLICATION_JSON); this.mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()) .andExpect(MockMvcResultMatchers.jsonPath("$.ret").value("your uuid:1234")) .andDo(MockMvcResultHandlers.print()); } 最後,當然是正確的密碼才能通過。若果大家還是半信半疑,我們可以跑起真的正服務(IDE RUN或mvn spring-boot:run),然後用curl去試。 curl http://localhost:8080/api/someRecord/1234 // failed with 401 curl -u "admin:pass" http://localhost:8080/api/someRecord/1234 // successed 使用SQL Database讀取用戶登入資訊 一般而言,我們不可能把所有用戶登資訊打在InMemoryUser中,通常背後有一個資料庫儲存所有的用戶資訊,我們在登入時,讀取它來做對比檢證。 為此,我們在maven中,加入 Spring Data JPA h2 database (或任何你的資料庫,如mysql 、 sql server) 最後一步,我們把InMemoryUser去掉,改為從資料庫讀取。因為原始碼太多,就不全部貼上。最主要的是WebSecurityConfig.java要關掉之前的UserDetailsService,改為提供一個UserServiceImpl類,它會實現UserDetailsService的功能。 @Configuration @EnableWebSecurity public class WebSecurityConfig { // 把原來的Bean先變成註解,其他不變 // @Bean // public UserDetailsService userDetailsService() { // UserDetails user = User.withUsername("admin") // .password(passwordEncoder().encode("pass")) // .roles("USER").build(); // return new InMemoryUserDetailsManager(user); // } } // spring-boot-tutorial/spring-boot-web-api-data/src/main/java/io/github/macauyeah/springboot/tutorial/springbootwebapidata/config/UserServiceImpl.java // other import import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; @Service public class UserServiceImpl implements UserDetailsService { @Autowired PasswordEncoder passwordEncoder; @Autowired UserRepo userRepo; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 因為我們資料庫沒有資料,為了方便測試密碼的加密,我們在java code上直接插入一筆資料。 UserEntity defaultUser = new UserEntity(); defaultUser.setUsername("admin"); defaultUser.setPassword(passwordEncoder.encode("pass")); defaultUser.setRole("USER"); defaultUser.setUuid(UUID.randomUUID().toString()); userRepo.save(defaultUser); // 上述為測試用插入資料,不應該出現在正式使用環境中。 UserEntity user = userRepo.findOneByUsername(username) .orElseThrow(() -> new UsernameNotFoundException(username + " not found")); // 找找資料庫有沒有正在登入的該名使用者username List authorities = List.of(new SimpleGrantedAuthority("ROLE_" + user.getRole())); LOG.debug("got user uuid:{}, username:{}, role:{} from database", user.getUuid(), username, user.getRole()); // 如果前面的 findOneByUsername 有結果回傳,我們就給它一個ROLE_XXX的權限。 return new User(username, user.getPassword(), authorities); // 這裏從沒有檢查過密碼是否有匹配,全部交給Spring Security去做 } } //spring-boot-tutorial/spring-boot-web-api-data/src/main/java/io/github/macauyeah/springboot/tutorial/springbootwebapidata/entity/UserEntity.java // spring-boot-tutorial/spring-boot-web-api-data/src/main/java/io/github/macauyeah/springboot/tutorial/springbootwebapidata/repo/UserRepo.java 上述段落中,筆者省略了UserEntity和UserRepo,它們只是一般的spring-data-jpa概念,有需要可以經文末的連結查看完全原始碼。最需要注意的,是UserEntity的password欄位,在資料庫中是以加密的方式儲存。我們在配匹登入者與資料庫記錄時,也沒有自行檢驗密碼的需要。我們只是在加密過的密碼回傳給Spring Security,Spring框架會自行把登入者輸入的密碼與加密了的密碼作比較。

2024年7月1日-7月7日

玄學星相
熊神進・2024-06-30

一周生肖运程预测 鼠:今个星期事业可能会有转变,所以属鼠的朋友应该主动求变去增旺自己的运势;你可以在工作之余的时间找一些新东西学习,又或者尝试其它的工作;对于职位已经不甚稳定的朋友更要加倍留意,主动求变可令你的运势增强。财运亨通,你努力完成订下的业务目标,只要保持善念及正能力,业务发展自必有神助。身体很健康,心灵也很快乐。 牛:有五黄煞入命,同时比肩之日,气场不吉,需要注意休息和睡眠,避免身体产生疾病或者血光之灾,今个星期不适宜外出旅游或者游山玩水,最好能避开危险性质的工作或者项目,安全为上,不可冒险。财运方面,容易有好坏两个情况,投资需要谨慎为上。为家庭、亲人或房子牵挂,油漆美化、修缮收纳、社交联谊破费,所得变动不大。爱情是单身公害恐惧症。 虎:本周应该多出去旅游或者是走动,这样也有助于提升个人的运势。桃花运不错,随着交际圈扩大而认识更多优秀的异性,宜把握住机会争取脱单。已婚或已有伴侣的你要异性保持距离,以免不必要的肢体亲近接触,令你的伴侣感到不好受。你对工作一丝不苟,务实而真诚,让客户对你留下良好印象。放工后应多留在家里休息,拥有更佳精神状态方能使工作顺利。 兔:运势再度攀升,受到「福德」的加持,各项运势都有极强的表现,尤其在感情运势上。如身体不健康的朋友,建议请一件「化病葫芦」开运吉祥物助身,放在床头以保平安。今星期的财运方面体现在生活上,小心跟人发生一些碰撞,有失财的可能,要多留意随身物品。幸运号码是1, 11号。 龙:本星期有凶星入命宫,可能会有一些突然发生的状况,避免受伤,需要多多注意,做事之前也要谨慎小心。如果去郊外, 请穿长裤保护自己。感情运中无桃花,夫妻不冲不克,不会影响双方之间的关系。只要相互凉解,避免发生纠纷,无需再自寻烦恼,未婚属龙人士尽量争取,建议摆放「心意雨花石」开运吉祥物,旺婚姻招桃花,有利于增强感情。 蛇:未来两三个星期虽仍有挫折,但已渐能降低损害,找出有利突破点,并一一兑现承诺。中长期远景看好,值得为自己一路捱过来而感到自豪。爱情是一种幸福交易。表面看上去财运似乎不错,但不义之财千万不能取,凡事小心谨慎,因为多半会因财招灾,一不小心就会有法律方面的麻烦出现,甚至可能会招致牢狱官灾。 马:今个星期会有自寻烦恼,自作自受的情况。故遇到问题或困难时,不要钻牛角尖,多向专家及有能力的请教,胜过自己独坐苦思。2002年出生的生肖马尤其需要调和心绪,避免因为个人原因而耽误一切工作事宜的进展,拖团队后腿,也会让你的上司对你有不满,阻碍事业发展。幸运数字是0,2号。晚上如果有空,请做一次烧供。 羊:今个星期抵抗力会比较弱,所以病痛自然会多,但如果平日健康没有太大问题的话只需要注意养生之道就可以了。对于单身人士,有机会展现自己的魅力,在团体活动、或朋友的撮合引介下,可以促进你的机缘。能够认识交心对象时,也要懂得把握时机,若能够彼此真心对待,也将会成就良好姻缘。 猴:工作、事业运势较差,会有人、事上的纷争,应息事宁人,以免官司临身。还有就是人际关系方面,要注意口舌之争,不能轻易和别人发生争吵,会影响到你们的情绪。要注意饮食健康以及少抽烟喝酒,多到空气好的地方旅游这样可以有一个好的身体来开创你的新的征程。幸运吉祥物是门冲石。 鸡:由于本周桃花太旺,应该注意感情纠葛而出现三角恋爱之关系而导致感情破裂家庭不稳之事情。要注意保重身体。小心因为感情问题惹祸上身。健康方面,因有「伏尸星」出现,故此属鸡的人必须密切注意身体健康,并要小必留意家居安全,请记住平安二字值千金。吉利方位:西南方、正东方、西北方。 狗:因命宫中有众多的吉星拱照,在运程上会大有起色。有桃花星及红鸾星入命,故未婚者会有机遇,已婚者则要小心谨慎,约束自己的言行了。本周要特别注意交通安全,如有不慎易引发血光之灾。感情方面矛盾重重,应互相体谅包容。对于男孩子家长要看护好眼睛,多吃温性食物,电视、电脑等伤眼的电器最好让孩子少玩。感情吉位:东南、正西。 猪:在这个星期的压力会很大,这会让你们的心情变得焦躁不安,从而老是想要向别人发泄你们的脾气,这就会让你们的家人朋友感到困恼,你们的朋友也会慢慢疏远你们,对你们的人际关系会有很大的影响,所以你们一定要注意调节你们的压力,不能够无形给自己太大的压力,可以的话你们和家人一起出去旅游多看看大自然,能够让你们能够保持愉悦的心情。

玛瑙手串深藏不露的小秘密

宗教玄學
熊神進・2024-06-25

#为什么女生喜欢佩带玛瑙手串 一位网红留言给笔者,她说自从请了笔者的开光玛瑙手串,她接了很多大客户,生意翻倍,笔者请她多些做烟供,布施三恶道。 笔者留意到在国内很多年青姑娘都喜欢佩带玛瑙手串,尤其是刚刚大学毕业的女生,有一次我在北京大学工作时,发现有几位女学生,她们手上佩带了玛瑙手串。 玛瑙手串的价格不贵,一条原石手串,目前是300元以下,这是一个大众可以接受的价格。玛瑙是地球上最常见的矿石,人们早在3000多年前就发现它的存在,可是,厉害的商人在「阿拉善玛瑙」(几十块元一斤)原石的表面进行人工染色来提升鲜艳,有些更将多个「阿拉善玛瑙原石」粘合在一起,制造成较大的形态和斑块,从而提高卖价。 在风水师傅的开光过程中,人工打磨的石太过光滑,我们很难把玄粉浸入石髓, 失去效果。 自古以来,风水师傅便把玛瑙视为宝石中的“第三眼”,象征着「希望」。一条貌不惊人的玛瑙(暗红/暗黑)可改善内分泌,加强血液循环。 大家知不知道为什么大学毕业后,我们做第一份工总是有些不愉快,理由是什么,很简单,就是年轻入世未深,喜欢表现自我,日子久了他/她们才知道七分人事三分工作的道理。我常常建议他/她们佩带玛瑙手串,第一是因为价格大众化,不是很贵;第二是因为它可以产生欢笑正能量,减少我们对工作的不满。 很多年青人喜欢「阿拉善玛瑙」,理由是他/她们被七彩色素迷惑,我认为原生态的玛瑙原石才是符合年轻人的需要,它可以平顺急躁情绪,走上成功之路。 笔者读过水晶检测课程,对于玛瑙手串是有要求的,在巴西工作的时间,我拣选的原石必须含有二氧化硅的水(熔岩本身中的硅酸盐成分分解所产生),因为这种岩石中的蒸气空腔可以给我把元气打进去,形成更大的空腔,产生正能量。 目前这些手串珠子是10mm左右,适宜体重55至65公斤女生佩带,可以佩带左手,亦可以放在包包里,我们建议回家脱下。 玛瑙手串是给女生正能量,如果妳想爱情美满,工作顺利,妳可以佩带这款。

玛瑙手串深藏不露的小秘密

玄學星相
熊神進・2024-06-25

#为什么女生喜欢佩带玛瑙手串 一位网红留言给笔者,她说自从请了笔者的开光玛瑙手串,她接了很多大客户,生意翻倍,笔者请她多些做烟供,布施三恶道。 笔者留意到在国内很多年青姑娘都喜欢佩带玛瑙手串,尤其是刚刚大学毕业的女生,有一次我在北京大学工作时,发现有几位女学生,她们手上佩带了玛瑙手串。 玛瑙手串的价格不贵,一条原石手串,目前是300元以下,这是一个大众可以接受的价格。玛瑙是地球上最常见的矿石,人们早在3000多年前就发现它的存在,可是,厉害的商人在「阿拉善玛瑙」(几十块元一斤)原石的表面进行人工染色来提升鲜艳,有些更将多个「阿拉善玛瑙原石」粘合在一起,制造成较大的形态和斑块,从而提高卖价。 在风水师傅的开光过程中,人工打磨的石太过光滑,我们很难把玄粉浸入石髓, 失去效果。 自古以来,风水师傅便把玛瑙视为宝石中的“第三眼”,象征着「希望」。一条貌不惊人的玛瑙(暗红/暗黑)可改善内分泌,加强血液循环。 大家知不知道为什么大学毕业后,我们做第一份工总是有些不愉快,理由是什么,很简单,就是年轻入世未深,喜欢表现自我,日子久了他/她们才知道七分人事三分工作的道理。我常常建议他/她们佩带玛瑙手串,第一是因为价格大众化,不是很贵;第二是因为它可以产生欢笑正能量,减少我们对工作的不满。 很多年青人喜欢「阿拉善玛瑙」,理由是他/她们被七彩色素迷惑,我认为原生态的玛瑙原石才是符合年轻人的需要,它可以平顺急躁情绪,走上成功之路。 笔者读过水晶检测课程,对于玛瑙手串是有要求的,在巴西工作的时间,我拣选的原石必须含有二氧化硅的水(熔岩本身中的硅酸盐成分分解所产生),因为这种岩石中的蒸气空腔可以给我把元气打进去,形成更大的空腔,产生正能量。 目前这些手串珠子是10mm左右,适宜体重55至65公斤女生佩带,可以佩带左手,亦可以放在包包里,我们建议回家脱下。 玛瑙手串是给女生正能量,如果妳想爱情美满,工作顺利,妳可以佩带这款。

玛瑙手串深藏不露的小秘密

宗教玄學
熊神進・2024-06-25

#为什么女生喜欢佩带玛瑙手串 一位网红留言给笔者,她说自从请了笔者的开光玛瑙手串,她接了很多大客户,生意翻倍,笔者请她多些做烟供,布施三恶道。 笔者留意到在国内很多年青姑娘都喜欢佩带玛瑙手串,尤其是刚刚大学毕业的女生,有一次我在北京大学工作时,发现有几位女学生,她们手上佩带了玛瑙手串。 玛瑙手串的价格不贵,一条原石手串,目前是300元以下,这是一个大众可以接受的价格。玛瑙是地球上最常见的矿石,人们早在3000多年前就发现它的存在,可是,厉害的商人在「阿拉善玛瑙」(几十块元一斤)原石的表面进行人工染色来提升鲜艳,有些更将多个「阿拉善玛瑙原石」粘合在一起,制造成较大的形态和斑块,从而提高卖价。 在风水师傅的开光过程中,人工打磨的石太过光滑,我们很难把玄粉浸入石髓, 失去效果。 自古以来,风水师傅便把玛瑙视为宝石中的“第三眼”,象征着「希望」。一条貌不惊人的玛瑙(暗红/暗黑)可改善内分泌,加强血液循环。 大家知不知道为什么大学毕业后,我们做第一份工总是有些不愉快,理由是什么,很简单,就是年轻入世未深,喜欢表现自我,日子久了他/她们才知道七分人事三分工作的道理。我常常建议他/她们佩带玛瑙手串,第一是因为价格大众化,不是很贵;第二是因为它可以产生欢笑正能量,减少我们对工作的不满。 很多年青人喜欢「阿拉善玛瑙」,理由是他/她们被七彩色素迷惑,我认为原生态的玛瑙原石才是符合年轻人的需要,它可以平顺急躁情绪,走上成功之路。 笔者读过水晶检测课程,对于玛瑙手串是有要求的,在巴西工作的时间,我拣选的原石必须含有二氧化硅的水(熔岩本身中的硅酸盐成分分解所产生),因为这种岩石中的蒸气空腔可以给我把元气打进去,形成更大的空腔,产生正能量。 目前这些手串珠子是10mm左右,适宜体重55至65公斤女生佩带,可以佩带左手,亦可以放在包包里,我们建议回家脱下。 玛瑙手串是给女生正能量,如果妳想爱情美满,工作顺利,妳可以佩带这款。

《魔物獵人世界》《魔物獵人崛起》通用心得

手機‧電玩
MacauYeah・2024-06-25

好久沒有寫Game心得分享,那是因為筆者也真的很久沒有開新坑。最近因為《魔物獵人》有新作預告,大家又好好地重新把《魔物獵人世界》拿出來練練手。筆者也順便把過去的買了沒有怎玩的《冰原》DLC拿出來,好好地玩一遍,總算完了一個心願。 因為玩了兩款近作,對於魔物獵人系統多少有一點入門心得,就來梳理一下,方便新朋友入坑時不再碰壁。 防具 隨著主線推進,不單武器可以強化,防具也可以繼續強化。強化等級因為階段推進而有突破。筆者以前就是不知道這件事,前期以為防具早已升滿,但後來一直貓車,才知道防具防禦力太低,需要經過重複強化提高防禦力。 防禦、體力增幅技能 防禦力、體力最大值兩者當然是越高越好。但在推進度前期,什至後期因為需要額外技能,防禦、體力不一定全滿。有必要時防禦增幅時選一半,體力增幅點滿比較好。因為後期挨怪物一套連招,即使防禦多高,也不能不回血。而體力增幅通常較易點滿,避免因為異常狀態影響而被連招到死,可以增加容錯率。而且後期回血道具一般直接使用秘藥,一次過回滿血條。所以筆者認為體力增幅比防禦增幅更有效。 防禦力疊加 若果你像筆者一樣菜,後期必需要使用各種方式疊加防禦力。主要方式有三個。食貓飯、道具持有、道具使用。貓飯在《魔物獵人世界》和《魔物獵人崛起》,都有機率成份,不過好在後期,總有票卷可以提升機率,筆者都偏向把投資在防禦力上。道具持有,主要在XX之爪和XX護符之上,帶著出戰就好,兩者最多各帶一份,但可以疊加。道具使用,就是硬化藥、硬化粉塵、忍耐種子。三種可以同時使用,可疊加,但硬化粉塵、忍耐種子有時效。 精靈加護 有一定機率減少傷害,但筆者後期不夠技能欄位,沒有去配。它也是增加容錯的機率,但防禦、體力、精靈加護全要的話,攻擊技能就更少了。所以筆者放棄它。 龍車 中後期,魔物都會四處跑,發怒時更是撞來撞去,總之就是讓你打空氣。筆者初時也不知道怎樣對策,總是跟著魔物屁股走。但其實這樣更費時,其實你可以原地等待,有空就調合或為武器上Buff,反正它很快就會回頭。想更有效的提升DPS,應該花時間去量度回來時的落點。魔物從遠處過來,但其實中途很少變向,比較有條件預判和輸出。若為團戰,因為有隊友分散仇恨,才需要主動追上魔物。 打點 初接觸這系列時,筆者就不斷看到【肉質】這一詞,但其實玩到現在,筆者都不太掌握。但通常都是集中打魔物的頭就了事。當打頭都出現彈刀時,再攻擊其他地方。有條件有心情,可以逐隻魔物研究,長期打到有效位置,傷害差很多,才能會易出現魔物倒地的狀態。 配裝 別人的配裝,其實自己並不一定能用,特別是那些TA影片(Time Attack)。MH系統的技能取得,都很有運氣成份,所以想要的技能不一定馬上能配到,大家主要去找自己武器的核心技能就OK。有些武器可能有多個流派,而且隨著時間累積,素材的隨機出現,我們有必要定期整理裝備組合。不同攻略網站,都會分享畢業裝,不過筆者到現在,都未去到這個攻守兼備的狀態。還是忘提畢業裝吧,後期的強怪,還是要針對性地重點挑整。 救援、團戰 有時卡關,有條件就叫救援吧,不必刻意自己打。因為進度限制,主線沒有推進的話,強化功能不開放,死磨也不一定有裝備提升。來救援的人一般是已推完主線,回來刷素材的,他們有較高的防禦去吸收傷害。他們不一定比你強,但貓的機率就比較低。但也不排除有時人沒打完主線就來抱大腿,結果搭沉船一起貓車。所以一定一定要準備【生命粉塵】,有必要時,就為殘血的同伴回血。那怕團友很強,也有機會出現異常狀態疊加的情況,為他回個血,他能更加集中輸出。 主機版都要買額外的會員制,才能聯機求助。PC版就沒有這個限制,接通網路就可以招外援。筆者推薦PC版,因為會員制的其他贈品都很雞肋,還不如PC版來得清靜。 以上,就是筆者玩完《魔物獵人世界》和《魔物獵人掘起》兩作的心得,雖然筆者還是很菜,但至少後期也玩得下去。希望以上各點可以為沒通關的朋友帶來實際的幫助。

找到了法螺,一切好开始

宗教玄學
熊神進・2024-06-24

相傳釋迦牟尼誕生時,天人獻下八寶。藏語把佛門八寶稱「扎西達杰,亦叫達杰明蘇」,而我們淨土宗稱「右旋白海螺」,南方地區大部份信衆稱它「法螺」,而風水師傅叫它做「鎮宅法寶」。 古代的電影都常常看到男武士吹「法螺」, 其實「法螺」有三個明顯用途: 法螺用來召集僧衆。 古人在深山修行時,爲避免猛獸追逐, 可吹起「法螺」驅散野獸。 法螺可以呼喚天神,《千手千眼觀世音菩薩廣大圓滿無礙大悲心陀羅尼經》有一句話說:“若爲召呼一切諸天神,當用寶螺手。” 法螺降伏邪魔外道。 在風水學上, 筆者還發現「法螺」可以幫助男女解脫感情煩惱, 因爲「右旋白海螺」是佛的法音,其力量可以廣大悠揚,使一切煩惱人得到解脫。紅塵世界, 男女之間的感情總沒有對與錯, 在筆者眼中, 男女的愛都是債, 你不是前世欠了他, 今生又爲何會遇上?很多女生(已婚,未婚)都會把「法螺」放在家裏, 就是爲瞭解脫俗世煩惱。 「右旋白海螺」是佛教中最重要的法器之一,我們都是把它用來供養上師、三寶。在北方地區, 信衆會把它放在家裏、壇上。在西藏,最受尊崇,被視爲三千世界回蕩不息的聲音。在南方,有些女生把它放臥室,用來趕走「爛桃花」。 「右旋白海螺」是一種佛相, 這是因爲佛陀說法之音好像法螺般美妙,佛陀臍紋以及所有的毛髮都是右旋,眉間衪的白毫右旋而威嚴,這就是佛教說的右旋白螺之相。 據佛經記載:聞法螺聲可滅諸重罪。《不空罥索神變真言經》中說:“若加持螺,諸高處望,大聲吹之,四生之衆生,聞螺聲滅諸重罪,能受身舍已,等生天上。”這段話告訴我們,當你犯了大罪,你供養「右旋白海螺」, 你可以减罪。什麽是大罪,一位女生做了「打胎」「人流」,這就是大罪。 「右旋白海螺」的圖案有幾款, 款款都是佛教人士的心血結晶, 筆者熊神進一直把它們放在尼泊爾精舍, 白色是海螺殼, 銀色是尼泊爾銀的佛吉祥圖案, 手工認真, 由于供養久長,又是天然的,總有色差, 如果追求完美的讀者, 不應該考慮擁有。 人生總有起落, 如果遇到事業不順, 婚姻不好, 健康出問題的日子, 我們可以拿起「右旋白海螺」向西方吹幾口, 在玄學上有個說法: 一吹順風順水; 二吹時來運到; 三吹心想好事用。 佛有八寶添吉祥, 你家中有多少件寶?

github flow - github 開發流程

科技新知
MacauYeah・2024-06-20

那些年那個很穩定卻又不受歡迎的 git flow 開發流程 多年前,朋友就向筆者介紹git的團隊整操作流程。筆者深思過後,的確實用,那些年的git-flow,很美滿,由開發、測試,到發佈、修補漏動(backport),都有清楚明確的指引。 原作者連結:git-flow 大家如果沒有更複雜的需求,真的可以照搬,筆者也很推這一個模型。 但在長期推廣下,筆者發現大部份人其實都不熟git的基本操作,什至連git graph也不看,現在看git flow,就更不可能接受。那怕是有常用git的個人團隊,也是不怎使用分支模型。 前一兩年,筆者也不懂,筆者也努力地簡化git flow。例如把master和develop合而為一,但最後也是少有人可以接受,很多人還是卡在分支那邊,對checkout、merge還是很陌生。在跟更多不同人的協作過後,筆者總於意會到一件事。其實大部份人,只想知道最後、最新的狀態,只會更新 master / main ,也因為個人開發,所以連衝突也不會有,更不需要使用merge。那怕是少型團隊,頂多也是維護main的衝突,間中用用merge,而checkout還是用不著。 其實這個情況,並不限於小型團隊。因為 web app 和 DevOps 的流行,所以越來越少機會要維護多個舊的穩定版本。大家都專心於最後一個開發及發佈版本就完事,用戶的某個版本有問題?更新到最新版本吧。(註:越底層的應用開發模式,因為相容性問題,不可能只保留一個穩定版本。) 那麼我們就大力簡化吧 - github flow 開發流程 既然大部份情況,大家都只在乎 main / master / 預設分支,那我們也沒有必要跟著複雜的 git flow 走。但在 DevOps 的角度下,為保證 main / master 穩定性,大家還是至少要遵守branching 、pull (merge) request 、code review 、auto test 原則 。 github就最簡單的branching 、pull request 、code review 提出了它們的 github flow。 簡而言之,就是每個人在開發時,都先從 main 起一個新分支,不斷更新。待合適的時候,就透過 pull requst,向原項目負責人提出申請,只要項目負責人點頭,就可以把改動傳入 main 中。又因為Github 原本的定位在於個人與個人之間的協作,初時已經需要通過fork建立獨立的倉庫,那怕你不愛分支也必需分支。所以 pull request,code review 的作用更明顯,後逐的協作更理所當然。 但若果回到公司團隊協,Github flow 就應該像筆者之前提出協作方案,各自起分支,最後由某個人守門,把所有結果放到 main 中。(前文連結)