12/08/2018, 09:58

Giới thiệu về DataMapper

Giới thiệu chung Data Mapper là một mô hình ánh xạ hướng đối tượng (ORM). Khác với mô hình Active Record, Data Mapper giữ cho dữ liệu được lưu trong bộ nhớ máy và trong cơ sở dữ liệu độc lập với nhau. Mô hình Data Mapper phát huy hiệu quả trong các ứng dụng có số luật lệ và quy trình nghiệp vụ ...

Giới thiệu chung

Data Mapper là một mô hình ánh xạ hướng đối tượng (ORM). Khác với mô hình Active Record, Data Mapper giữ cho dữ liệu được lưu trong bộ nhớ máy và trong cơ sở dữ liệu độc lập với nhau. Mô hình Data Mapper phát huy hiệu quả trong các ứng dụng có số luật lệ và quy trình nghiệp vụ phức tạp, hoặc khi phát triển một ứng dụng mới từ cơ sở dữ liệu có sẵn.

Thư viện DataMapper là một trong những thư viện phổ biến để cài đặt và sử dụng mô hình Data Mapper cho Ruby. Bài mày sẽ giới thiệu những bước đơn giản nhất để làm quen với thư viện này.

Cài đặt

Bước đầu tiên là cài đặt các gem cần thiết bằng câu lệnh: gem install data_mapper Câu lệnh này cài đặt phần lớn các gem cận thiết:

  • dm-core
  • dm-aggregates
  • dm-constraints
  • dm-migrations
  • dm-transactions
  • dm-serializer
  • dm-timestamps
  • dm-validations
  • dm-types

Sau đó, cài đặt một gem adapter tùy thuộc vào cơ sở dữ liệu đang sử dụng: dm-sqlite-adapter, dm-mysql-adapter hoặc dm-postgres-adapter

Để sử dụng thư viện DataMapper vào trong project, ta gọi: require "data_mapper"

Tạo model

Dưới đay là một lớp model đơn giản sử dụng DataMapper

class User
    include DataMapper::Resource

    property :id, 		Serial
    property :username, String
    property :email, 	String
end

Module DataMapper::Resource được include vào trong lớp cung cấp các hàm cơ bản của thư viện. Như ví dụ ở trên, hàm property được sử dụng để tạo ba thuộc tính cho lớp: id, username và email. Trong đó, Serial là thuộc tính để tạo số tự nhiên tăng dần đóng vai trò khóa chính của lớp.

Migrate cơ sở dữ liệu

Sau khi tạo xong các model, công việc tiếp theo là migrate cơ sở dữ liệu. DataMapper cung cấp hai hàm thực hiện việc này:

DataMapper.auto_migrate!
DataMapper.auto_upgrade!

Điểm khác biệt là hàm auto_migrate! sẽ xóa sạch toạn bộ dữ liệu trong cơ sở dữ liệu và tạo một schema mới; trong khi đó hàm auto_upgrade! cố gắng tìm ra những yêu cầu thay đổi với cơ sở dữ liệu và thực hiện thay đổi vào schema. Cả hai hàm này đều có thể gọi sau khi đọc một lớp bất kì. Dĩ nhiên, không ai muốn chạy auto_migrate! sau mỗi lần đọc một lớp, mà sẽ chạy auto_upgrade! sau mỗi lần reload trong quá trình development. Vấn đề này được giải quyết như sau:

configure :development do
    DataMapper.auto_upgrade!
end

Ta có thể thấy rằng, cho đến lúc này, ta vẫn chưa phải sử dụng bất cứ câu truy vấn SQL nào; đó chính là đặc trưng của các mô hình ORM khi ta chỉ cần viết code bình thường mà vẫn có thể làm việc với các cơ sở dữ liệu quan hệ.

Thêm các thuộc tính

Sau các bước khởi tạo model và cơ sở dữ liệu, bây giờ ta sẽ cải tiến cho các model này, bắt đầu từ việc thêm các nhãn thời gian.

Nhãn thời gian

Trong các gem của DataMapper, gem dm-timestamps sẽ đảm nhận công việc này. Tất cả những gì cần làm là thêm vào các thuộc tính created_at và updated_at vào model, gem này sẽ tự động xử lý với các trường này khi cần.

property :created_at, DateTime
property :updated_at, DateTime

Dĩ nhiên, ta có thể không thêm hai thuộc tính này, nếu thực sự thấy không cần thiết.

Các tùy chọn

Có một số tùy chọn có thể thêm vào các thuộc tính. Ví dụ, ta có thể xác định xem thuộc tính đó có cần phải có hay không, có là duy nhất hay không, hoặc xác định giá trị mặc định cho trường đó. Dưới đây là một ví dụ:

class Post
    include DataMapper::Resource

    property :slug, 		String, key: true, unique_index: true, default: ->{|resource,prop| resource.title.downcase.gsub " ", "-"}
    property :title, 		String, required: true
    property :body, 		Text, 	required: true
    property :created_at, 	DateTime
    property :updated_at, 	DateTime
end

Ở ví dụ trên, các trường title và body là các trường buộc phải có. Ta xác định thuộc tính slug là khóa chính, bắt buộc trường này phải là duy nhất và được đánh index. Vì thế, giá trị mặc định của slug không phải là một giá trị cố định. Thay vào đó, giá trị mặc định của slug sẽ lấy từ resource truyền vào của DataMapper, hay từ bảng Post trong cơ sở dữ liệu, lấy thuộc tính title của nó, cho đi qua các hàm downcase (chuyển chữ hoa thành chữ thường) và gsub (chuyển các dấu trắng thành gạch dưới '_'). Nhờ đó, từ một xâu: "This is a Title"

Sẽ trở thành: "this-is-a-title"

Validations

Ta có thể thêm validation vào trong các tùy chọn của thuộc tính. Ví dụ:

property :email, 	String, format: :email_address
property :password, String, length: 10..255

Associations

Thiết lập các quan hệ, khóa ngoài của thư viện DataMapper cũng tương tự như thư viện ActiveRecord. Ví dụ ta muốn tạo model User có nhiều Post, và model Post thuộc về User.

Ở model User, thêm dòng này:

has n, :posts

Sau đó, ở model Ost, thêm dòng này:

belongs_to :user

Cơ sở dữ liệu sẽ tự thêm vào thuộc tính user_id cho bảng Post.

Các từ khóa Associations dùng trong DataMapper và các từ khóa tương ứng trong ActiveRecord:

**DataMapper** 							**ActiveRecord**
has n 									has_many
has 1 									has_one
belongs_to 								belongs_to
has n, :things, :through => Resource 	has_and_belongs_to_many
has n, :things, :through => :model 		has_many :association, :through => Model

Accessors

Nếu muốn thay đổi đầu vào của các thuộc tính, ta có thể tự định nghĩa các hàm accessor. Ví dụ, trong model User trường name luôn được lưu trữ dưới dạng chữ thường. Ta có thể viết một hàm accessor như với một lớp bình thường. Bằng phương pháp này, ta có thể lấy giá trị nhập vào của đối tượng và lưu trữ theo cách ta mong muốn.

def name= new_username
    super new_username.downcase
end

Ở đây, ta định nghĩa hàm name=, nhờ thế mà mỗi khi nhập một name mới, nó sẽ được lưu trữ bằng chữ thường.

Một số thư viện khác

Bên cạnh DataMapper, còn có những thư viện khác hỗ trợ cài đặt mô hình Data Mapper cho Ruby:

  • Perpetuity
  • Ruby Object Mapper (ROM)
0