系統分析師-軟件水平考試(高級)-理論-系統規劃

系統分析師-軟件水平考試(高級)-理論-系統規劃

前言

系統規劃是什麼東東呢?

通俗點說,就是為了實現企業的戰略目標,分析企業現狀(技術外部)與系統狀況(技術內部),從而提出新的技術要求,對現有系統進行規劃設計等變化。

  1. 目標是實現企業戰略目標;
  2. 分析企業現狀(了解情況了,才能下手唄。站在技術角度,企業現狀就分技術和非技術);
  3. 根據掌握的資料,對接下來的系統開發做出規劃(技術規劃,那是CTO的工作,這裏就直接系統規劃);
  4. 落實規劃,開發或改造系統,從而完成對企業戰略目標的支持。

期間會涉及對一些項目或系統(一個公司會擁有多個項目與多個系統,用於支撐公司產業線)的評估,如機會選擇,可行性分析,效益分析(分析師比架構師多了這些思量與考察,而高項則更為具體)。

XMIND

(圖片絕對清楚。如果看不清楚,請下載,或者在新頁面中打開圖片)

總結

XMIND重難點的解讀

步驟

為了幫助大家更好地理解這部分內容,我們舉個栗子。

這樣說吧,阿里巴巴定下了淘寶雙十一戰略目標。你作為CTO,就要站在公司高層,開始規劃阿里的系統,讓它為雙十一這一戰略目標服務。

具體的實施過程應該是這樣的:

  1. 首先,根據雙十一這一戰略目標,你得分析阿里現在技術環境(可以做出怎樣的改變,業務上是否可以進行一定的妥協等等),並了解系統當前的狀況(是否可以承受住雙十一壓力。承受不了的話,瓶頸在哪裡等等)。無論做什麼,收集信息,了解情況都是首要的。
  2. 其次,站在技術的角度,你需要確定公司信息系統該達到怎樣的目標(更大一些說,應該是技術的目標標準)。比如雙十一這樣的戰略目標,我確定公司的系統應該達到十萬的訂單處理速度(2019年,阿里的訂單創建峰值已經達到了五十萬左右),以及一系列類似的系統要求。
  3. 然後,根據前面制定的一系列系統要求。開始規劃系統的拆分,將目標系統進行邏輯的拆分。但是如果已經有了系統基礎,那麼就需要在原有的系統上,進行調整。如阿里雲平台必須保證在雙十一時提供足夠強大的基礎設施支撐(可以在必要時,支撐大當量的橫向擴展)。又如支付寶需要確保在雙十一零點(流量衝擊的峰值)保證可以支撐百萬計的支付請求,並且妥善處理超出的部分。這樣就完成了目標的落地方案生成。
  4. 接下來,需要確定工作的優先級別,以及開發順序。由於總的開發資源是有限的,並且不同業務之間存在各種錯綜複雜的關係,所以,需要CTO站在公司高層角度,進行這個層次的方案安排。如由於阿里雲是其它業務的支撐基礎,是實現目標的必要因素(甚至都沒有太多業務妥協的空間),那麼我們可以將它作為優先級第一的工作,並最大程度地傾斜企業資源等。其它工作以此類推。從複雜的角度,可以了解項目組合管理,以及企業戰略管理等(但是分析師考試不會這麼複雜的,這裏只是舉個栗子)。
  5. 接着,我需要對這些方案進行可行性分析。畢竟這些方案可能是我根據自己的經驗,以及周邊個別的意見,花了一天的時間整合出來的。其中可能存在一些不合理,不科學的部分,而我並不了解(個人的力量與認知畢竟有限。也許我對軟件十分清楚,但是在硬件上提出了超脫物理規律的不可能方案,而我想當然地認為可以)。所以,需要進行可行性分析。甚至通過論證會議,進行具體的論證,從而確定方案是否可行,是否需要調整。
  6. 最後,根據前面這些簡要的方案與意見,通過自己與下屬不斷溝通,從而形成一份具體的系統設計的方案-系統設計任務書。作為接下來實施系統建設工作的具體依據。

可行性分析

這裏着重提一下這個部分,這個部分充分體現了系統的業務特性。

從考試角度說,這個部分雖然不是一個章節,但是每次考試都會考幾分。

從實際角度來說,可能一些感興趣的比較清楚,阿里的p7-p8,騰訊的t3-t4,晉陞都是有一定困難的。晉陞條件中有一條,需要對業務有足夠的了解。就如同國外對程序員的分級看法,也對程序員的產品思維有着非常重的要求。大廠要求程序員對業務有一定的了解,一方面是確定經歷真實性,另一方面就是觀察面試者的業務思維,產品思維。可能p7,只是要求根據業務場景,實現業務方案,並適當根據技術,提出對業務的看法(如調整業務,提出新的業務特性等)。到了p8-p9就是希望你能在一個大的目標下,提出自己的產品理念。在項目經理的產品模型或產品想法的基礎上,站在技術角度,給出新的產品想法,去完善產品模型,提高產品的層次。

以上看法,不保證完全正確,畢竟不同人的解讀是不同的。但是還是值得參考一下的(日後我晉陞p8,p9,考慮回來更新一下)。

那麼可行性分析可以帶來產品思維嘛?

當然,,,不能。

但是,可行性分析,絕對是一個絕佳的入門機會。可以幫助開發人員,從一個新的角度去看待自己開發的系統。並且這個入門的門檻很低,開發人員可以很好的理解,入門。後續的道路,就需要各位自己走了。畢竟完事開頭難,開始解決了,後面走下去就行了。當然後續有更多積累了,我會分享我對產品,業務的看法。

至於其它部分,都比較簡單,就不在此深入談論了。

學習必要性

考試的必要性,我只說一句,分值可觀。

現實的意義,我總結了三點比較重要的:

  • 提升高度。通過這部分的學習,可以為你晉陞公司技術高層埋下一顆種子。起碼你和Boss扯皮的時候,不再是只有技術名詞了。
  • 改善眼界。通過這部分的學習,可以改變你對信息系統的純技術看法。有時候,人的改變,只是需要一個開始。
  • 心中有數。通過這部分的學習,可以令你明白現有項目的一些基本商業特性。起碼你知道你的項目能走多遠,是不是該跑路了。囧

這個章節就這些內容,如果有什麼不清楚的,可以@我。如果有正在準備考試或已經通過考試的,或有交流需求的,可以@我,加群。

