智慧電動機車與電池交換站體系並進,Gogoro 打造「平民特斯拉」

  台灣新創企業 Gogoro 挾著創辦人陸學森、主要投資人尹衍樑,以及 1.5 億美元募資總額,吸引了產業界的注意。Gogoro 揭櫫智慧城市與智慧能源的大方向,但主要專利卻集中在電動機車相關領域,電動機車與智慧城市的連結乍看似乎有些牽強,使得產業界相當好奇 Gogoro 為何研發機車卻言必稱智慧城市?   至 CES 2015,謎底揭曉,Gogoro 一方面展出設計與功能上均讓人驚豔的智慧電動機車,但更重要的戰略性意義,則在於搭配機車的電池交換站體系之上,電池交換站體系不僅作為電動機車的後勤系統,更可望藉由機車位入口,打進未來智慧城市基礎建設的重要領域。   Gogoro 先前對其產品三緘其口,頗有蘋果飢餓行銷策略之風範,也成功的引起媒體關注與好奇,CES 2015 上正式發表產品後,國內外多家科技媒體均第一時間報導,一時成為產業新聞熱門焦點,Gogoro 此次產品發表,較引人注目的主體是智慧電動機車,但實際上,Gogoro 可說是同時發表兩種產品,電動機車只是其一,電動機車的電池交換站則可說是另一個關鍵產品,兩者的戰略性一樣重要。  
 

   
革新電動車充電   Gogoro 電池交換站「GoStation」,系統設計風格相當簡潔,大小約如同一般的 ATM 機台,全白機身微微傾斜,上有 8 個容納電池的槽孔,頂端為智慧面板,旁邊可以單體 8 個槽孔為單位擴充,以利在熱門地段,能提供更多交換用電池,電池上有綠色提把設計,點綴於白色整體外觀之中,可說兼具時尚與實用的風格,這樣的設計可說較為偏向消費性電子產品風格,而非傳統的汽機車設計風格。  
 

    Gogoro 電動機車使用的電池與特斯拉(Tesla)Model S 電動車相同,為 Panasonic 18650 鋰電池,這呼應了先前 Gogoro 與 Panasonic 合作的消息,電池外觀大小有如鞋盒大小,重約 9 公斤,為一般人雙手可提起的重量,內建 NFC 等 25 種感測器,Gogoro 主打電池交換概念,當機車騎到電池沒電時,並不用慢慢等著充電,而是騎到 GoStation,將機車電池取出,放入空槽孔中,6 秒鐘後,會由其他槽孔跳出充滿電力的電池,拿取後直接上路。  
 

    (Source:)   過去電動機車為人詬病之處就是充電慢、行駛距離不足,但若能在城市中遍布 GoStation,則可解決此二個問題,因為隨時交換電池只需 6 秒鐘,只要在 GoStation 普及的範圍內,可說實際上不用擔心行駛距離問題,而由於機車在亞洲大多為市區交通,如通勤、快遞送貨等使用,只要能在市區內普遍設立 GoStation,就可大為提升電動機車的實用性,解除過去電動機車銷售上最大的障礙。而 GoStation 設置成本也並不高,每座約為 1 萬美元。    
邁入智慧城市   另一方面,GoStation 與交通有關、存有大量電池,並與電力網路相連結的特性,一旦普遍設立,將成為進軍智慧城市的踏腳石。   智慧城市的重要領域包括交通、能源、水資源、保健、廢棄物處理等領域,GoStation 的交換資料,能夠讓城市政府交通相關單位掌握機車的使用熱點,進而做出更好的市政規劃,而 GoStation 內部存放大量充電中的電池,在歐美,類似的設施,如特斯拉的電池交換站,都設想到可以同時作為智慧電網的一環,如當市區內某區域能源需求突增,可由附近的站點暫停充電,甚至從電池饋電,來因應變化,而不至於跳電,而 GoStation 也可能在夜間電力離峰時間充電,降低白天尖峰的用電壓力。  
 

 

  除此之外,《富比世》(Forbes)報導,陸學森表示,當大都會人口成長,尖峰用電需求不斷上升,想像 Gogoro 智慧電動機車與 GoStation 能收集車主何時、在何處交通的資料,將有助於城市的智慧電網了解與預測用電需求,因而能提早因應。如此一來,城市電力網路將不必為了尖峰流量而大幅擴建或更新輸配系統,可節省大量預算。   陸學森接受科技媒體《SlashGear》採訪時也指出,在都會化的趨勢下,世界最終將有 6 成人口居住在大城市之中,而目前的都市居民有 12% 的收入,都花費在能源相關的開銷上,其中有一半是汽油,若能節約這一大部分的開支,不僅對人類有幫助,也是廣大的市場,更有助於降低全球碳排放。   由此可知,GoStation 不僅只是推動電動機車的推手,其最終願景為協助城市以及電力網路智慧化。此外,GoStation 的智慧面板,若與智慧城市的資訊系統結合,不僅可提供氣候等資訊,甚至可能作為智慧服務功能。    
以亞洲都市為起點   智慧城市目前為全球主流趨勢,在台灣也成為多個市政府的重要施政目標,如台北市長柯文哲即期望建立智慧政府、智慧城市與智慧市民,也因此造成資訊局長一度難產,最初柯文哲市長期待新任資訊局長能夠成為台北智慧城市的資訊長,然而,智慧城市牽涉專業領域過廣,並非單一一人所能勝任,所需要的人才規格,也非市政府的薪資標準所能負擔,於是有了「香蕉的待遇只能僱到猴子」之嘆,最後折衷辦法為市府成立智慧城市委員會,由各領域專業的委員協助制定策略,資訊局負責執行。   由台北市資訊局長一度難產,可看出智慧城市千頭萬緒,對每個城市來說都是相當大的挑戰,若城市內以機車需求,由民間力量普設 GoStation,將可成為智慧城市推動的一大助力。  
 
 
 
 
 
  (Source:)
 
 
 
 
    Gogoro 於 CES 2015 中透露,2015 年將於一個亞洲城市開始進行 GoStation 計畫,但尚未明言將為哪個城市,可說繼承一貫的飢餓行銷策略,推測 Gogoro 可能於下一個消費性電子展會或是汽機車重要展會發表其首要主打城市。而《富比世》則報導,Gogoro 將選擇超過 1,000 萬人口的大都會,作為第一個起步的城市。    
