使用 ppx_rapper_lwt 的 SQLite CREATE、INSERT、SELECT
任務
資料庫 / SQLite / SQLite CREATE、INSERT、SELECT
使用的 Opam 套件
- ppx_rapper_lwt 測試版本:3.1.0 — 使用的函式庫:ppx_rapper_lwt
- ppx_rapper 測試版本:3.1.0 — 使用的函式庫:ppx_rapper
- caqti-driver-sqlite3 測試版本:1.9.0 — 使用的函式庫:caqti-driver-sqlite3
- caqti-lwt 測試版本:1.9.0 — 使用的函式庫:caqti-lwt
- lwt 測試版本:5.7.0 — 使用的函式庫:lwt, lwt.unix
程式碼
Caqti/ppx_rapper
組合使用 Lwt 環境。 Lwt 的 let 運算子 ( let* )
和 ( let*? )
的定義與平常一樣,以便簡潔地表示鏈式 promise。 ( let*? )
從回傳的 Ok result
中提取結果,如果值為 Error err
,則停止執行。
let ( let* ) = Lwt.bind
let ( let*? ) = Lwt_result.bind
輔助函式 iter_queries
循序排程查詢清單。每個查詢都是一個函式,它將資料庫的連線控制代碼作為引數。
let iter_queries queries connection =
List.fold_left
(fun a f ->
Lwt_result.bind a (fun () -> f connection))
(Lwt.return (Ok ()))
queries
此處的 %rapper
節點使 ppx_rapper
產生程式碼,以便在套用 create_employees_table () connection
函式時,提供的 SQL CREATE
查詢將在沒有任何參數且沒有從資料庫接收任何資料的情況下執行。
如果查詢成功執行,我們會取回 Ok ()
值,否則我們會取得 Error
值。
let create_employees_table =
[%rapper
execute {sql| CREATE TABLE employees
(name VARCHAR,
firstname VARCHAR,
age INTEGER)
|sql}
]
type employee =
{ name:string; firstname:string; age:int }
let employees = [
{name = "Dupont"; firstname = "Jacques"; age = 36};
{name = "Legendre"; firstname = "Patrick"; age = 42}
]
對於 SQL INSERT
查詢,ppx_rapper
會產生函式 insert_employee (p: employee) connection
。標籤 record_in
會告訴 ppx_rapper
從提供的記錄值讀取 name
、firstname
和 age
的值,而 %[TYPE_NAME]{[INPUT_FIELD_NAME]}
標記法指定對輸入值執行哪些轉換。
let insert_employee =
[%rapper
execute
{sql| INSERT INTO employees VALUES
(%string{name},
%string{firstname},
%int{age})
|sql}
record_in
]
get_many
標籤使 ppx_rapper
產生程式碼,該程式碼查詢資料庫並接收值清單。 record_out
標籤指定每個清單項目都將是一個記錄。
@[TYPE_NAME]{[COLUMN_NAME]}
標記法指定對輸出值執行哪些轉換。
let get_all_employees =
[%rapper
get_many
{sql|SELECT
@string{name},
@string{firstname},
@int{age}
FROM employees
|sql}
record_out
]
這是另一個範例查詢,使用 get_opt
標籤透過 SQL WHERE
子句選取單列。此查詢同時具有輸入 (name
) 和輸出值 (name
、firstname
、age
)。
這裡缺少 record_in
標籤使 ppx_rapper
產生程式碼,其中輸入值作為具名引數傳遞。 get_opt
標籤表示結果將是一個選項:如果找不到符合條件的列,則為 None
;如果符合條件的列,則為 Some r
。
let get_employee_by_name =
[%rapper
get_opt
{sql|SELECT
@string{name},
@string{firstname},
@int{age}
FROM employees
WHERE name=%string{name}
|sql}
record_out
]
ppx_rapper
產生的所有查詢函式都採用一個引數和一個 connection
參數。必須使用 employee
類型的值和 connection
呼叫函式 insert_employee
。若要從清單插入多個記錄,我們使用 List.map
來建立函式清單。當呼叫時,這些函式中的每一個都會執行其相關聯的查詢。函式 iter_queries
會依序執行查詢。
請注意,如果您必須插入多個記錄,則執行批次插入查詢是合理的。
let execute_queries connection =
let*? () = create_employees_table () connection in
let*? () =
iter_queries
(List.map insert_employee employees)
connection
in
let*? employees = get_all_employees () connection in
employees |> List.iter (fun employees ->
Printf.printf
"name=%s, firstname=%s, age=%d\n"
employees.name
employees.firstname
employees.age);
let*? employees =
get_employee_by_name ~name:"Dupont" connection
in
match employees with
| Some employees' ->
Printf.printf
"found:name=%s, firstname=%s, age=%d\n"
employees'.name
employees'.firstname
employees'.age;
Lwt_result.return ()
| None ->
print_string "Not found";
Lwt_result.return ()
主程式首先建立一個 Lwt 環境。函式 with_connection
會開啟資料庫,使用 connection
資料庫控制代碼執行函式,並再次關閉資料庫連線,即使發生例外狀況也是如此。
let () =
match Lwt_main.run @@
Caqti_lwt.with_connection
(Uri.of_string "sqlite3:essai.sqlite")
execute_queries
with
| Result.Ok () ->
print_string "OK\n"
| Result.Error err ->
print_string (Caqti_error.show err)
討論
Caqti
函式庫允許使用 SQLite、MariaDB 和 PostgreSQL 進行可攜式程式設計。 ppx_rapper
將註解的 SQL 字串轉換為 Caqti
查詢。此預處理器使所有類型轉換透明化,並利用 OCaml 的強類型。它也會檢查給定查詢的 SQL 語法。請參閱 Caqti
參考頁面和 ppx_rapper
參考頁面。