希望這篇博客對大家有所幫助。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

文件上傳

文件上傳是 Web 開發常見需求,上傳文件需要用到文件輸入框,如果給文件輸入框添加一個 multiple 屬性則可以一次選擇多個文件(不支持的瀏覽器會自動忽略這個屬性)

<input multiple type="file">

點擊這個輸入框就可以打開瀏覽文件對話框選擇文件了,一般一個輸入框上傳一個文件就行,要上傳多個文件也可以用多個輸入框來處理,這樣做是為了兼容那些不支持 multiple 屬性的瀏覽器,同時用戶一般也不會選擇多個文件

基本上傳方式

當把文件輸入框放入表單中,提交表單的時候即可將選中的文件一起提交上傳到服務器,需要注意的是由於提交的表單中包含文件,因此要修改一下錶單元素的 enctype 屬性為 multipart/form-data

<form action="#" enctype="multipart/form-data" method="post">
  <input name="file" type="file">
  <button type="submit">Upload</button>
</form>

這樣上傳方式是傳統的同步上傳,上傳的文件如果很大,往往需要等待很久,上傳完成后頁面還會重新加載,並且必須等待上傳完成后才能繼續操作

早期的瀏覽器並不支持異步上傳,不過可以使用 iframe 來模擬,在頁面中隱藏一個 <iframe> 元素,指定一個 name 值,同時將 <form> 元素的 target 屬性值指定為 <iframe> 元素的 name 屬性的值,將兩者關聯起來

<form action="#" enctype="multipart/form-data" method="post" target="upload-frame">
  <input name="file" type="file">
  <button type="submit">Upload</button>
</form>
<iframe id="upload-frame" name="upload-frame" src="about:blank" style="display: none;"></iframe>

這樣在提交表單上傳的時候,頁面就不會重新加載了,取而代之的是 iframe 重新加載了,不過 iframe 原本就是隱藏的,即使重新加載也不會感知到

訪問文件

File API 提供了訪問文件的能力,通過輸入框的 files 屬性訪問,這會得到一個 FileList,這是一個集合,如果只選擇了一個文件,那麼集合中的第一個元素就是這個文件

var input = document.querySelector('input[type="file"]')
var file = input.files[0]

console.log(file.name) // 文件名稱
console.log(file.size) // 文件大小
console.log(file.type) // 文件類型

支持 File API 的瀏覽器可以參考

Ajax 上傳

由於可以通過 File API 直接訪問文件內容,再結合 XMLHttpRequest 對象直接將文件上傳,將其作為參數傳給 XMLHttpRequest 對象的 send 方法即可

var xhr = new XMLHttpRequest()
xhr.open('POST', '/upload/url', true)
xhr.send(file)

不過一些原因不建議直接這樣傳遞文件,而是使用 FormData 對象來包裝需要上傳的文件,FormData 是一個構造函數,使用的時候先 new 一個實例,然後通過實例的 append 方法向其中添加數據,直接把需要上傳的文件添加進去

var formData = new FormData()
formData.append('file', file, file.name) // 第 3 個參數是文件名稱
formData.append('username', 'Mary') // 還可以添加額外的參數

甚至也可以直接把表單元素作為實例化參數,這樣整個表單中的數據就全部包含進去了

var formData = new FormData(document.querySelector('form'))

數據準備好后,就是上傳了,同樣是作為參數傳給 XMLHttpRequest 對象的 send 方法

var xhr = new XMLHttpRequest()
xhr.open('POST', '/upload/url', true)
xhr.send(formData)

監測上傳進度

XMLHttpRequest 對象還提供了一個 progress 事件,基於這個事件可以知道上傳進度如何

var xhr = new XMLHttpRequest()
xhr.open('POST', '/upload/url', true)
xhr.upload.onprogress = progressHandler // 這個函數接下來定義

上傳的 progress 事件由 xhr.upload 對象觸發,在事件處理程序中使用這個事件對象的 loaded(已上傳字節數) 和 total(總數) 屬性來計算上傳的進度

function progressHandler(e) {
  var percent = Math.round((e.loaded / e.total) * 100)
}

上面的計算會得到一個表示完成百分比的数字,不過這兩個值也不一定總會有,保險一點先判斷一下事件對象的 lengthComputable 屬性

function progressHandler(e) {
  if (e.lengthComputable) {
    var percent = Math.round((e.loaded / e.total) * 100)
  }
}

支持 Ajax 上傳的瀏覽器可以參考

分割上傳

使用文件對象的 slice 方法可以分割文件,給該方法傳遞兩個參數,一個起始位置和一個結束位置,這會返回一個新的 Blob 對象,包含原文件從起始位置到結束位置的那一部分(文件 File 對象其實也是 Blob 對象,這可以通過 file instanceof Blob 確定,Blob 是 File 的父類)

var blob = file.slice(0, 1024) // 文件從字節位置 0 到字節位置 1024 那 1KB

將文件分割成幾個 Blob 對象分別上傳就能實現將大文件分割上傳

function upload(file) {
  let formData = new FormData()
  formData.append('file', file)
  let xhr = new XMLHttpRequest()
  xhr.open('POST', '/upload/url', true)
  xhr.send(formData)
}

var blob = file.slice(0, 1024)
upload(blob) // 上傳第一部分

var blob2 = file.slice(1024, 2048)
upload(blob2) // 上傳第二部分

// 上傳剩餘部分

通常用一個循環來處理更方便

var pos = 0 // 起始位置
var size = 1024 // 塊的大小

while (pos < file.size) {
  let blob = file.slice(pos, pos + size) // 結束位置 = 起始位置 + 塊大小

  upload(blob)
  pos += size // 下次從結束位置開始繼續分割
}

服務器接收到分塊文件進行重新組裝的代碼就不在這裏展示了

使用這種方式上傳文件會一次性發送多個 HTTP 請求,那麼如何處理這種多個請求同時發送的情況呢?方法有很多,可以用 Promise 來處理,讓每次上傳都返回一個 promise 對象,然後用 Promise.all 方法來合併處理,Promise.all 方法接受一個數組作為參數,因此將每次上傳返回的 promise 對象放在一個數組中

var promises = []

while (pos < file.size) {
  let blob = file.slice(pos, pos + size)

  promises.push(upload(blob)) // upload 應該返回一個 promise
  pos += size
}

同時改造一下 upload 函數使其返回一個 promise