消費性電子產品設計思維   然而,如此廣大的願景,仍然要 Gogoro 的智慧電動機車本身成功,才有可能成真。在機車本身,Gogoro 的表現可說也讓人相當驚艷,其機車的設計可說跳脫了傳統電動機車的既定印象,與 GoStation 的設計風格一樣,偏向消費性電子產品風格,而非傳統的汽機車設計風格,多家歐美科技媒體認為,這樣的特立獨行風格,來自於陸學森為設計消費性電子產品出身,過去從未設計過機車。但如此一來,反而帶來了新鮮感。科技媒體《The Verge》譽為「你所買得起最酷的機車」。   機車車身與零組件也與過去電動機車不同,並不利用現有機車零組件,全數零組件含傳動系統、懸吊系統、前後輪碟煞、LED 車燈、底盤與所有的電子系統都是由 Gogoro 原創設計打造。  

  機車的車身可說以輕便、小巧、簡潔的都市風格為主,在性能上,可由停車狀態在 4.2 秒鐘內加速到 30 英里(48.3 公里),最高速度約達 60 英里(96.6 公里),內建雙電池槽,兩個電池都充滿電的狀態下,行駛距離達 100 公里,以市區行駛來說這樣的性能與續航力已經相當足夠,CES 2015 上 Gogoro 更展示機車壓車快速轉彎與燒胎,宣告其智慧電動機車絕非過去一般人印象中「無力」的舊式電動機車。  

    (Source:)   目前全球機車市場最主流的機車為 125cc 車種,約佔 8 成市場,Gogoro 也以 125cc 為「假想敵」,其傳動系統動力在鋁輕量化車身上的表現,比 125cc 對手快上 16%。而 Gogoro 機車也加強防水性,而比內燃機機車防水性更佳,當亞熱帶地區下大雨積水,傳統機車擔心排氣管浸水拋錨,Gogoro 機車卻沒有這樣的顧慮。   Gogoro 強調其產品為智慧電動機車(Smartscooter),非僅是一般電動機車,身為智慧產品,除了頭燈會在天色暗時自動開啟這種智慧功能以外,與智慧型手機的連結更是重點。  
 

     
電動車與智慧手機的結合   陸學森曾於宏達電任職的經歷,為 Gogoro 帶來了更完整的與智慧型手機整合的概念,除了會在智慧型手機 app上顯示基本的行駛路線、電池電力等資料,可以用 app 開行李箱,以及可透過手機 app 個人化設定諸如機車面板顏色、鎖車燈號顏色等等以外,車身上有 30 種感測器,含電池上的 25 種感測器,隨時檢查車身與電池狀況,每 10 分鐘回報,若是車子有了任何問題,就會即時在手機上通知車主,若是車主沒有看手機,則會在下次交換電池時,在 GoStation 的智慧螢幕上通知車主,確保車主的安全。  
 

  (Source:)   Gogoro 智慧電動機車還有一個對手機重度使用者最貼心的功能:在行李箱裡頭就有 USB 埠可以幫手機充電。   車身上的感測器更結合了演算法,當車主買回 Gogoro 智慧電動機車,開始騎乘後,感測器收集的資料,會讓智慧電動機車不斷學習車主的行駛模式,一邊調整能源管理模式,最後,機車的能源管理,將能完全配合車主的日常需求,達到最省電的效果。    
用手機模式走一條不一樣的路   甚至連電動機車的商業模式也可能模仿手機。手機往往與電信業者結合,以便宜價格推出,但是收取服務費用,《富比世》報導,Gogoro 表示,電動車最大的成本來源就是電池,電池本身佔 4 成成本,若把電池與充電迴路除去,機車車身成本就會大為降低,如此一來,Gogoro 將可以追求手機商業模式,也就是說,機車本身以相當低廉的價格販售,主要從電池充電服務收費來得到營收。   如此一來,更能避免電動機車過去不能普及的另一個主要障礙:價格過高。Gogoro 雖然尚未宣布其價格,但預期若在手機商業模式下的補貼後,將比想像中低,由於其設計風格,與可能的低價,相對於特斯拉的高價,科技媒體《SlashGear》將 Gogoro 譽為「平民特斯拉」(Tesla for the everyone)。   大部分國內媒體,將 Gogoro 視為一般電動機車報導,但從許多外媒報導可看出,歐美媒體以及產業界已經看出 Gogoro 的野心與潛力,全球目前估計約有 2 億輛機車,而機車產業已經幾乎 20 年沒有重大創新,的確是進行破壞式創新的適當領域,而 Gogoro 結合智慧城市概念的遠大願景,將從哪個城市開始落實,發展是否順利,不僅是電動機車界動見觀瞻,也將成為智慧能源與智慧城市領域的注意焦點。   (首圖來源:)      本文全文授權轉載自《科技新報》─〈〉

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

※推薦評價好的iphone維修中心

電動車免牌照稅 可望延長至 2018 年

為鼓勵購買低汙染的環保車輛,電動汽車將再延長牌照稅免稅期 3 年。包括電動小客車、巴士與貨車,免徵牌照稅優惠將可持續至 2018 年 1 月 5 日為止。   為基於扶植國內電動車研發製造的需要,立法院財政委員會 12 日將審查由行政院提出的用牌照稅法修正草案,授權給地方政府對以電能為動力的電動汽車,免徵牌照稅優惠期間,可以再延長 3 年。經濟日報指出,這項減稅法案可望趕在立法院本會期結束前完成修法程序,以便接續提供減免。   財政部指出,免徵電動車使用照稅優惠措施自 2012 年 1 月 5 日施行至今,國庫釋出的免稅利益將近新台幣 1,000 萬元,平均每年約為 300 萬元。過去 3 年,申請免稅的電動車由 319 輛成長到 574 輛,減稅金額也從約 200 萬元倍增至近 400 萬元。財政部認為,電動車申請免稅案件逐年增加,代表電動車的使用與購買意願也在上升。  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

北京路燈變充電樁 望每天為一萬輛車充電

