- vừa được xem lúc

[Database] Bài 5 - VIết Code Quản Lý Một Database Đơn Giản (Tiếp Theo)

0 0 18

Người đăng: Semi Art

Theo Viblo Asia

Trường hợp sử dụng của insert là khi chúng ta tạo một danh mục mới trên giao diện quản lý blog. Lúc này biểu mẫu nhập liệu <form> có chứa các thành phần như ô nhập tên name, ô nhập các từ khóa liên quan keywords, khung soạn thảo nội dung markdown. Các dữ liệu thu được ở đây sẽ được gửi tới một endpoint đặc định để yêu cầu thêm bản ghi mới vào database.

Code xử lý của route tương ứng sẽ sử dụng class Category để tạo ra một object mô tả bản ghi mới và gọi thủ tục insert-category qua manager. Như vậy là chúng ta chỉ còn thiếu một trị số id mới - được quản lý bởi code database là hoàn thiện đủ nội dung của một bản ghi. Tham số out_ ở đây là tùy chọn để code bên ngoài có thể gắn một object Category bất kỳ và theo dõi kết quả thực hiện insert. Như vậy chúng ta sẽ có các tác vụ chia nhỏ cần xử lý là -

  • /* generate new record's id */ -tạo ra một giá trị id mới
  • /* populate output */ - ghi dữ liệu hoàn chỉnh của bản ghi mới vào out_inserted
  • /* write to data folder */ - ghi dữ liệu của bản ghi mới vào thư mục data
const Category = require("../../type/Category")
const generateNewRecordId = require("./sub-procedure/generate-new-record-id--async-throw")
const writeRecordToDataFolder = require("./sub-procedure/write-record-to-data-folder--async-throw") module.exports = async ( in_submitted = new Category(), out_inserted = new Category()
) => { try { /* generate new record's id */ var generated = { recordId: null } await generateNewRecordId(generated) /* populate output */ Category.clone(in_submitted, out_inserted) out_inserted.set("@id", generated.recordId) /* write to data folder */ await writeRecordToDataFolder(out_inserted) } catch (error) { throw error }
} // module.exports

Đối với tác vụ đầu tiên, việc tạo ra một id mới sẽ được thực hiện bằng cách tách lấy giá trị id của bản ghi cuối cùng và tăng lên một đơn vị. Kết quả được trả về qua một object bất kỳ có thuộc tính recordId.

const readAllRecordFolderNames = require("./read-all-record-folder-names--async-throw"); module.exports = async ( out_generated = { recordId: null }
) => { try { /* collect all record folder names */ var allRecordFolderNames = [] await readAllRecordFolderNames(allRecordFolderNames) /* get the latest id */ var latestRecordFolderName = allRecordFolderNames.pop() var [ _, __, latestRecordId ] = latestRecordFolderName.match(/(id-)(\d+)/) /* generate new id */ var newRecordIdNumber = Number.parstInt(latestRecordId) + 1 out_generated.recordId = String(newRecordIdNumber).padStart(2, "0") } catch (error) { throw error }
} // module.exports

Do chúng ta đã sử dụng các trị số id để làm tên thư mục dữ liệu của các bản ghi, vì vậy nên thao tác tách lấy các giá trị id có thể dừng ở bước đọc tên của các thư mục và chưa cần mở các tệp header.json bên trong. Ở đây chúng ta chỉ cần lưu ý về dạng của trị số id khi ghi vào database hoặc gửi cho trình duyệt web cần phải đồng nhất là dạng chuỗi string có hai chữ số.

Thao tác đọc và thu thập tên thư mục của tất cả các bản ghi được tách thành một sub-procedure để có thể sử dụng lại cho các procedure khác - và được hỗ trợ xử lý bởi module File System.