function upload(file) {
  return new Promise((resolve, reject) => {
    let formData = new FormData()
    formData.append('file', file)
    let xhr = new XMLHttpRequest()
    xhr.open('POST', '/upload/url', true)
    xhr.onload = () => resolve(xhr.responseText)
    xhr.onerror = () => reject(xhr.statusText)
    xhr.send(formData)
  })
}

當一切完成后

Promise.all(promises).then((response) => {
  console.log('Upload success!')
}).catch((err) => {
  console.log(err)
})

支持文件分割的瀏覽器可以參考

判斷一下文件對象是否有該方法就能知道瀏覽器是否支持該方法,對於早期的部分版本瀏覽器需要加上對應的瀏覽器廠商前綴

var slice = file.slice || file.webkitSlice || file.mozSlice

if (slice) {
  let blob = slice.call(file, 0, 1024) // call
  upload(blob)
} else {
  upload(file) // 不支持分割就只能直接上傳整個文件了,或者提示文件過大
}

拖拽上傳

通過拖拽 API 可以實現拖拽文件上傳,默認情況下,拖拽一個文件到瀏覽器中,瀏覽器會嘗試打開這個文件,要使用拖拽功能需要阻止這個默認行為

document.addEventListener('dragover', function(e) {
  e.preventDefault()
  e.stopPropagation()
})

任意指定一個元素來作為釋放拖拽的區域,給一個元素綁定 drop 事件

var element = document.querySelector('label')
element.addEventListener('drop', function(e) {
  e.preventDefault()
  e.stopPropagation()

  // ...
})

通過該事件對象的 dataTransfer 屬性獲取文件,然後上傳即可

var file = e.dataTransfer.files[0]
upload(file) // upload 函數前面已經定義

選擇類型