北京市首批路燈充電樁改造已經展開,位於昌平區京密北路上的八十八盞路燈,從傳統高壓鈉燈改造為LED燈,可讓十幾輛電動汽車同時充電;此外,昌平區內還設置了八個純電動汽車慢充樁,專供電動車充電,充電時間約為四、五個小時,且白天晚上皆可充電。北京市科委指出,希望今年內能達成北京五環內建成五公里半徑的快速充電網路和每天為一萬輛車充電的目標。   除北京市內,連接北京與上海全程1262公里的京滬高速公路也將開通全線快速充電系統。平均每五十公里將設一座充電站,最快三十分鐘可充電完成。同等里程電費支出為燃油車的一半,全程充電費不到人民幣400元(約合新臺幣2052元)。   目前中國大陸的國家電網已完成2.4萬個充電樁,形成「兩縱一橫」網路,規模為世界之最。但據報導,其目前已建成的四百多座充電站幾乎沒有盈利,處於全線虧損,而在深圳市營運的七座充電站,每年虧損額超過1000萬人民幣(約5132.3萬元)。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

通用推雪佛蘭 Bolt 欲與定價相當的 Tesla Model 3 抗衡

  美國底特律汽車展於 1 月 12 日開幕,通用汽車(GM)新款電動車雪佛蘭 Bolt(Chevrolet Bolt)首度亮相,預料未來將與電動車大廠特斯拉(Tesla)激烈競爭。   雪佛蘭 Bolt 每次充電可行駛 200 英里,達到先前油電混合車款 Volt 的 4 倍之多, 電池由韓國樂金化學(LG Chem)所製造,預計於 2017 年上市,售價約為 3 萬美元,可望與同樣預定 2017 年上市,售價約 3.5 萬美元的 Model 3 車款競相抗衡。根據知情人士表示,Bolt 為掀背式(Hatchback)設計,外型傾向跨界車款。   通用汽車盼能藉雪佛蘭 Bolt,加強消費者對該品牌具備完整產品線的印象,也希望能將此車款擴及至包括中國等全球市場。     (Source:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

產能尚未滿載 特斯拉要到 2020 年才可能獲利

特斯拉執行長 Elon Musk 在世界汽車業大會 (Automotive News World Congress) 中表示,以目前狀況看來,特斯拉的電動車在 2020 年前都無法開始獲利。   根據華爾街日報報導,特斯拉的 Model 3 電動車在現有會計原則下,至少要等到 2020 年產能滿載時才能正式轉盈。Model 3 是特斯拉功能性較少的電動車,一台可能售價約介於 3 萬到 4 萬美元之間,比起其他 Model S 系列一台動輒超過 10 萬美元來說相對便宜,Model 3 預計在 2017 年推出。特斯拉的股價盤後下跌 7%,來到 189.95 美元。   對於電動車的發展,Musk 也要求其他汽車公司盡速推出自家電動車。特斯拉目前電動車銷售量約為 35,000 台,預計到 2025 年可賣出數百萬台。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

Tesla 股價大跌 穆斯克荷包失血 28 億美元

電動車大廠特斯拉(Tesla) 股價重創,不僅投資人損失慘重,公司創辦人穆斯克(Elon Musk)財富更是大失血。根據經濟日報報導,Tesla 股價 2014 年 9 月至今大跌 34%,穆斯克財富也蒸發 28 億美元。   穆斯克表示,Tesla 在中國的銷售低迷,使曾經超搶手的 Tesla 股票價格 14 日大跌 11.56 美元,跌幅約 6%,若和去年 9 月每股 291.42 美元的高價相比,Tesla 股票在這段期間崩跌掉 3 分之 1 的價值。   穆斯克則擁有逾 2,800 萬股的 Tesla 股票,相當流通股總數的 4 分之 1 左右,這意味股價大跌讓穆斯克 28 億美元的財富煙消雲散。不過,雖然上述損失數字很可觀,但穆斯克本身可能不太意外,他在去年 9 月曾警告說,Tesla 的股價「有點太高」,當時投資人不予理會,Tesla 股價還接著上漲至每股 281.19 美元。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

常見的索引模型淺析

  索引的出現是為了提高數據庫查詢的效率,就像書的目錄一樣。常見的索引模型有哈希表、有序數組、B+樹。

  • 自適應哈希索引(AHI)

  哈希表是一種常見的數據結構,即通過哈希算法計算出一個数字在表中的位置,並將数字存入該表。哈希索引就是通過哈希表來實現的,一般情況下查找時間複雜度為O(1)。InnoDB會監控對錶上各索引頁的查詢,會自動根據訪問的頻率和模式為某些熱點頁建立哈希索引,所以又叫自適應哈希索引,訪問模式一樣指查詢的條件一樣。

  比如我們維護一張身份證信息和用戶姓名的表,需要根據身份證號查詢姓名,哈希索引大概是這樣的:

  哈希索引適合只有等值查詢的場景,例如select * from T where index_col = ‘##’。哈希索引是無序的,如果需要區間查詢,那就要把所有數據掃描一遍。

  • 有序數組索引

  有序數組在等值查詢和區間查詢場景中效率都很高,同樣用上面的表舉例,索引大概是這樣的:

  要查詢某條數據或者區間的時候,使用二分法時間複雜度為O(logN)。但如果需要在中間更新數據時,那麼就要移動後面所有的數據。有序數組索引只適用於靜態存儲引擎,比如保存2019年度學校所有學生信息。

  • B+樹索引

  B+樹是為磁盤或其他直接存取輔助設備設計的一種平衡查找樹。下面是一顆高度為2的B+樹,所有記錄都在恭弘=叶 恭弘子結點上順序存放,恭弘=叶 恭弘子結點通過指針相連。

  B+樹索引就是B+樹在數據庫中的實現,由於B+索引在數據庫中具有高扇出性,在數據庫中B+樹的高度一般為2~4層。查找某一鍵值的行記錄時最多只需要2~4次IO。以InnoDB的一個整数字段索引為例,這顆B+樹大概是1200叉樹,這裏N叉樹的N取決於數據塊的大小。高度為4的時候就可以存1200的3次方個值,大概為17億。考慮到樹根的數據塊總是在內存中,一個10億行的表上一個整数字段的索引,查找一個值最多只需要訪問3次磁盤。

  在InnoDB存儲引擎中,表是根據主鍵順序存放的。根據恭弘=叶 恭弘子結點內容,B+樹索引又分為聚簇索引和輔助索引。

    • 聚簇索引:按照每張表的主鍵構造一顆B+樹,恭弘=叶 恭弘子結點的key是主鍵值, value是該行的其他字段值,聚簇索引的恭弘=叶 恭弘子結點也稱為數據頁。
    • 輔助索引:恭弘=叶 恭弘子結點的內容是主鍵的值。

我們用一個例子來說明上面的概念,創建一張表,在字段k上有索引:

create table T(
id int primary key, 
k int not null, 
name varchar(16),
index (k))engine=InnoDB;

  表中R1~R5的(ID,k)值分別為(100,1)、(200,2)、(300,3)、(500,5)和(600,6),兩顆B+樹如下,可以明顯看到這兩個顆樹的區別。

  使用普通索引查詢時,例如 select * from T where k = 2,需要先搜索k索引樹,得到ID的值為200;再到ID索引搜索一次。這個過程就叫做回表,非主鍵索引查詢需要多搜索一棵樹。

  參考資料:《MySQL實戰45講》

       《MySQL技術內幕:InnoDB存儲引擎》第二版

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

※推薦評價好的iphone維修中心

4. union-find算法

  算法的主題思想:

  1.優秀的算法因為能夠解決實際問題而變得更為重要;

  2.高效算法的代碼也可以很簡單;

  3.理解某個實現的性能特點是一個挑戰;

  4.在解決同一個問題的多種算法之間進行選擇時,科學方法是一種重要的工具;

  5.迭代式改進能夠讓算法的效率越來越高效;

 

   1. 動態連通性

  動態連接:輸入是一對整數對的序列,其中每個整數代表某種類型的對象(或觸點),我們將整數對p q 解釋為意味着p連接到q。我們假設“連接到”是等價關係:

  對稱性:如果p連接到q,則q 連接到p。

  傳遞性:如果p連接到q且q 連接到r,則p連接到r。
  自反性:p與p連接。
  等價關係將對象劃分為多個等價類 或連接的組件。等價類稱為連通分量或分量。
  我們的目標是編寫一個程序,以從序列中過濾掉多餘的對:當程序從輸入中讀取整數對 p q時,只有在該對點不等價的情況下,才應將對寫入到輸出中,並且將p連接到q。如果等價,則程序應忽略整數對pq 並繼續讀取下對。

  

 

  動態連通性問題的應用:

    1.網絡

    2.變量名等價性

    3.數學集合

      在更高的抽象層次上,可以將輸入的所有整數看做屬於不同的數學集合。

   2. 定義問題

  設計算法的第一個任務就是精確地定義問題。

  算法解決的問題越大,它完成任務所需的時間和空間可能越多。我們不可能預先知道這其間的量化關係,通常只會在發現解決問題很困難,或是代價巨大,或是發現算法所提供的信息比原問題所需要的更加有用時修改問題。例如,連通性問題只要求我們的程序能夠判斷出給定的整數對是否相連,但並沒有要求給出兩者之間的通路上的所有連接。這樣的要求更難,並會得出另一組不同的算法。

  為了定義和說明問題,先設計一份API  來封裝基本操作: 初始化,連接兩個觸點,查找某個觸點的分量 ,判斷兩個觸點是否屬於同一分量,分量的數量:

    /// <summary>
    /// 動態連通API
    /// </summary>
    public interface IUnionFind
    {
        /// <summary>
        /// 連接
        /// </summary>
        /// <param name="p"></param>
        /// <param name="q"></param>
        void Union(int p, int q);

        /// <summary>
        /// 查找觸點 p 的分量標識符
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        int Find(int p);

        /// <summary>
        /// 判斷兩個觸點是否處於同一分量
        /// </summary>
        /// <param name="p"></param>
        /// <param name="q"></param>
        /// <returns></returns>
        bool Connected(int p, int q);

        /// <summary>
        /// 連通分量的數量
        /// </summary>
        /// <returns></returns>
        int Count();
    }

  為解決動態連通性問題設計算法的任務轉化為實現這份API:

    1. 定義一種數據結構表示已知的連接;

    2. 基於此數據結構高效的實現API的方法;

  數據結構的性質會直接影響算法的效率。這裏,觸點為索引,觸點和連接分量都是用 int 值表示,將會使用分量中某個觸點的值作為分量的標識符。所以,一開始,每個觸點都是只含有自己的分量,分量標識符為觸點的值。由此,可以初步實現一部分方法:

 

    public class FirstUnionFind:IUnionFind
    {
        private int[] id;//* 分量id 以觸點作為索引
        private int count;//分量數量

        public FirstUnionFind(int n)
        {
            count = n;
            id = new int[n];
            for (var i = 0; i < n; i++)
            {
                id[i] = i; // 第一個 i 作為觸點,第二個 i 作為觸點的值
            }
        }

        public int Count()
        {
            return count;
        }

        public bool Connected(int p, int q)
        {
            return Find(p) == Find(q);
        }

        public int Find(int p)
        {
            
        }

        public void Union(int p, int q)
        {
            
        }
    }

 

  Union-find 的成本模型 是數組的訪問次數(無論讀寫)。

   3. quick-find算法實現

  quick-find 算法是保證當且僅當 id[p] 等於 id[q] 時,p 和 q 是連通的。也就是說,在同一個連通分量中的所有觸點在 id[ ] 中的值全部相等。

  所以 Find 方法只需返回 id[q],Union 方法需要先判斷 Find(p)  是否等於 Find(q) ,若相等直接返回;若不相等,需要將 q 所在的連通分量中所有觸點的 id [ ] 值全部更新為 id[p]。

    public class QuickFindUF: IUnionFind
    {
        private int[] id;//* 分量id 以觸點作為索引
        private int count;//分量數量

        public QuickFindUF(int n)
        {
            count = n;
            id = new int[n];
            for (var i = 0; i < n; i++)
            {
                id[i] = i; // 第一個 i 作為觸點,第二個 i 作為觸點的值
            }
        }

        public int Count()
        {
            return count;
        }

        public bool Connected(int p, int q)
        {
            return Find(p) == Find(q);
        }

        public int Find(int p)
        {
            return id[p];
        }

        public void Union(int p, int q)
        {
            var pID = Find(p);
            var qID = Find(q);

            if (pID == qID)
                return;

            for (var i = 0; i < id.Length; i++)
            {
                if (id[i] == qID)
                    id[i] = pID;
            }
            count--; //連通分量減少
        }

        public void Show()
        {
            for(var i = 0;i<id.Length;i++)
                Console.WriteLine("索引:"+i+",值:"+ id[i] );
            Console.WriteLine("連通分量數量:"+count);
        }
    }

  

  算法分析

  Find() 方法只需訪問一次數組,所以速度很快。但是對於處理大型問題,每對輸入 Union() 方法都需要掃描整個數組。

  每一次歸併兩個分量的 Union() 方法訪問數組的次數在 N+3 到 2N+1 之間。由代碼可知,兩次 Find 操作訪問兩次數組,掃描數組會訪問N次,改變其中一個分量中所有觸點的值需要訪問 1 到 N – 1 次(最好情況是該分量中只有一個觸點,最壞情況是該分量中有 N – 1個觸點),2+N+N-1。

  如果使用quick-find 算法來解決動態連通性問題並且最後只得到一個連通分量,至少需要調用 N-1 次Union() 方法,那麼至少需要 (N+3)(N-1) ~ N^2 次訪問數組,是平方級別的。

 

   4. quick-union算法實現

  quick-union 算法重點提高 union 方法的速度,它也是基於相同的數據結構 — 已觸點為索引的 id[ ] 數組,但是 id[ ] 的值是同一分量中另一觸點的索引(名稱),也可能是自己(根觸點)——這種聯繫成為鏈接。

  在實現 Find() 方法時,從給定觸點,鏈接到另一個觸點,知道到達根觸點,即鏈接指向自己。同時修改 Union() 方法,分別找到 p q 的根觸點,將其中一個根觸點鏈接到根觸點。

public class QuickUnionUF : IUnionFind
    {
        private int[] id;
        private int count;

        public QuickUnionUF(int n)
        {
            count = n;
            id = new int[n];
            for (var i = 0; i < n; i++)
            {
                id[i] = i; // 第一個 i 作為觸點,第二個 i 作為觸點的值
            }
        }

        public int Count()
        {
            return count;
        }

        public bool Connected(int p, int q)
        {
            return Find(p) == Find(q);
        }

        public int Find(int p)
        {
            while (p != id[p])
                p = id[p];
            return p;
        }

        public void Union(int p, int q)
        {
            var pRoot = Find(p);
            var qRoot = Find(q);

            if (pRoot == qRoot)
                return;

            id[pRoot] =qRoot;
count
--; //連通分量減少 } public void Show() { for (var i = 0; i < id.Length; i++) Console.WriteLine("索引:" + i + ",值:" + id[i]); Console.WriteLine("連通分量數量:" + count); } }

  

  森林表示

  id[ ] 數組用父鏈接的形式表示一片森林,用節點表示觸點。無論從任何觸點所對應的節點隨着鏈接查找,最後都將到達含有該節點的根節點。初始化數組之後,每個節點的鏈接都指向自己。

 

  算法分析

  定義:一棵樹的大小是它的節點的數量。樹中一個節點的深度是它到根節點的路徑上鏈接數。樹的高度是它的所有節點中的最大深度。

  quick-union 算法比 quick-find 算法更快,因為它對每對輸入不需要遍歷整個數組。

  分析quick-union 算法的成本比 quick-find 算法的成本要困難,因為quick-union 算法依賴於輸入的特點。在最好的情況下,find() 方法只需訪問一次數組就可以得到一個觸點的分量表示;在最壞情況下,需要 2i+1 次數組訪問(i 時觸點的深度)。由此得出,該算法解決動態連通性問題,在最佳情況下的運行時間是線性級別,最壞情況下的輸入是平方級別。解決了 quick-find 算法中 union() 方法總是線性級別,解決動態連通性問題總是平方級別。

  quick-union 算法中 find() 方法訪問數組的次數為 1(到達根節點只需訪問一次) 加上 給定觸點所對應節點的深度的兩倍(while 循環,一次讀,一次寫)。union() 訪問兩次 find() ,如果兩個觸點不在同一分量還需加一次寫數組。

   假設輸入的整數對是有序的 0-1, 0-2,0-3 等,N-1 對之後N個觸點將全部處於相同的集合之中,且得到的樹的高度為 N-1。由上可知,對於整數對 0-i , find() 訪問數組的次數為 2i + 1,因此,處理 N 對整數對所需的所有訪問數組的總次數為 3+5+7+ ……+(2N+1) ~ n^2

  

  

  5.加權 quick-union 算法實現

  簡單改動就可以避免 quick-union算法 出現最壞情況。quick-union算法 union 方法是隨意將一棵樹連接到另一棵樹,改為總是將小樹連接到大樹,這需要記錄每一棵樹的大小,稱為加權quick-union算法。

  代碼:

    public class WeightedQuickUnionUF: IUnionFind
    {
        int[] sz;//以觸點為索引的 各個根節點對應的分量樹大小
        private int[] id;
        private int count;

        public WeightedQuickUnionUF(int n)
        {
            count = n;
            id = new int[n];
            sz = new int[n];
            for (var i = 0; i < n; i++)
            {
                id[i] = i; // 第一個 i 作為觸點,第二個 i 作為觸點的值
                sz[i] = 1;
            }
        }

        public int Count()
        {
            return count;
        }

        public bool Connected(int p, int q)
        {
            return Find(p) == Find(q);
        }

        public int Find(int p)
        {
            while (p != id[p])
                p = id[p];
            return p;
        }

        public void Union(int p, int q)
        {
            var pRoot = Find(p);
            var qRoot = Find(q);

            if (pRoot == qRoot)
                return;

            if (sz[pRoot] < sz[qRoot])
            {
                id[pRoot] = qRoot;
            }
            else
            {
                id[qRoot] = pRoot;
            }
                
            count--; //連通分量減少
        }

        public void Show()
        {
            for (var i = 0; i < id.Length; i++)
                Console.WriteLine("索引:" + i + ",值:" + id[i]);
            Console.WriteLine("連通分量數量:" + count);
        }
    }

 

  算法分析

  加權 quicj-union 算法最壞的情況:

  

  這種情況,將要被歸併的樹的大小總是相等的(且總是 2 的 冥),都含有 2^n 個節點,高度都正好是 n 。當歸併兩個含有 2^n 個節點的樹時,得到的樹含有 2 ^ n+1 個節點,高度增加到 n+1 。

  節點大小: 1  2  4  8  2^k = N

  高       度: 0  1  2  3  k

  k = logN

  所以加權 quick-union 算法可以保證對數級別的性能。

  對於 N 個觸點,加權 quick-union 算法構造的森林中的任意節點的深度最多為logN。

  對於加權 quick-union 算法 和 N 個觸點,在最壞情況下 find,connected 和 union 方法的成本的增長量級為 logN。

  對於動態連通性問題,加權 quick-union 算法 是三種算法中唯一可以用於解決大型問題的算法。加權 quick-union 算法 處理 N 個觸點和 M 條連接時最多訪問數組 c M logN 次,其中 c 為常數。

  

  三個算法處理一百萬個觸點運行時間對比:

  

  

  三個算法性能特點:

 

  6.最優算法 – 路徑壓縮

  在檢查節點的同時將它們直接連接到根節點。

  實現:為 find 方法添加一個循環,將在路徑上的所有節點都直接鏈接到根節點。完全扁平化的樹。

 

  

  研究各種基礎問題的基本步驟:

  1. 完整而詳細地定義問題,找出解決問題所必須的基本抽象操作並定義一份API。

  2. 簡潔地實現一種初級算法,給出一個精心組織的開發用例並使用實際數據作為輸入。

  3. 當實現所能解決的問題的最大規模達不到期望時決定改進還是放棄。

  4. 逐步改進實現,通過經驗性分析和數學分析驗證改進后的效果。

  5. 用更高層次的抽象表示數據結構或算法來設計更高級的改進版本。

  6. 如果可能盡量為最壞情況下的性能提供保證,但在處理普通數據時也要有良好的性能。

  7.在適當的時候將更細緻的深入研究留給有經驗的研究者並解決下一個問題。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

IOT設備SmartConfig實現

一般情況下,IOT設備(針對wifi設備)在智能化過程中需要連接到家庭路由。但在此之前,需要將wifi信息(通常是ssid和password,即名字和密碼)發給設備,這一步驟被稱為配網。移動設備如Android、iOS等扮演發送wifi信息的角色,簡單來說就是移動應用要與IOT設備建立通信,進而交換數據。針對配網這一步驟,市面上一般有兩種做法:

  • AP連接方式:IOT設備發出AP(Access Point,可理解為路由器,可發出wifi)信息;移動設備STA(Station,可以連接wifi)連接到IOT設備AP,接着就可以發送wifi(家庭路由的wifi)信息給設備了。另外,也可互換角色,及移動設備釋放熱點,IOT設備進行連接。
  • SmartConfig(一鍵配置)方式:不需要建立連接,移動設備將wifi信息(需提前獲取)寫入數據包,組播循環發出此數據包;IOT設備處於監聽所有網絡的模式,接收到UDP包后解析出wifi信息拿去連網。

可以發現,SmartConfig不需建立連接,步驟較少,實現起來也較容易,並且用戶也無需進行過多的操作。本文的IOT設備基於ESP32開發板,解釋原理及實現如何通過Android APP發出UDP包實現SmartConfig。知識點:計算機網絡、UDP、組播、DatagramSocket

一、網絡知識回顧

計算機網絡分層結構如下:

  • 應用層:體系中的最高層。任務是通過應用程序間的交互來完成特定網絡應用。不同的網絡應用對應不同的協議:如HTTPDNSSMTP。其交互的數據單元稱為報文。
  • 運輸層:複雜向兩台主機中進程直接的通信提供通用的數據傳輸服務,使用端口作為向上傳遞的進程標識,主要有TCP和UDP。
  • 網絡層:負責為分組交換網絡上的不同主機提供通信服務,使用IP協議。
  • 網絡接口層:包括數據鏈路層和物理層,傳輸單位分別是幀和比特。

1. IP協議

IP(Internet Protocol)協議是網絡層的主要協議。其版本有IPv4(32位)、IPv6(128位)。與IP協議配套使用的還有地址解析協議(ARP)、網際控制報文協議(ICMP,重要應用即常見的PING,測試連通性)、網際組管理協議(IGMP)。

IP數據報格式,由首部和數據部分兩部分組成:

IP地址分類如下:

1.1 兩級IP地址

IP地址是每一台主機唯一的標識符,由網絡號和主機號組成。A、B、C三類均為單播地址(一對一),D類為多播地址(一對多)。

1.2 三級IP地址

在兩級中新增了子網號字段,也稱為劃分子網。其方法是從主機號借用若干位作為子網號。

子網掩碼:是一個網絡或子網的重要屬性,可通過子網掩碼計算出目的主機所處於哪一個子網。若沒有劃分子網,則使用默認子網掩碼(A類255.0.0.0、B類255.255.0.0、C類255.255.255.0)

1.3 無分類編址

無分類編址(CIDR)也稱為構造超網,使用網絡前綴+主機號規則,並使用斜線標明前綴位數,如:

128.15.34.77/20
1.4 IP多播

多播又稱為組播,提供一對多的通信,大大節約網絡資源。IP數據報中不能寫入某一個IP地址,需寫入多播組的標識符(需要接收的主機與此標識符關聯)。D類地址即為多播組的標識符,所以多播地址(D類)只能作為目的地址。分為本局域網上的硬件多播、互聯網多播兩種。

多播使用到的協議

  • IGMP(網際組管理協議):讓連接在本地局域網上的多播路由器(能夠運行多播協議的路由器)知道本局域網上是否有主機(進程)參加或退出了某個多播組。
  • 多播路由選擇協議:用於多播路由器之間的協同工作,以便讓多播數據報以最小的代價傳輸。

2. UDP協議

運輸層向上面的應用層提供通信服務,通信的端點並不是主機而是主機中進程,使用協議端口號標識進程(如HTTP為80)。UDP協議是運輸層中重要的兩個協議之一。

  • UDP是無連接的
  • UDP使用盡最大努力交付,不保證可靠交付
  • UDP是面向報文的
  • UDP沒有擁塞控制
  • UDP支持一對一,一對多,多對一和多對多
  • UDP首部開銷小

二、Java中的UDP

1. Socket

socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象為幾個簡單的接口供應用層調用已實現進程在網絡中通信。簡單來說,socket是一種接口,對傳輸層(TCP/UPD協議)進行了的封裝。

socket通信

  • TCP socket:需建立連接,TCP三次握手,基於流的通信(InputStrea和OutputStream)
  • UDP socket:無需建立連接,基於報文的通信。可以組播的形式發出報文,適合本場景中的配網步驟。

2. Java中的socket

2.1 類解釋

Java為Socket編程封裝了幾個重要的類(均為客戶端-服務端模式):

Socket
實現了一個客戶端socket,作為兩台機器通信的終端,默認採用TCP。connect()方法請求socket連接、getXXXStream()方法獲取輸入/出流、close()關閉流。

ServerSocket
實現了一個服務器的socket,等待客戶端的連接請求。bind()方法綁定一個IP地址和端口、accept()方法監聽並返回一個Socket對象(會阻塞)、close()關閉一個socket

SocketAddress # InetSocketAddress
前者是一個抽象類,提供了一個socket地址,不關心傳輸層協議;後者繼承自前者,表示帶有IP地址和端口號的socket地址。

DatagramSocket
實現了一個發送和接收數據報的socket,使用UDP。send()方法發送一個數據報(DatagramPacket)、receive()方法接收一個數據報(一直阻塞接至收到數據報或超時)、close()方法關閉一個socket。

DatagramPacket
使用DatagramSocket時的數據報載體。

2.2 UDP實例

SmartConfig採用UDP實現,所以在前述知識的基礎下,先編寫一個例子熟悉java udp的使用,首先建立服務端的代碼:

public class UDPServer {
    /**
     * 設置緩衝區的長度
     */
    private static final int BUFFER_SIZE = 255;
    /**
     * 指定端口,客戶端需保持一致
     */
    private static final int PORT = 8089;

    public static void main(String[] args) {
        DatagramSocket datagramSocket = null;
        try {
            datagramSocket = new DatagramSocket(PORT);
            DatagramPacket datagramPacket = new DatagramPacket(new byte[BUFFER_SIZE], BUFFER_SIZE);
            while (true) {
                // 接收數據報,處於阻塞狀態
                datagramSocket.receive(datagramPacket);
                System.out.println("Receive data from client:" + new String(datagramPacket.getData()));
                // 服務器端發出響應信息
                byte[] responseData = "Server response".getBytes();
                datagramPacket.setData(responseData);
                datagramSocket.send(datagramPacket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (datagramSocket != null) {
                datagramSocket.close();
            }
        }
    }
}

客戶端發出數據報:

public class UDPClient {
    /**
     * 指定端口,與服務端保持一致
     */
    private static final int PORT = 8089;
    /**
     * 超時重發時間
     */
    private static final int TIME_OUT = 2000;
    /**
     * 最大重試次數
     */
    private static final int MAX_RETRY = 3;

    public static void main(String[] args) throws IOException {
        try {
            byte[] sendMsg = "Client msg".getBytes();
            // 創建數據報
            DatagramSocket socket = new DatagramSocket();
            // 設置阻塞超時時間
            socket.setSoTimeout(TIME_OUT);
            // 創建server主機的ip地址(此處使用了本機地址)
            InetAddress inetAddress = InetAddress.getByName("192.168.xxx.xxx");
            // 發送和接收的數據報文
            DatagramPacket sendPacket = new DatagramPacket(sendMsg, sendMsg.length, inetAddress, PORT);
            DatagramPacket receivePacket = new DatagramPacket(new byte[sendMsg.length], sendMsg.length);
            // 數據報文可能丟失,設置重試計數器
            int tryTimes = 0;
            boolean receiveResponse = false;
            // 將數據報文發送出去
            socket.send(sendPacket);
            while (!receiveResponse && (tryTimes < MAX_RETRY)) {
                try {
                    // 阻塞接收數據報文
                    socket.receive(receivePacket);
                    // 檢查返回的數據報文
                    if (!receivePacket.getAddress().equals(inetAddress)) {
                        throw new IOException("Unknown server's data");
                    }
                    receiveResponse = true;
                } catch (InterruptedIOException e) {
                    // 重試
                    tryTimes++;
                    System.out.println("TimeOut, try " + (MAX_RETRY - tryTimes) + " times");
                }
            }
            if (receiveResponse) {
                System.out.println("Receive from server:" + new String(receivePacket.getData()));
            } else {
                System.out.println("No data!");
            }
            socket.close();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }
}

運行結果:

* 發現客戶端收到的數據被截斷了,這是因為沒有重置接收包的長度,在服務端datagramPacket.setLength()可解決。

三、SmartConfig

根據前面的socket相關應用,基本想到如何實現一鍵配置。在實際應用中,原理一樣,只是增加了組播(這一點需要和IOT設備端共同確定,數據的格式也需協定)。在實現中,需要針對不同IP組播地址發出循環的UDP報文,增加設備端接收到的可能性;同時APP也要開啟服務端程序監聽發出數據報的響應,以此更新UI或進行下一步的數據通信。相關核心代碼如下:

// 對每一個組播地址循環發出報文 
while (!mIsInterrupt && System.currentTimeMillis() - currentTime < mParameter
         .getTimeoutGuideCodeMillisecond()) {
     mSocketClient.sendData(gcBytes2,
             mParameter.getTargetHostname(),
             mParameter.getTargetPort(),
             mParameter.getIntervalGuideCodeMillisecond());
     // 跳出條件,發出UDP報文達到一定時間
     if (System.currentTimeMillis() - startTime > mParameter.getWaitUdpSendingMillisecond()) {
         break;
     }
}

組播地址設置:

public String getTargetHostname() {
    if (mBroadcast) {
        return "255.255.255.255";
    } else {
        int count = __getNextDatagramCount();
        return "234." + (count + 1) + "." + count + "." + count;
    }
}

完整代碼省略(利益相關,代碼匿了^_^),基本思路很簡單。最終的實現是IOT設備收到UDP發出的wifi信息,並以此成功連接wifi,連接服務器,進而綁定賬號。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

mybatis緩存之一級緩存(二)

這篇文章介紹下mybatis的一級緩存的生命周期

一級緩存的產生

一級緩存的產生,並不是看mappper的xml文件的select方法,看下面的例子

mapper.xml

    <select id="getById" resultType="entity.TempEntity">
       select * from  temp where id = #{id}
    </select>

test

    @Test
    public  void testSelectAsUpdate() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = build.openSession();
        sqlSession.update("dao.Temp03Dao.getById", 1);
        sqlSession.update("dao.Temp03Dao.getById", 1);
    }

執行結果

2020-06-26 17:33:27,899 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:33:27,922 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:33:27,923 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:33:27,923 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)

我們可以看到執行了2次查詢。說明並沒有產生緩存。說明和sqlsession調用的方法是有關係的

只有調用上圖中的方法才會產生一級緩存

一級緩存的銷毀

1.關閉session

這個是根據debug看到的一級緩存的最終結構。下面是整個依賴的類圖

test

 @Test
    public  void test() throws IOException, NoSuchFieldException, IllegalAccessException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = build.openSession();
        TempEntity tempEntity1 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity1);

        Field executorField = sqlSession.getClass().getDeclaredField("executor");
        executorField.setAccessible(true);
        CachingExecutor  cachingExecutor = (CachingExecutor) executorField.get(sqlSession);

        Field declaredField = cachingExecutor.getClass().getDeclaredField("delegate");
        declaredField.setAccessible(true);
        SimpleExecutor simpleExecutor  = (SimpleExecutor) declaredField.get(cachingExecutor);

        Field localCacheField = simpleExecutor.getClass().getSuperclass().getDeclaredField("localCache");
        localCacheField.setAccessible(true);
        PerpetualCache perpetualCache = (PerpetualCache) localCacheField.get(simpleExecutor);

        Field cacheField = perpetualCache.getClass().getDeclaredField("cache");
        cacheField.setAccessible(true);
        Map<Object,Object> map= (Map<Object, Object>) cacheField.get(perpetualCache);
        logger.info("緩存關閉前");
        for (Map.Entry<Object,Object> objectObjectEntry:map.entrySet()){
            logger.info(objectObjectEntry.getKey() + "===" + objectObjectEntry.getValue());
        }
        sqlSession.close();
        logger.info("緩存關閉后");

        for (Map.Entry<Object,Object> objectObjectEntry:map.entrySet()){
            logger.info(objectObjectEntry.getKey() + "=" + objectObjectEntry.getValue());
        }
    }

運行結果

2020-06-26 17:38:52,777 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:38:52,801 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:38:52,824 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:38:52,824 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:38:52,825 INFO [TempTest] - 緩存關閉前
2020-06-26 17:38:52,826 INFO [TempTest] - -1654591322:461730790:dao.Temp03Dao.getById:0:2147483647:select * from  temp where id = ?:1:dev===[TempEntity{id=1, value1='11111', value2='aaaaa'}]
2020-06-26 17:38:52,827 INFO [TempTest] - 緩存關閉后

可以看到session關閉后,緩存就不存在了

2.Commit提交

test

    @Test
    public  void testCommit() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = build.openSession();
        TempEntity tempEntity1 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity1);
        sqlSession.commit();
        TempEntity tempEntity2 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity2);
        logger.info(tempEntity1 == tempEntity2);

    }

