Mở đầu
Khi viết một ứng dụng moblie bạn thường thấy rằng mình cần lưu dữ liệu giữa các lần khởi chạy ứng dụng. Nếu dữ liệu đủ đơn giản như một số setting của app, thông tin đăng nhập,.. bạn có thể lưu chúng dưới dạng key-value ở sharedPreferences. Nhưng khi dữ liệu cần lưu trữ phức tạp hơn và phụ thuộc lẫn nhau , thì chúng ta sẽ cần sử dụng một hệ thống lưu trữ dữ liệu chính thức hơn – database. Database nói chung cung cấp việc insert, update, query nhanh hơn các phương thức lưu trữ local khác (file, sharedpreferences,…).
Cũng như lập trình native Android hay IOS, Flutter cũng dùng SQlite – một lựa chọn phổ biến nhất để quả lý database. Trong bài viết này mình sẽ trình bày cách quản lý database với plugin spflite. Sqflite là plugin phổ biến và được giới thiệu trên document của flutter nên bạn có thể yên tâm sử dụng nó
Một vài điều về SQFLite
- SQFLite hỗ trợ cho iOS, Android và MacOS, không hỗ trợ nền tảng web.
- Support transctions và batches.
- Tự động quản lý version
- Có helper cho các truy vấn insert, query, update, delete
- Hoạt động DB được thực hiện trong background thread trên iOS và Android
Các kiểu dữ liệu được hỗ trợ bởi sqlite:
- Integer: Dart type – int
- Real: Dart type – num
- Text: Dart type – String
- Blob:Dart type – Uint8List
DateTime không được hỗ trợ -> có thể lưu bằng millisSinceEpoch hoặc String.
Bool không được hỗ trợ -> dùng kiểu khác thay thế, ví dụ 0 hoặc 1
Ví dụ
Chúng ta sẽ đi vào một ví dụ cụ thể để hiểu một cách dễ dàng hơn. Trong ví dụ này ta sẽ lưu thông tin của một model movie vào database khi ta nhấn yêu thích.
Đầu tiên ta cần
Thêm dependency cần thiết vào file pubspec.yaml
1 2 3 4 5 6 7 | dependencies<span class="token punctuation">:</span> flutter<span class="token punctuation">:</span> sdk<span class="token punctuation">:</span> flutter sqflite<span class="token punctuation">:</span> <span class="token operator">^</span><span class="token number">1.3</span><span class="token number">.1</span> path<span class="token punctuation">:</span> <span class="token number">1.6</span><span class="token number">.4</span> equatable<span class="token punctuation">:</span> <span class="token operator">^</span><span class="token number">1.1</span><span class="token number">.1</span> |
Tọa class Movie, xác định những thông tin cần lưu trữ. Ở đây ta sẽ lưu trữ id, porterPath, title, overview, releaseDate, voteAverage. Trong đó id là trường unique, ta sẽ set làm primary key.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <span class="token keyword">class</span> <span class="token class-name">Movie</span> <span class="token keyword">extends</span> <span class="token class-name">Equatable</span> <span class="token punctuation">{</span> int id<span class="token punctuation">;</span> String backdropPath<span class="token punctuation">;</span> List<span class="token operator"><</span>Genre<span class="token operator">></span> genres<span class="token punctuation">;</span> String title<span class="token punctuation">;</span> String overview<span class="token punctuation">;</span> String porterPath<span class="token punctuation">;</span> List<span class="token operator"><</span>Company<span class="token operator">></span> productionCompanies<span class="token punctuation">;</span> String releaseDate<span class="token punctuation">;</span> int runtime<span class="token punctuation">;</span> int revenue<span class="token punctuation">;</span> int budget<span class="token punctuation">;</span> List<span class="token operator"><</span>Video<span class="token operator">></span> videos<span class="token punctuation">;</span> List<span class="token operator"><</span>Actor<span class="token operator">></span> cast<span class="token punctuation">;</span> double voteAverage<span class="token punctuation">;</span> int voteCount<span class="token punctuation">;</span> <span class="token metadata symbol">@override</span> List<span class="token operator"><</span>Object<span class="token operator">></span> <span class="token keyword">get</span> props <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span>id<span class="token punctuation">,</span> title<span class="token punctuation">]</span><span class="token punctuation">;</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> <span class="token keyword">dynamic</span><span class="token operator">></span> <span class="token function">toMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> <span class="token keyword">dynamic</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span> <span class="token operator">=</span> id <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">[</span><span class="token string">"poster_path"</span><span class="token punctuation">]</span> <span class="token operator">=</span> porterPath <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">[</span><span class="token string">"title"</span><span class="token punctuation">]</span> <span class="token operator">=</span> title <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">[</span><span class="token string">"overview"</span><span class="token punctuation">]</span> <span class="token operator">=</span> overview <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">[</span><span class="token string">"release_date"</span><span class="token punctuation">]</span> <span class="token operator">=</span> releaseDate <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">[</span><span class="token string">"vote_average"</span><span class="token punctuation">]</span> <span class="token operator">=</span> voteAverage<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">static</span> Movie <span class="token function">formJson</span><span class="token punctuation">(</span>Map<span class="token operator"><</span>String<span class="token punctuation">,</span> <span class="token keyword">dynamic</span><span class="token operator">></span> json<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">Movie</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token punctuation">.</span>id <span class="token operator">=</span> json<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span> <span class="token punctuation">.</span><span class="token punctuation">.</span>title <span class="token operator">=</span> json<span class="token punctuation">[</span><span class="token string">"title"</span><span class="token punctuation">]</span> <span class="token punctuation">.</span><span class="token punctuation">.</span>overview <span class="token operator">=</span> json<span class="token punctuation">[</span><span class="token string">"overview"</span><span class="token punctuation">]</span> <span class="token punctuation">.</span><span class="token punctuation">.</span>porterPath <span class="token operator">=</span> json<span class="token punctuation">[</span><span class="token string">"poster_path"</span><span class="token punctuation">]</span> <span class="token punctuation">.</span><span class="token punctuation">.</span>releaseDate <span class="token operator">=</span> json<span class="token punctuation">[</span><span class="token string">"release_date"</span><span class="token punctuation">]</span> <span class="token punctuation">.</span><span class="token punctuation">.</span>voteAverage <span class="token operator">=</span> json<span class="token punctuation">[</span><span class="token string">"vote_average"</span><span class="token punctuation">]</span> <span class="token operator">*</span> <span class="token number">1.0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Mở database
Trước khi đọc và ghi dữ liệu vào cơ sở dữ liệu, ta cần mở một kết nối:
- Xác định đường dẫn đến database bằng cách sử dụng getDatabasePath () từ package sqflite, kết hợp với hàm join từ package path.
- Mở cơ sở dữ liệu với hàm openDatabase () của sqflite.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">const</span> String DB_NAME <span class="token operator">=</span> <span class="token string">"movies_database.db"</span><span class="token punctuation">;</span> Database _database<span class="token punctuation">;</span> Future<span class="token operator"><</span>Database<span class="token operator">></span> <span class="token keyword">get</span> database <span class="token keyword">async</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>_database <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token keyword">return</span> _database<span class="token punctuation">;</span> _database <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">_initDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> _database<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">_initDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">async</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">await</span> <span class="token function">openDatabase</span><span class="token punctuation">(</span> <span class="token function">join</span><span class="token punctuation">(</span><span class="token keyword">await</span> <span class="token function">getDatabasesPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> DB_NAME<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Tạo Table “favorite”
Tiếp theo, tạo một table lưu trữ thông tin về favorite Movie.
- id kiểu int -> INTEGER
- porterPath, title, overview, releaseDate kiểu String -> TEXT
- voteAverage kiểu double -> REAL
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">const</span> String TABLE_FAVORITE <span class="token operator">=</span> <span class="token string">"favorite"</span><span class="token punctuation">;</span> db<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token string">"""CREATE TABLE $TABLE_FAVORITE ( id INTEGER PRIMARY KEY, poster_path TEXT, title TEXT, overview TEXT, release_date TEXT, vote_average REAL )"""</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Lưu ý: tên cột nên giống với key trong hàng toMap(), formJson() điều này giúp cho việc read, insert dữ liệu dễ dàng hơn.
Insert một Movie vào DB
1 2 | <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>TABLE_FAVORITE<span class="token punctuation">,</span> movie<span class="token punctuation">.</span><span class="token function">toMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Movie được convert thành Map() và được lưu vào bảng TABLE_FAVORITE, với key sẽ là tên cột value sẽ là giá trị được điền vào cột đó.
Bạn cũng có thể chỉ định conflictAlgorithm để sử dụng trong trường hợp cùng một movie được chèn hai lần.
Lấy List Movie đã được lưu
1 2 3 | List<span class="token operator"><</span>Map<span class="token operator"><</span>String<span class="token punctuation">,</span> <span class="token keyword">dynamic</span><span class="token operator">>></span> maps <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">query</span><span class="token punctuation">(</span>TABLE_FAVORITE<span class="token punctuation">)</span><span class="token punctuation">;</span> List<span class="token operator"><</span>Movie<span class="token operator">></span> movies <span class="token operator">=</span> maps<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> Movie<span class="token punctuation">.</span><span class="token function">formJson</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Truy vấn db.query(TABLE_FAVORITE)
sẽ trả về list các record trong DB, mỗi record sẽ là một Map với key là tên cột, value là dữ liệu của cột đó. Việc ta cần làm sau đó chỉ là convert sang List<Movie> và dùng thôi.
Updata Movie đã có trong bảng
1 2 3 4 | <span class="token keyword">final</span> db <span class="token operator">=</span> <span class="token keyword">await</span> database<span class="token punctuation">;</span> <span class="token keyword">final</span> result <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>TABLE_FAVORITE<span class="token punctuation">,</span> movie<span class="token punctuation">.</span><span class="token function">toMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> where<span class="token punctuation">:</span> <span class="token string">"id = ?"</span><span class="token punctuation">,</span> whereArgs<span class="token punctuation">:</span> <span class="token punctuation">[</span>movie<span class="token punctuation">.</span>id<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Update record có id == movie.id bằng dữ liệu mới movie.toMap().
Xóa movie
1 2 3 | <span class="token keyword">final</span> db <span class="token operator">=</span> <span class="token keyword">await</span> database<span class="token punctuation">;</span> <span class="token keyword">final</span> result <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>TABLE_FAVORITE<span class="token punctuation">,</span> where<span class="token punctuation">:</span> <span class="token string">"id = ?"</span><span class="token punctuation">,</span> whereArgs<span class="token punctuation">:</span> <span class="token punctuation">[</span>movie<span class="token punctuation">.</span>id<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Delete record có id == movie.id
Đóng DB
Bình thường DB sẽ được đóng khi bạn tắt app. Tuy nhiên nếu bạn muốn giải phóng resource, có thể đóng DB bằng cách
1 2 | <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Kết luận
Trên đây mình vừa trình bày một cách đơn giản về việc sử dụng SQLite trong một ứng dụng Flutter. Code của phần ví dụ trên bạn có thể xem chi tiết ở đây.
Nếu phần trên quá đơn giản, bạn muốn tìm hiểu sâu hơn thì có thể vọc pagkage sqflite ở đây.
Cảm ơn các bạn đã theo dõi bài viết !!