給文件輸入框添加 accept 屬性即可指定選擇文件的類型,比如要選擇 png 格式的圖片,則指定其值為 image/png,如果要允許選擇所有類型的圖片,就是 image/*

<input accept="image/*" type="file">

添加 capture 屬性可以調用設備機能,比如 capture="camera" 可以調用相機拍照,不過這並不是一個標準屬性,不同設備實現方式也不一樣,需要注意

<input accept="image/*" capture="camera" type="file">

經測 iOS 設備添加該屬性后只能拍照而不能從相冊選擇文件了,所以判斷一下

if (iOS) { // iOS 用 navigator.userAgent 判斷
  input.removeAttribute('capture')
}

不支持的瀏覽器會自動忽略這些屬性

自定義樣式

文件輸入框在各個瀏覽器中呈現的樣子都不大相同,而且給 input 定義樣式也不是那麼方便,如果有需要應用自定義樣式,有一個技巧,可以用一個 label 關聯到這個文件輸入框,當點擊這個 label 元素的時候就會觸發文件輸入框的點擊,打開瀏覽文件的對話框,相當於點擊了文件輸入框一樣的效果

<label for="file-input"></label>
<input id="file-input" style="clip: rect(0,0,0,0); position: absolute;" type="file">

這時就可以將原本的文件輸入框隱藏了,然後給 label 元素任意地應用樣式,畢竟要給 label 元素應用樣式比 input 方便得多

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

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

Spark(一)—— 大數據處理入門

一、Spark介紹

Apache Spark is a fast and general-purpose cluster computing system. It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs. It also supports a rich set of higher-level tools including Spark SQL for SQL and structured data processing, MLlib for machine learning, GraphX for graph processing, and Spark Streaming.

Spark是一個快速且多功能的集群計算系統。它為多種不同語言提供高級API,和支持一般執行圖的優化引擎。它也有豐富的高級工具集,Spark SQL進行結構化數據的處理,MLib處理機器學習,GraphX進行圖處理,以及Spark Streaming流計算。

組成

它的主要組件有:

  • SparkCore
    • 將分佈式數據抽象為彈性分佈式數據集(RDD),實現了應用任務調度、RPC、序列化和壓縮,併為運行在其上的上層組件提供API。
  • SparkSQL
    • Spark Sql 是Spark來操作結構化數據的程序包,可以讓我使用SQL語句的方式來查詢數據,Spark支持 多種數據源,包含Hive表,parquest以及JSON等內容。
  • SparkStreaming
    • 是Spark提供的實時數據進行流式計算的組件。
  • MLlib
    • 提供常用機器學習算法的實現庫。
  • GraphX
    • 提供一個分佈式圖計算框架,能高效進行圖計算。
  • BlinkDB
    • 用於在海量數據上進行交互式SQL的近似查詢引擎。
  • Tachyon
    • 以內存為中心高容錯的的分佈式文件系統。

返回一個包含數據集前n個元素的數組

二、WordCount程序講解

編寫代碼

scala程序編寫

object WordCountDemo {

  def main(args: Array[String]): Unit = {
    //創建Spark配置對象
    val conf = new SparkConf().setMaster("local").setAppName("MyApp")
    //通過conf創建sc
    val sc = new SparkContext(conf)
    //讀取文件
    val rdd1 = sc.textFile("/Users/README.md")
    //計算
    val rdd2 = rdd1.flatMap(line => line.split(" ")).map(word => (word,1)).reduceByKey(_+_)
    //打印
    rdd2.take(10).foreach(println)

  }

}

java程序編寫


public class WordCountJavaDemo {

    public static void main(String[] args) {
        
        SparkConf conf = new SparkConf();
        conf.setAppName("myapp").setMaster("local");
        JavaSparkContext sc = new JavaSparkContext(conf);
        JavaRDD<String> rdd1 = sc.textFile("/Users/README.md");
        JavaRDD<String> rdd2 = rdd1.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String s) throws Exception {
                List<String> list = new ArrayList<>();
                String[] arr = s.split(" ");
                for (String ss : arr) {
                    list.add(ss);
                }
                return list.iterator();
            }
        });

        JavaPairRDD<String, Integer> rdd3 = rdd2.mapToPair(new PairFunction<String, String, Integer>() {

            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<String, Integer>(s, 1);
            }

        });

        JavaPairRDD<String, Integer> rdd4 = rdd3.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });

        List<Tuple2<String, Integer>> list = rdd4.collect();
        for (Tuple2<String, Integer> t : list) {
            System.out.println(t._1() + " " + t._2());
        }
    }

}

三、原理介紹

RDD

  • 由一系列Partition組成
  • RDD之間有一系列依賴關係
  • RDD每個算子實際上是作用在每個Partition上
  • RDD會提供一系列最佳位置
  • 分區器是作用在KV格式的RDD上

RDD會在多個節點上存儲,就和hdfs的分佈式道理是一樣的。hdfs文件被切分為多個block存儲在各個節點上,而RDD是被切分為多個partition。不同的partition可能在不同的節點上。

Spark執行流程

1、Driver
分發task,在分發之前,會調用RDD的方法,獲取partition的位置。
將task的計算結果,拉回到Driver端
Driver是一個JVM進程

2、Worker

寬依賴、窄依賴

圖中stage2的并行度是4,也就是有4個task。

寬依賴

父RDD與子RDD,partition的關係是一對多,就是寬依賴。寬依賴於shuffle對應。

窄依賴

父RDD與子RDD,partition的關係是一對一或多對一,就是窄依賴。

四、Spark常用算子

Transformation算子

特點:懶執行

(1)map

map的輸入變換函數應用於RDD中所有元素

(2)flatMap

flatMap與map區別在於map為“映射”,而flatMap“先映射,后扁平化”,map對每一次(func)都產生一個元素,返回一個對象,而flatMap多一步就是將所有對象合併為一個對象。

(3)flatMapValues

每個元素的Value被輸入函數映射為一系列的值,然後這些值再與原RDD中的Key組成一系列新的KV對。

代碼

x = sc.parallelize([("a", ["x", "y", "z"]), ("b", ["p", "r"])])
def f(x): return x
x.flatMapValues(f).collect()

打印結果

 [('a', 'x'), ('a', 'y'), ('a', 'z'), ('b', 'p'), ('b', 'r')]

filter

過濾操作,滿足filter內function函數為true的RDD內所有元素組成一個新的數據集。

(4)groupByKey

主要作用是將相同的所有的鍵值對分組到一個集合序列當中,其順序是不確定的。

(5)reduceByKey

與groupByKey類似,卻有不同。如(a,1), (a,2), (b,1), (b,2)。groupByKey產生中間結果為( (a,1), (a,2) ), ( (b,1), (b,2) )。而reduceByKey為(a,3), (b,3)。

reduceByKey主要作用是聚合,groupByKey主要作用是分組。

(6)take

Action算子

特點:立即觸發執行

五、SparkSQL

介紹

Spark SQL is a Spark module for structured data processing. Unlike the basic Spark RDD API, the interfaces provided by Spark SQL provide Spark with more information about the structure of both the data and the computation being performed. Internally, Spark SQL uses this extra information to perform extra optimizations. There are several ways to interact with Spark SQL including SQL and the Dataset API. When computing a result the same execution engine is used, independent of which API/language you are using to express the computation. This unification means that developers can easily switch back and forth between different APIs based on which provides the most natural way to express a given transformation.

SparkSQL是Spark的一個用來處理結構化數據的模塊。使用類似SQL的方式訪問Hadoop,實現MR計算。

Datasets的概念

A Dataset is a distributed collection of data. Dataset is a new interface added in Spark 1.6 that provides the benefits of RDDs (strong typing, ability to use powerful lambda functions) with the benefits of Spark SQL’s optimized execution engine. A Dataset can be constructed from JVM objects and then manipulated using functional transformations (map, flatMap, filter, etc.). The Dataset API is available in Scala and Java. Python does not have the support for the Dataset API. But due to Python’s dynamic nature, many of the benefits of the Dataset API are already available (i.e. you can access the field of a row by name naturally row.columnName). The case for R is similar.

Dataset是分佈式數據集合。

DataFrames概念

A DataFrame is a Dataset organized into named columns. It is conceptually equivalent to a table in a relational database or a data frame in R/Python, but with richer optimizations under the hood. DataFrames can be constructed from a wide array of sources such as: structured data files, tables in Hive, external databases, or existing RDDs. The DataFrame API is available in Scala, Java, Python, and R. In Scala and Java, a DataFrame is represented by a Dataset of Rows. In the Scala API, DataFrame is simply a type alias of Dataset[Row]. While, in Java API, users need to use Dataset to represent a DataFrame.

基本使用

(1)創建DataFrames

數據

{"id":"1","name":"zhangsan","age":"12"}
{"id":"2","name":"lisi","age":"12"}
{"id":"3","name":"wangwu","age":"12"}

代碼


object SparkSqlDemo {

  def main(args: Array[String]): Unit = {

    //創建Spark配置對象
    val conf = new SparkConf().setMaster("local[4]").setAppName("MyApp");

    val spark = SparkSession
      .builder()
      .appName("Spark SQL basic example")
      .config(conf)
      .getOrCreate()

    val df = spark.read.json("/Users/opensource/dev-problem/source/people_sample_json.json");
    df.show()

  }

}

(2)查詢

val df = spark.read.json("/Users/fangzhijie/opensource/dev-problem/source/people_sample_json.json");
df.createOrReplaceTempView("people")
val sqlDF = spark.sql("SELECT * FROM people WHERE name = 'zhangsan'")
sqlDF.show()

六、SparkStreaming

介紹

Spark Streaming is an extension of the core Spark API that enables scalable, high-throughput, fault-tolerant stream processing of live data streams. Data can be ingested from many sources like Kafka, Flume, Kinesis, or TCP sockets, and can be processed using complex algorithms expressed with high-level functions like map, reduce, join and window. Finally, processed data can be pushed out to filesystems, databases, and live dashboards. In fact, you can apply Spark’s machine learning and graph processing algorithms on data streams.

基本使用

(1)簡單使用


object SparkStreamingDemo {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
    //創建Spark流上下文
    val ssc = new StreamingContext(conf, Seconds(1))
    //創建Socket文本流
    val lines = ssc.socketTextStream("localhost", 9999)
    val words = lines.flatMap(_.split(" "))

    val pairs = words.map(word => (word, 1))
    val wordCounts = pairs.reduceByKey(_ + _)

    // Print the first ten elements of each RDD generated in this DStream to the console
    wordCounts.print()
    //啟動
    ssc.start()
    //等待結束
    ssc.awaitTermination()  // Wait for the computation to terminate


  }

}

使用shell命令監聽端口,輸入待計算內容

$ nc -lk 9999

原理

SparkStreaming的編程抽象是離散化流(DStream),它是一個RDD序列,每個RDD代表數據流中一個時間片內的數據。

參考文檔

《Spark快速大數據分析》

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

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

美軍擬造機動微型核反應爐 解偏遠基地電力需求

摘錄自2020年3月10日中央社報導

美國軍方今(9日)表示,正在規畫部署微型核能反應爐,這種移動式發電廠可用貨車載運,且不受時間限制為偏遠地區的基地提供電力。

法新社報導,美國國防部已經為了這種移動式核反應爐跟三間企業簽訂合約,目標是產生1百萬瓦至5百萬瓦(megawatt)的電力。預計在兩年內,其中一間公司將獲選來生產反應爐的原型。國防部聲明表示,這項名為「裴雷」(Pele)的計畫是透過發展安全的、可移動的、先進的微型核子反應爐,以因應國防部多元的任務需求。

然而,美軍規畫部署微型核能發電設備,部分民間核能專家對此抱持懷疑態度。這些專家認為,若遭遇攻擊,反應爐被破壞後恐導致放射性燃料外洩,或是核物料落入敵人手中且被用來製造低階的髒彈(dirty bomb)。

能源議題
國際新聞
美國
核反應爐
移動

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

武漢肺炎衝擊 IEA:10餘年來石油需求將首度萎縮

摘錄自2020年3月9日中央社報導

國際能源總署(IEA)今(9日)表示,由於2019年冠狀病毒疾病(武漢肺炎)疫情拖累全球經濟活動,2020年全球石油需求料將出現10餘年來首度萎縮。

路透社報導,總部位於法國巴黎的國際能源總署表示,預期2020年石油需求為每日9990萬桶,除較先前預測值減少約100萬桶,也比去年需求少了9萬桶。石油需求將出現2009年以來首度萎縮。國際能源總署執行董事比羅爾(Fatih Birol)發布聲明表示:「這場冠狀病毒危機廣泛影響能源市場,包括煤炭、天然氣與再生能源等。但它對石油市場的衝擊尤其嚴重,因為它阻礙人員與商品流通。」

在國際能源總署大幅下修石油需求預測之際,沙烏地阿拉伯已點燃國際原油價格戰,油價下滑逾1/4,預料還將出現29年來單日最大跌幅。

能源議題
國際新聞
IEA
武漢肺炎
探勘石油

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

百度將在美國測試無人駕駛汽車 計畫2018年前推商用車型

日前,百度首席科學家吳恩達在接受國外媒體採訪時透露,百度將很快在美國測試無人駕駛汽車,並希望在2018年前推出一款可商用的車型。

百度無人駕駛車專案於2013年起步,由百度研究院主導研發,其技術核心是“百度汽車大腦”,包括高精度地圖、定位、感知、智慧決策與控制四大模組。2014年7月,百度首次對外證實啟動“百度無人駕駛汽車”研發計畫。

2015年12月10日,百度宣佈,百度無人駕駛車國內首次實現城市、環路及高速道路混合路況下的全自動駕駛。百度公佈的路測路線顯示,百度無人駕駛車從位於北京中關村軟體園的百度大廈附近出發,駛入G7京新高速公路,經五環路,抵達奧林匹克森林公園,並隨後按原路線返回。百度無人駕駛車往返全程均實現自動駕駛,並實現了多次跟車減速、變道、超車、上下匝道、調頭等複雜駕駛動作,完成了進入高速(匯入車流)到駛出高速(離開車流)的不同道路場景的切換。測試時最高速度達到100公里/小時。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

桃園綠能績效佳,電動車補助高、寺廟積極認購綠電

桃園市近兩年來大力推行各項綠能政策,已小有所成。目前,全市有2.7萬輛電動機車,穩坐台灣六都第一;全台今年綠電認購大戶前兩名,也都是位於桃園的寺廟。

桃園市環保局統計,桃園市目前有2.7萬輛電動機車,相當於每年減排7,452公噸的二氧化碳以及4,023公斤的PM2.5,同時種植65萬棵樹木。目前全桃園市有超過110萬輛機車,電動機車比例僅有2.42%,桃園市今年繼續提供新購電動機車新台幣1.5萬元,加上經濟部工業局的1萬元補助,最高補助金達2.5萬,希望能繼續推動民眾替換機車,進一步提高減碳量。

同時,騎乘電動機車滿三年者,環保局推出更換鋰電池最高新台幣4,000元補助,而合法立案場所設置充電站最高也可享有1萬元補助。環保局還開發手機APP,結合GPS定位系統,幫助使用者快速找到充電站,加強民眾改用電動車的意願。

神明也愛綠電,成全台認購最大手

今年台灣綠電認購量最高的單位,是位於桃園的慈護宮,認購30萬度電;第二名則是桃園中壢仁海宮,認購10萬度電。慈護宮主委劉新發表示,廟宇是台灣民眾生活中心,加上有24小時全年不滅的「光明燈」,用電需求量大,因此除了改用更省電的LED燈之外,也積極參與認購綠電,為環保盡一份心力。

目前台電提出的綠電認購附加費率為每度電新台幣1.06元,最低綠電認購門檻為100度;只要認購最低單位的綠電,就等同於多種五棵樹。

除了寺廟之外,桃園地區也有長榮航空、東龍興業、萬海航運、科德寶遠東等企業加入認購綠電,總認購度數超過百萬度。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

麥格納攜手格特拉克將亮相第六屆中國國際新能源汽車論壇2016

由希邁商務諮詢(上海)有限公司主辦的2016年第六屆中國國際新能源汽車論壇即將於4月20日-4月22日在上海隆重舉行。此次論壇獲得了亞太電動車學會、中國國家新能源機動車產品品質監督檢驗中心、上海交大密西根學院、賽迪顧問及中國綠色能源產業技術創新戰略聯盟的大力支持。截止3月14日,論壇已經確認40位演講嘉賓出席本次論壇並做高品質學術演講。演講嘉賓分別來自菲律賓電動車協會、國家新能源機動車產品品質監督檢驗中心、美國國家能源局、中國工程院等在內的政府單位與研究機構,以及包括寶馬、通用汽車、特斯拉、長安汽車、宇通客車、奇瑞、比亞迪戴姆勒、上汽、北汽、觀致等在內的多個知名整車商,將在論壇上共同研討新能源汽車行業政策趨勢、技術路線及難點、基礎設施建設、商業模式,延續以往的豐碩成果,繼續為新能源汽車行業作出貢獻。

作為全球首屈一指的汽車零部件供應商,麥格納旗下動力總成事業部此次將攜手格特拉克成為本屆論壇的鉑金贊助商,不僅會分享電子驅動和混合驅動控制及新能源汽車熱管理研究的技術報告,還將同期展示新能源汽車領域的前沿產品。麥格納動力總成是全球領先的汽車動力總成系統供應商,集傳動系統設計、研發、試驗和製造能力為一體,以世界級的製造水準不斷實現產品和工藝的創新,為國內外客戶傳遞卓越價值。

本屆論壇相比歷屆舉辦規模最大的第六屆新能源汽車論壇,涉及主論壇及三個分論壇、考察活動、頒獎典禮和交流晚宴。屆時將誠邀全球範圍內的整車製造商、電網電力公司、電池廠商、零部件供應商、核心技術提供商和政府官員近500位行業人士一起,對新能源汽車產業面臨的挑戰,機遇與對策各方面進行為期三天更深層次並具有建設和戰略性的探討。

如需更多詳情,請諮詢:
大會連絡人:Hill ZENG(曾先生)
聯繫電話:0086 21-6045 1760 或郵箱
我們期待與貴單位一起出席於2016年4月20-22日在上海舉辦的第六屆中國國際新能源汽車論壇2016,以利決策!

 

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

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

flink 流式處理中如何集成mybatis框架

flink 中自身雖然實現了大量的connectors,如下圖所示,也實現了jdbc的connector,可以通過jdbc 去操作數據庫,但是flink-jdbc包中對數據庫的操作是以ROW來操作並且對數據庫事務的控制比較死板,有時候操作關係型數據庫我們會非常懷念在java web應用開發中的非常優秀的mybatis框架,那麼其實flink中是可以自己集成mybatis進來的。 我們這裏以flink 1.9版本為例來進行集成。

如下圖為flink內部自帶的flink-jdbc:

 

創建一個flink的流式處理項目,引入flink的maven依賴和mybatis依賴(注意這裏引入的是非spring版本,也就是mybatis的單機版):

<properties>

<flink.version>1.9.0</flink.version>
</properties>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
<!-- flink java 包 -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-streaming-java_2.11</artifactId>
    <version>${flink.version}</version>
</dependency>

maven依賴引入以後,那麼需要在resources下面定義mybatis-config.xml 配置:

mybatis-config.xml 需要定義如下配置:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="BankBillPublic" type="xxxx.xx.xx.BankBillPublic" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://xx.xx.xx.xx:3306/hue?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&autoReconnect=true" />
                <property name="username" value="xxxx" />
                <property name="password" value="xxxx*123%" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/xxxxxMapper.xml" />
    </mappers>
</configuration>

typeAlias 標籤中為自定義的數據類型,然後在xxxxxMapper.xml 中parameterType或者resultType就可以直接用這種定義的數據類型。

dataSource type=”POOLED” 我們使用的是mybatis中的POOLED 類型,也就是連接池的方式去使用。默認支持如下這三種類型。

 我們也可以使用阿里巴巴開源的druid連接池,那麼就需要引入對應的maven依賴,如下所示:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.14</version>
        </dependency>  

 然後定義一個對應的druid的DataSource,如下所示:

import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.DataSourceFactory;
import com.alibaba.druid.pool.DruidDataSource;

public class DruidDataSourceFactory implements DataSourceFactory {
    private Properties props;

    @Override
    public DataSource getDataSource() {
        DruidDataSource dds = new DruidDataSource();
        dds.setDriverClassName(this.props.getProperty("driver"));
        dds.setUrl(this.props.getProperty("url"));
        dds.setUsername(this.props.getProperty("username"));
        dds.setPassword(this.props.getProperty("password"));
        // 其他配置可以根據MyBatis主配置文件進行配置
        try {
            dds.init();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dds;
    }

    @Override
    public void setProperties(Properties props) {
        this.props = props;
    }
}

之後就可以mybatis的配置中使用了,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="BankBillPublic" type="xxxx.xx.xx.BankBillPublic" />
        <typeAlias alias="DRUID" 
 type="com.xx.mybatis.druid.utils.DruidDataSourceFactory" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="DRUID">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://xx.xx.xx.xx:3306/hue?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&autoReconnect=true" />
                <property name="username" value="xxxx" />
                <property name="password" value="xxxx*123%" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/xxxxxMapper.xml" />
    </mappers>
</configuration>

<mappers> 下面為定義的mybatis 的xxxxxMapper文件。裏面放置的都是sql語句。

本文作者張永清,轉載請註明出處:

xxxxxMapper.xml 中的sql示例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xx.xx.bigdata.flink.xx.xx.mapper.UserRelaInfoMapper">
    <!--查詢關鍵字匹配 -->
    <select id="queryUserRelaInfo" parameterType="String" resultType="UserRelaInfo">
        SELECT id AS id,
        USER_NAME AS userName,
        APPL_IDCARD AS applIdCard,
        PEER_USER AS peerUser,
        RELA_TYPE AS relaType,
        CREATE_USER AS createUser,
        CREATE_TIME AS createTime
        FROM USER_RELA_INFO
        <where>
            <if test="applIdCard != null">
                APPL_IDCARD=#{applIdCard}
            </if>
            <if test="peerUser != null">
            AND PEER_USER=#{peerUser}
            </if>
        </where>
    </select>
</mapper>

 定義Mapper,一般可以定義一個interface ,和xxxxxMapper.xml中的namespace保持一致

注意傳入的參數一般加上@Param 註解,傳入的參數和xxxxxMapper.xml中需要的參數保持一致

public interface UserRelaInfoMapper {
    List<UserRelaInfo> queryUserRelaInfo(@Param("applIdCard")String applIdCard,@Param("peerUser") String peerUser);
}

定義SessionFactory工廠(單例模式):

/**
 *
 *  sqlsession factory 單例  事務設置為手動提交
 */