運行結果

2020-06-26 17:40:40,821 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:40:40,846 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:40:40,862 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:40:40,862 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:40:40,863 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:40:40,863 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:40:40,864 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:40:40,864 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:40:40,864 INFO [TempTest] - false

說明sqlSession.commit時會清空緩存

3.Rollback

test

    @Test
    public  void testRollback() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = build.openSession();
        TempEntity tempEntity1 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity1);
        sqlSession.rollback();
        TempEntity tempEntity2 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity2);
        logger.info(tempEntity1 == tempEntity2);

    }

執行結果

2020-06-26 17:42:23,793 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:42:23,833 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:42:23,843 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:42:23,843 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:42:23,844 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:42:23,844 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:42:23,845 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:42:23,845 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:42:23,845 INFO [TempTest] - false

sqlSession.rollback()也會清空緩存

4.update更新

這裡是在第一次查詢后,緊接着進行update操作。這裏與表無關。就是操作其它表,也會清空緩存。

test

    @Test
    public  void testForUpdate() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = build.openSession();
        TempEntity tempEntity1 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity1);
        sqlSession.update("dao.Temp03Dao.updateById", 1);
        TempEntity tempEntity2 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity2);
        logger.info(tempEntity1 == tempEntity2);

    }

運行結果