const fsPromises = require("fs/promises")
const path = require("path") module.exports = async ( out_allFolderNames = []
) => { try { var pathToCategoryDataFolder = path.join(__dirname, "../../../data/category") var dir = await fsPromises.opendir(pathToCategoryDataFolder) for await (var dirEnt of dir) { out_allFolderNames.push(dirEnt.name) } // for await } catch (error) { throw error }
} // module.exports

Bây giờ thì chúng ta đã có thể viết một vài dòng trong test.js để kiểm tra hoạt động của các sub-procedure hỗ trợ tạo ra một id mới rồi. Đầu tiên chúng ta sẽ kiểm tra thao tác đọc tên của tất cả các thư mục bản ghi category.

const readAllCategoryRecordFolderNames = require("./database/procedure/category/sub-procedure/read-all-record-folder-names--async-throw") void async function() { var allCategoryRecordFolderNames = [] await readAllCategoryRecordFolderNames(allCategoryRecordFolderNames) for (var folderName of allCategoryRecordFolderNames) { console.log(folderName) }
} () // void

CMD | Terminal

npm test id-00
id-01
id-02

Vậy giá trị id tiếp theo sẽ được tạo ra là "03".

const generateNewCategoryRecordId = require("./database/procedure/category/sub-procedure/generate-new-record-id--async-throw") void async function() { var newId = { recordId: null } await generateNewCategoryRecordId(newId) console.log(newId)
} () // void

CMD | Terminal

npm test { recordId: '03' }

Sau khi đã có được id mới thì thao tác tiếp theo mà chúng ta cần xử lý là tạo ra một bản object mô tả một bản ghi category hoàn chỉnh để ghi vào thư mục data và đồng thời là để dùng làm kết quả trả về cho code gọi thủ tục insert.

/* requires ... */ module.exports = async ( in_submitted = new Category(), out_inserted = new Category()
) => { try { /* generate new record's id ... (done) */ /* populate output */ Category.clone(in_submitted, out_inserted) out_inserted.set("@id", generated.recordId) /* write to data folder ... */ } catch (error) { throw error }
} // module.exports

Công việc cần thực hiện ở thao tác này thì về cơ bản chỉ là sao chép nội dung từ object được gửi đến in_submitted vào object kết quả out_inserted và sau đó gắn thêm trị số id vừa mới được tạo ra ở bước trước đó. Ở đây chúng ta sẽ ủy thác việc sao chép nội dung từ in_submitted vào out_inserted cho một phương thức static của class Category.

class Category
extends Map { /* ... */ static clone( in_source = new Category(), out_target = new Category() ) { var entries = [ ...in_source ] for (var [key, value] of entries) { out_target.set(key, value) } // for return Category }
} // class

Sau đó viết code kiểm tra kết quả vận hành của phương thức Category.copy().

const Category = require("./database/type/Category") var source = new Category()
source.set("@id", "Infinity") .set("name", "webdev") .set("keywords", ["tutorial", "web"]) .set("markdown", "Looonggg... content...") var target = new Category()
Category.clone(source, target) console.log(target)

CMD | Terminal

npm test Category(4) [Map] { '@id' => 'Infinity', 'name' => 'webdev', 'keywords' => [ 'tutorial', 'web' ], 'markdown' => 'Looonggg... content...'
}

Tiếp theo là tiến hành ghi dữ liệu của bản ghi mới vào thư mục data.

/* requires ... */ module.exports = async ( in_submitted = new Category(), out_inserted = new Category()
) => { try { /* generate new record's id ... (done) */ /* populate output ... (done) */ /* write to data folder */ await writeRecordToDataFolder(out_inserted) } catch (error) { throw error }
} // module.exports

Nội dung chính của thao tác này gồm các bước là - tạo ra một thư mục cho bản ghi mới, và ghi dữ liệu vào các tệp header.jsoncontent.md.

const path = require("path")
const fsPromises = require("fs/promises")
const Category = require("../../../type/Category/class")
const writeRecordHeaderToFile = require("./write-record-header-to-file--async-throw")
const writeRecordContentToFile = require("./write-record-content-to-file--async-throw") module.exports = async ( in_record = new Category()
) => { try { /* prepare path to record's data folder */ var categoryFolderPath = path.join(__dirname, "../../../data/category") var recordFolderName = "id-" + in_record.get("@id") var recordFolderPath = path.join(categoryFolderPath, recordFolderName) /* create folder for new record */ await fsPromises.mkdir(recordFolderPath) /* write new record's data to files */ await writeRecordHeaderToFile(in_record, recordFolderPath) await writeRecordContentToFile(in_record, recordFolderPath) } catch (error) { throw error }
} // module.exports

Thao tác ghi dữ liệu vào các tệp header.jsoncontent.md sẽ cần thêm một chút sự chuẩn bị và có thể khiến code của sub-procedure này bị rối; Do đó chúng ta sẽ ủy thác tới các sub-procedure tương ứng với mỗi kiểu tệp cần ghi dữ liệu.

const path = require("path")
const fsPromises = require("fs/promises")
const Category = require("../../../type/Category") module.exports = async ( in_record = new Category(), in_recordFolderPath = "./"
) => { try { var headerFilePath = path.join(in_recordFolderPath, "header.json") var headerJSON = {} Category.populateHeaderJSON(in_record, headerJSON) var headerText = JSON.stringify(headerJSON) await fsPromises.writeFile(headerFilePath, headerText) } catch (error) { throw error }
} // module.exports
const path = require("path")
const fsPromises = require("fs/promises")
const Category = require("../../../type/Category") module.exports = async ( in_record = new Category(), in_recordFolderPath = "./"
) => { try { var contentFilePath = path.join(in_recordFolderPath, "content.md") var markdownText = in_record.get("markdown") await fsPromises.writeFile(contentFilePath, markdownText) } catch (error) { throw error }
} // module.exports

Thao tác ghi nội dung cho tệp content.md thực sự không có gì đặc biệt ngoài việc chuẩn bị đường dẫn tới tệp cần ghi. Tuy nhiên thao tác ghi nội dung cho header.json thì chúng ta cần chuyển phần header trong object bản ghi thành một chuỗi JSON. Và ở bước này thì chúng ta sẽ lại ủy thác cho một phương thức static của class Category.

class Category
extens Map { /* ... */ static populateHeaderJSON( in_source = new Category(), out_headerJSON = {} ) { var allEntries = [ ...in_source ] var headerEntries = allEntries.slice(0, -1) for (var entry of headerEntries) { var [key, value] = entry out_headerJSON[key] = value } // for }
} // class module.exports = Category

Bây giờ thì chúng ta đã có thể viết code để chạy thử các sub-procedure và thủ tục chính insert. Chúng ta sẽ xuất phát từ phương thức Category.populateHeaderJSON() -

const Category = require("./database/type/Category") var record = new Category()
record.set("@id", "Infinity") .set("name", "webdev") .set("keywords", ["tutorial", "web"]) .set("markdown", "Looonggg... content...") var headerJSON = {}
Category.populateHeaderJSON(record, headerJSON) console.log(headerJSON)

CMD | Terminal

npm test { '@id': 'Infinity', name: 'webdev', keywords: [ 'tutorial', 'web' ] }

Trong kết quả in ra chúng ta đã thấy trường dữ liệu markdown không được sao chép sang object headerJSON. Như vậy là kết quả ghi vào tệp header.json cũng đã được đảm bảo. Bây giờ chúng ta sẽ thử insert luôn một bản ghi category mới.

const Category = require("./database/type/Category")
const databaseManager = require("./database/manager") void async function() { var newRecord = new Category() newRecord.set("@id", "Infinity") .set("name", "webdev") .set("keywords", ["tutorial", "web"]) .set("markdown", "Looonggg... content...") var inserted = new Category() await databaseManager.execute("insert-category", newRecord, inserted) console.log(inserted)
} () // void

CMD | Terminal

npm test Category(4) [Map] { '@id' => '03', 'name' => 'webdev', 'keywords' => [ 'tutorial', 'web' ], 'markdown' => 'Looonggg... content...'
}

Phù... như vậy là chúng ta đã thực hiện được thủ tục insert một bản ghi category mới vào database. Và trong bài viết tiếp theo, chúng ta sẽ tiếp tục viết code cho thủ tục select.

(Chưa đăng tải) [Database] Bài 6 - VIết Code Quản Lý Một Database Đơn Giản (Tiếp Theo)

Bình luận

Bài viết tương tự

- vừa được xem lúc

Mô hình quan hệ - thực thể (Entity – Relationship Model)

Mô hình quan hệ thực thể (Entity Relationship model - E-R) được CHEN giới thiệu vào năm 1976 là một mô hình được sử dụng rộng rãi trong các bản thiết kế cơ sở dữ liệu ở mức khái niệm, được xây dựng dựa trên việc nhận thức thế giới thực thông qua tập các đối tượng được gọi là các thực thể và các mối

0 0 117

- vừa được xem lúc

[Embulk #1] Công cụ giúp giảm nỗi đau chuyển đổi dữ liệu

Embulk là gì. Embulk là một công cụ open source có chức năng cơ bản là load các record từ database này và import sang database khác.

0 0 39

- vừa được xem lúc

Window Functions trong MySQL, Nâng cao và cực kì hữu dụng (Phần II).

Chào mọi người, lại là mình đây, ở phần trước mình đã giới thiệu với mọi người về Window Functions Phần I. Nếu chưa rõ nó là gì thì mọi người nên đọc lại trước nha, để nắm được định nghĩa và các key words, tránh mắt chữ O mồm chứ A vì phần này mình chủ yếu sẽ thực hành với các Window Functions.

0 0 89

- vừa được xem lúc

Window Functions trong MySQL, Nâng cao và cực kì hữu dụng (Phần I).

Chào mọi người, mình mới tìm hiểu đc topic Window Functions cá nhân mình cảm thấy khá là hay và mình đánh giá nó là phần nâng cao. Vì ít người biết nên Window Functions thấy rất ít khi sử dụng, thay vì đó là những câu subquery dài dằng dặc như tin nhắn nhắn cho crush, và người khác đọc hiểu được câu

0 0 895

- vừa được xem lúc

Disable và Enable trigger trong Oracle

Origin post: https://www.tranthanhdeveloper.com/2020/12/disable-va-enable-trigger-trong-oracle.html.

0 0 29

- vừa được xem lúc

Lưu trữ dữ liệu với Data Store

. Data Store là một trong những componet của bộ thư viện Android JetPack, nó là một sự lựa chọn hoàn hảo để thay thế cho SharedPreferences để lưu trữ dữ liệu đơn giản dưới dạng key-value. Chúng ta cùng làm một so sánh nhỏ để thấy sự tối ưu của Data Store với SharedPreferences nhé.

0 0 61