public class MybatisSessionFactory {
    private static final Logger LOG = LoggerFactory.getLogger(MybatisSessionFactory.class);
    private static SqlSessionFactory sqlSessionFactory;
    private MybatisSessionFactory(){
        super();
    }
    public synchronized static SqlSessionFactory getSqlSessionFactory(){
        if(null==sqlSessionFactory){
            InputStream inputStream=null;
            try{
                inputStream = MybatisSessionFactory.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
            catch (Exception e){
                LOG.error("create MybatisSessionFactory read mybatis-config.xml cause Exception",e);
            }
            if(null!=sqlSessionFactory){
                LOG.info("get Mybatis sqlsession sucessed....");
            }
            else {
                LOG.info("get Mybatis sqlsession failed....");
            }
        }
        return sqlSessionFactory;
    }
}

  

使用mybatis 對數據庫進行操作:

        SqlSession sqlSession = MybatisSessionFactory.getSqlSessionFactory().openSession();
        UserRelaInfoMapper  userRelaInfoMapper  = sqlSession.getMapper(UserRelaInfoMapper .class);
		//調用對應的方法
		userRelaInfoMapper.xxxx();
		//提交事務
		sqlSession.commit();
		//回滾事務,一般可以捕獲異常,在發生Exception的時候,事務進行回滾
		sqlSession.rollback();
		
		
		

這裏以mysql為示例,寫一個flink下mysql的sink示例,可以自己來靈活控制事務的提交:

public class MysqlSinkFunction<IN> extends RichSinkFunction {
    private static final Logger LOG = LoggerFactory.getLogger(MysqlSinkFunction.class);
    @Override
    public void invoke(Object value, Context context) throws Exception{
        SqlSession sqlSession = MybatisSessionFactory.getSqlSessionFactory().openSession();
        try{
                            //插入
                            LOG.info("MysqlSinkFunction start to do insert data...");
                            xxx.xxx();
							//更新
                            LOG.info("MysqlSinkFunction start to do update data...");
							xxx.xxx();
                            //刪除
                            LOG.info("MysqlSinkFunction start to do delete data...");
							xxx.xxx();

                    
                
                sqlSession.commit();
                LOG.info("MysqlSinkFunction commit transaction success...");
        }
        catch (Throwable e){
            sqlSession.rollback();
            LOG.error("MysqlSinkFunction cause Exception,sqlSession transaction rollback...",e);
        }
    }
}  
相信您如果以前在spring中用過mybatis的話,對上面的這些操作一定不會陌生。由此你也可以發現,在大數據中可以完美的集成mybatis,這樣可以發揮mybatis框架對數據庫操作的優勢,使用起來也非常簡單方便。
一旦集成了mybaitis后,在flink中就可以方便的對各種各樣的關係型數據庫進行操作了。

本文作者張永清,轉載請註明出處:

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

一個類GraphQL的ORM數據訪問框架發布

Zongsoft.Data 發布公告

很高興我們的 ORM 數據訪問框架()在歷經兩個 SaaS 產品的應用之後,今天正式宣布對外推廣!
這是一個類  風格的 ORM(Object/Relational Mapping) 數據訪問框架。

又一個輪子?

在很長時間里,.NET 陣營似乎一直缺乏一個被普遍使用的 ORM 數據訪問框架,從最早的原生 ADO.NET 到舶來品 iBatis.NETHibernate.NET,後來又經歷了 Linq for SQL 與 Entity Framework 的混戰,可能是因為 Entity Framework 早期版本的模糊定位和反覆變更的設計導致了它失之霸主之位,進而造就了一段百舸爭流、群雄共逐的戰國時代。在歷經漫長而反覆的期待、失望、糾結和痛苦之後,我終於決定動手造一個輪子。

設計理念

在開始動手之前,先確定以下基本設計原則:

  • 數據庫優先(Database First)
  • 嚴格的 POCO/POJO 支持
  • 映射模型與代碼完全隔離
  • 禁止業務層出現 SQL 和類 SQL 代碼

在一個業務系統中,數據結構及其關係毋庸置疑是最底層的基礎性結構,數據庫應由系統架構師或開發負責人進行仔細設計 No Schema/Weakly Schema 的思潮是塗抹了蜂蜜的毒藥),數據訪問映射以數據庫表結構關係為基石,在此之上業務層亦以概念映射模型為準繩,層級之間相互隔離。

領域模型實體避免通過註解 (標籤) 來進行元數據定義,應確保嚴格符合 POCO/POJO 範式。通過語義化的 Schema 來聲明訪問的數據結構關係,禁止應用層的 SQLLinq 式的類 SQL 代碼可降低業務層對數據層的依賴、提升代碼可維護性外,還具備更加統一可控的便利性,併為數據訪問引擎的實現提供了更大的優化空間和自由度。

範例說明

下面通過三個的例子 (注:例子均基於 項目) 來佐證上面的部分設計理念,更多示例和闡述請參考  項目的  文檔和  項目的代碼。

提示: 下面的範例均基於 開源項目,該項目是一個完整的論壇社區的後台程序。你可能需要預先閱讀一下該項目的文檔,以便更好的理解範例代碼的業務邏輯。