2020-06-26 17:45:43,997 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:45:44,034 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:45:44,048 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:45:44,049 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:45:44,049 DEBUG [dao.Temp03Dao.updateById] - ==>  Preparing: update temp set value1 = 'ffffff' where id = ? 
2020-06-26 17:45:44,049 DEBUG [dao.Temp03Dao.updateById] - ==> Parameters: 1(Integer)
2020-06-26 17:45:44,050 DEBUG [dao.Temp03Dao.updateById] - <==    Updates: 1
2020-06-26 17:45:44,051 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:45:44,051 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:45:44,052 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:45:44,053 INFO [TempTest] - TempEntity{id=1, value1='ffffff', value2='aaaaa'}
2020-06-26 17:45:44,053 INFO [TempTest] - false

這裏還是在一個session會話中。記得之前有人給我說只要在一個session會話中,執行update不會清空緩存。這裏的代碼就證明了

5.clearCache 主動清除

test

    @Test
    public  void testClearCatch() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = build.openSession();
        TempEntity tempEntity1 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity1);
        sqlSession.clearCache();
        TempEntity tempEntity2 = sqlSession.selectOne("dao.Temp03Dao.getById", 1);
        logger.info(tempEntity2);
        logger.info(tempEntity1 == tempEntity2);

    }

運行結果

2020-06-26 17:48:42,085 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:48:42,110 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:48:42,124 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:48:42,124 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:48:42,125 DEBUG [dao.Temp03Dao.getById] - ==>  Preparing: select * from temp where id = ? 
2020-06-26 17:48:42,125 DEBUG [dao.Temp03Dao.getById] - ==> Parameters: 1(Integer)
2020-06-26 17:48:42,126 DEBUG [dao.Temp03Dao.getById] - <==      Total: 1
2020-06-26 17:48:42,126 INFO [TempTest] - TempEntity{id=1, value1='11111', value2='aaaaa'}
2020-06-26 17:48:42,126 INFO [TempTest] - false

一級緩存 臟讀問題

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!