示例一

導航查詢及導航過濾

var forums = this.DataAccess.Select<Forum>(
    Condition.Equal("SiteId", this.User.SiteId) &
    Condition.In("Visibility", Visibility.Internal, Visibility.Public) |
    (
        Condition.Equal("Visibility", Visibility.Specified) &
        Condition.Exists("Users",
                  Condition.Equal("UserId", this.User.UserId) &
                  (
                      Condition.Equal("IsModerator", true) |
                      Condition.NotEqual("Permission", Permission.None)
                  )
        )
    ),
    "*, MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}}"
);

上述數據訪問的查詢方法大致生成如下SQL腳本:

SELECT
    t.*,
    t1.ThreadId AS 'MostRecentThread.ThreadId',
    t1.Title AS 'MostRecentThread.Title',
    t1.CreatorId AS 'MostRecentThread.CreatorId',
    t2.UserId AS 'MostRecentThread.Creator.UserId',
    t2.Name AS 'MostRecentThread.Creator.Name',
    t2.Nickname AS 'MostRecentThread.Creator.Nickname',
    t2.Avatar AS 'MostRecentThread.Creator.Avatar'
FROM Forum t
    LEFT JOIN Thread AS t1 ON
        t.MostRecentThreadId=t1.ThreadId
    LEFT JOIN UserProfile AS t2 ON
        t1.CreatorId=t2.UserId
WHERE
    t.SiteId = @p1 AND
    t.Visibility IN (@p2, @p3) OR
    (
        t.Visibility = @p4 AND
        EXISTS
        (
            SELECT u.SiteId, u.ForumId, u.UserId
            FROM ForumUser u
            WHERE u.SiteId = t.SiteId AND
                  u.ForumId = t.ForumId AND
                  u.UserId = @p5 AND
                  (
                      u.IsModerator = @p6 OR
                      u.Permission != @p7
                  )
        )
    );

上述示例通過 Select 查詢方法的 schema 參數 (即值為 *, MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}} 的參數) 從數據結構關係的層次指定了查詢數據的形狀,因而不再需要 SQL 或類 SQL 語法中 JOIN 這樣命令式的語法元素,它不光提供了更簡潔且語義化的 API 訪問方式,而且還給數據訪問引擎底層提供了更大的優化空間和自由度。

如果將 Select 查詢方法的 schema 參數值改為 *,Moderators{*},MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}} 后,數據訪問引擎會將查詢內部分解為一對多的兩條 SQL 語句進行迭代執行,而這些都不需要業務層進行分拆處理,因而提升了效率並降低了業務層的複雜度。

注:Schema 模式表達式通過 Web API 提供給前端應用,將大大減少後端開發的工作量,提升前後端的工作效率。

示例二

一對多的關聯新增

// 構建待新增的實體對象
var forum = new
{
    SiteId = this.User.SiteId,
    GroupId = 100,
    Name = "xxxx",

    // 一對多的導航屬性
    Users = new ForumUser[]
    {
      new ForumUser { UserId = 1001, IsModerator = true },
      new ForumUser { UserId = 1002, Permission = Permission.Read },
      new ForumUser { UserId = 1003, Permission = Permission.Write },
    }
}

// 執行數據新增操作
this.DataAccess.Insert<Forum>(forum, "*, Users{*}");

上述數據訪問的新增方法大致生成如下SQL腳本:

/* 主表插入語句,執行一次 */
INSERT INTO Forum (SiteId,ForumId,GroupId,Name,...) VALUES (@p1,@p2,@p3,@p4,...);

/* 子表插入語句,執行多次 */
INSERT INTO ForumUser (SiteId,ForumId,UserId,Permission,IsModerator) VALUES (@p1,@p2,@p3,@p4,@p5);

上述示例通過 Insert 新增方法的 schema 參數(即值為 *,User{*} 的參數)指定了新增數據的形狀,由數據訪問引擎根據映射定義自動處理底層的 SQL 執行方式,確保業務層代碼的簡潔和更高的執行效率。

示例三

一對一和一對多的關聯更新,對於“一對多”的導航屬性,還能確保該屬性值 (集合類型) 以 UPSERT 模式寫入。

public bool Approve(ulong threadId)
{
    //構建更新的條件
    var criteria =
        Condition.Equal(nameof(Thread.ThreadId), threadId) &
        Condition.Equal(nameof(Thread.Approved), false) &
        Condition.Equal(nameof(Thread.SiteId), this.User.SiteId) &
        Condition.Exists("Forum.Users",
            Condition.Equal(nameof(Forum.ForumUser.UserId), this.User.UserId) &
            Condition.Equal(nameof(Forum.ForumUser.IsModerator), true));

    //執行數據更新操作
    return this.DataAccess.Update<Thread>(new
    {
        Approved = true,
        ApprovedTime = DateTime.Now,
        Post = new
        {
            Approved = true,
        }
    }, criteria, "*,Post{Approved}") > 0;
}

上述數據訪問的更新方法大致生成如下SQL腳本:

/* 以下代碼為支持 OUTPUT/RETURNING 子句的數據庫(如:SQLServer,Oracle,PostgreSQL) */

/* 根據更新的關聯鍵創建臨時表 */
CREATE TABLE #TMP
(
    PostId bigint NOT NULL
);

/* 更新主表,並將更新的關聯鍵輸出到內存臨時表 */
UPDATE T SET
    T.[Approved]=@p1,
    T.[ApprovedTime]=@p2
OUTPUT DELETED.PostId INTO #TMP
FROM [Community_Thread] AS T
    LEFT JOIN [Community_Forum] AS T1 ON /* Forum */
        T1.[SiteId]=T.[SiteId] AND
        T1.[ForumId]=T.[ForumId]
WHERE
    T.[ThreadId]=@p3 AND
    T.[Approved]=@p4 AND
    T.[SiteId]=@p5 AND EXISTS (
        SELECT [SiteId],[ForumId]
        FROM [Community_ForumUser]
        WHERE [SiteId]=T1.[SiteId] AND
              [ForumId]=T1.[ForumId] AND
              [UserId]=@p6 AND
              [IsModerator]=@p7
    );

/* 更新關聯表 */
UPDATE T SET
    T.[Approved]=@p1
FROM [Community_Post] AS T
WHERE EXISTS (
    SELECT [PostId]
    FROM #TMP
    WHERE [PostId]=T.[PostId]);

上述示例通過 Update 更新方法的 schema 參數(即值為 *,Post{Approved} 的參數)指定了更新數據的形狀,數據訪問引擎將根據數據庫類型生成高效的 SQL 語句,對於業務層而言這一切都是無感的、透明的。

對於一對多的導航屬性,數據訪問引擎默認將以 UPSERT 模式處理子集的寫入,關於 UPSERT 更多信息請參考  項目文檔。

性能

我們希望提供最佳的綜合性價比,對於一個 ORM 數據訪問引擎來說,性能的關注點主要 (不限) 有這些要素:

  1. 生成簡潔高效的 SQL 腳本,並盡可能利用特定數據庫的最新 SQL 語法;
  2. 數據查詢結果的實體組裝(Populate)過程必須高效;
  3. 避免反射,有效的語法樹緩存。

實現層面我們採用 Emitting 動態編譯技術對實體組裝(Populate)、數據參數綁定等進行預熱處理,可查閱  等相關類的源碼深入了解。

其他

得益於 “以聲明方式來表達數據結構關係” 的語義化設計理念,相對於命令式設計而言,它使得程序意圖更加聚焦,天然地對底層數據的表達和優化更加寬容與自由。

更多詳細內容 (譬如:讀寫分離、繼承表、數據模式、映射文件、過濾器、驗證器、類型轉換、數據隔離) 請查閱相關文檔。

支持贊助

我們歡迎並期待任何形式的推廣支持!

如果你認同我們的設計理念請為這個項目點贊(Star),如果你認為該項目很有用,並且希望支持它未來的發展,請給予必要的資金來支持它:

  1. 關注 Zongsoft 微信公眾號,對我們的文章進行打賞;
  2. 加入 ,可以獲得在線問答和技術支持;
  3. 如果您的企業需要現場技術支持與輔導,又或者需要開發新功能、即刻的錯誤修復等請給我。

提醒: 本文可能會更新,請閱讀原文:,以避免因內容陳舊而導致的謬誤,同時亦有更好的閱讀體驗。

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

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?