@dqcai/sqlite 1.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +1491 -2328
  2. package/lib/adapters/base-adapter.d.ts.map +1 -1
  3. package/lib/adapters/index.d.ts +2 -0
  4. package/lib/adapters/index.d.ts.map +1 -0
  5. package/lib/core/base-service.d.ts +119 -0
  6. package/lib/core/base-service.d.ts.map +1 -0
  7. package/lib/core/database-factory.d.ts +98 -0
  8. package/lib/core/database-factory.d.ts.map +1 -0
  9. package/lib/core/database-manager.d.ts +208 -0
  10. package/lib/core/database-manager.d.ts.map +1 -0
  11. package/lib/core/index.d.ts +5 -0
  12. package/lib/core/index.d.ts.map +1 -0
  13. package/lib/core/universal-dao.d.ts +64 -0
  14. package/lib/core/universal-dao.d.ts.map +1 -0
  15. package/lib/index.d.ts +324 -14
  16. package/lib/index.d.ts.map +1 -1
  17. package/lib/index.js +1 -1
  18. package/lib/index.js.map +1 -1
  19. package/lib/index.mjs +45 -1
  20. package/lib/index.mjs.map +1 -1
  21. package/lib/index.umd.js +1 -1
  22. package/lib/index.umd.js.map +1 -1
  23. package/lib/query/query-builder.d.ts +120 -0
  24. package/lib/query/query-builder.d.ts.map +1 -0
  25. package/lib/types.d.ts +142 -4
  26. package/lib/types.d.ts.map +1 -1
  27. package/lib/utils/csv-import.d.ts +102 -0
  28. package/lib/utils/csv-import.d.ts.map +1 -0
  29. package/lib/utils/index.d.ts +3 -0
  30. package/lib/utils/index.d.ts.map +1 -0
  31. package/lib/utils/migration-manager.d.ts +184 -0
  32. package/lib/utils/migration-manager.d.ts.map +1 -0
  33. package/package.json +82 -63
  34. package/README-all-source.md +0 -1248
  35. package/README-ps-gemini.md +0 -1180
  36. package/lib/adapters/browser-adapter.d.ts +0 -17
  37. package/lib/adapters/browser-adapter.d.ts.map +0 -1
  38. package/lib/adapters/bun-adapter.d.ts +0 -7
  39. package/lib/adapters/bun-adapter.d.ts.map +0 -1
  40. package/lib/adapters/deno-adapter.d.ts +0 -7
  41. package/lib/adapters/deno-adapter.d.ts.map +0 -1
  42. package/lib/adapters/node-adapter.d.ts +0 -7
  43. package/lib/adapters/node-adapter.d.ts.map +0 -1
  44. package/lib/adapters/react-native-adapter.d.ts +0 -20
  45. package/lib/adapters/react-native-adapter.d.ts.map +0 -1
  46. package/lib/query-builder.d.ts +0 -19
  47. package/lib/query-builder.d.ts.map +0 -1
  48. package/lib/sqlite-manager.d.ts +0 -11
  49. package/lib/sqlite-manager.d.ts.map +0 -1
  50. package/scripts/obfuscate.mjs +0 -155
  51. package/scripts/version-manager.js +0 -317
package/README.md CHANGED
@@ -1,2637 +1,1799 @@
1
- # @dqcai/sqlite - Universal SQLite Library
1
+ # @dqcai/sqlite - A Universal SQLite Library (@dqcai/sqlite v2.0.1)
2
2
 
3
3
  ![Universal SQLite](https://img.shields.io/badge/SQLite-Universal-blue)
4
4
  ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)
5
5
  ![Cross Platform](https://img.shields.io/badge/Platform-Universal-green)
6
6
 
7
- Một thư viện SQLite đa nền tảng hỗ trợ **tất cả** các môi trường JavaScript/TypeScript phổ biến với API thống nhất.
7
+ UniversalSQLite là một thư viện SQLite toàn diện, hỗ trợ đa nền tảng, được thiết kế để hoạt động mượt mà trên các môi trường như Browser, Node.js, Deno, Bun và React Native. Thư viện cung cấp giao diện thống nhất để quản lý cơ sở dữ liệu SQLite, bao gồm tạo schema, CRUD, query nâng cao, migration, import/export dữ liệu, và quản lý transaction. Nó sử dụng mô hình DAO (Data Access Object) để tách biệt logic truy cập dữ liệu, hỗ trợ role-based access control, và tích hợp dễ dàng với các framework.
8
8
 
9
- ## 🌟 Tính năng nổi bật
9
+ ## Features
10
10
 
11
- - **Universal**: Hoạt động trên Node.js, Browser, Deno, Bun, React Native
12
- - **TypeScript**: Hỗ trợ đầy đủ TypeScript với type safety
13
- - **Auto-detection**: Tự động phát hiện môi trường và chọn adapter phù hợp
14
- - **Query Builder**: Công cụ xây dựng truy vấn mạnh mẽ
15
- - **React Native Windows**: Hỗ trợ đặc biệt cho React Native Windows
16
- - **Zero Configuration**: Không cần cấu hình phức tạp
11
+ - **Cross-Platform Support**: Hoạt động trên Browser, Node.js, Deno, Bun, React Native (iOS/Android/Windows).
12
+ - **Schema-Based Management**: Tạo quản database từ JSON schema.
13
+ - **DAO Pattern**: UniversalDAO để thực hiện CRUD, query, transaction.
14
+ - **Query Builder**: Xây dựng query phức tạp với join, where, group by, having, union, CTE.
15
+ - **Migration System**: Quản migration với up/down scripts.
16
+ - **Data Import/Export**: Hỗ trợ import từ CSV/JSON với mapping, validation, và export to CSV.
17
+ - **Role-Based Access**: Quản lý kết nối dựa trên role người dùng.
18
+ - **Transaction Management**: Hỗ trợ transaction đơn và cross-schema.
19
+ - **Adapters**: Tự động detect môi trường, hỗ trợ register adapter tùy chỉnh.
20
+ - **Type-Safe**: Đầy đủ types TypeScript cho schema, query, và operations.
21
+ - **Utilities**: CSVImporter, MigrationManager, BaseService cho service layer.
22
+ - **DatabaseManager**: Quản lý kết nối, schema và vai trò người dùng
23
+ - **BaseService**: Lớp cơ sở cho CRUD operations
17
24
 
18
- ## 🚀 Cài đặt
25
+ ## Installation
26
+
27
+ Cài đặt qua npm hoặc yarn:
19
28
 
20
29
  ```bash
21
- npm install @dqcai/sqlite
30
+ npm install @dqcai/sqlite@2.0.0
31
+ # hoặc
32
+ yarn add @dqcai/sqlite@2.0.0
22
33
  ```
23
34
 
24
- ### Cài đặt dependencies theo môi trường
35
+ Đối với React Native, đảm bảo cài đặt các dependencies cần thiết cho adapter (nếu sử dụng adapter cụ thể như react-native-sqlite-storage).
36
+
37
+ ## Cài đặt
25
38
 
26
- #### Node.js
27
39
  ```bash
28
- npm install sqlite3
40
+ npm install @dqcai/sqlite
29
41
  ```
30
42
 
31
- #### Browser
32
- Không cần cài đặt thêm - sử dụng sql.js từ CDN
43
+ ## 1. Cấu hình Schema Database
44
+
45
+ Trước tiên, định nghĩa schema cho cơ sở dữ liệu:
46
+
47
+ ```typescript
48
+ import { DatabaseSchema } from '@dqcai/sqlite';
49
+
50
+ // Schema cho database users
51
+ const userSchema: DatabaseSchema = {
52
+ version: "1.0.0",
53
+ database_name: "users",
54
+ description: "User management database",
55
+ schemas: {
56
+ users: {
57
+ description: "User table",
58
+ cols: [
59
+ {
60
+ name: "id",
61
+ type: "integer",
62
+ primary_key: true,
63
+ auto_increment: true,
64
+ nullable: false
65
+ },
66
+ {
67
+ name: "username",
68
+ type: "varchar",
69
+ nullable: false,
70
+ unique: true,
71
+ length: 50
72
+ },
73
+ {
74
+ name: "email",
75
+ type: "varchar",
76
+ nullable: false,
77
+ unique: true,
78
+ length: 100
79
+ },
80
+ {
81
+ name: "password",
82
+ type: "varchar",
83
+ nullable: false,
84
+ length: 255
85
+ },
86
+ {
87
+ name: "created_at",
88
+ type: "datetime",
89
+ nullable: false,
90
+ default: "CURRENT_TIMESTAMP"
91
+ },
92
+ {
93
+ name: "updated_at",
94
+ type: "datetime",
95
+ nullable: true
96
+ }
97
+ ],
98
+ indexes: [
99
+ {
100
+ name: "idx_username",
101
+ columns: ["username"],
102
+ unique: true
103
+ },
104
+ {
105
+ name: "idx_email",
106
+ columns: ["email"],
107
+ unique: true
108
+ }
109
+ ]
110
+ },
111
+ profiles: {
112
+ description: "User profiles table",
113
+ cols: [
114
+ {
115
+ name: "id",
116
+ type: "integer",
117
+ primary_key: true,
118
+ auto_increment: true
119
+ },
120
+ {
121
+ name: "user_id",
122
+ type: "integer",
123
+ nullable: false
124
+ },
125
+ {
126
+ name: "first_name",
127
+ type: "varchar",
128
+ length: 50
129
+ },
130
+ {
131
+ name: "last_name",
132
+ type: "varchar",
133
+ length: 50
134
+ },
135
+ {
136
+ name: "phone",
137
+ type: "varchar",
138
+ length: 20
139
+ },
140
+ {
141
+ name: "address",
142
+ type: "text"
143
+ }
144
+ ],
145
+ foreign_keys: [
146
+ {
147
+ name: "fk_profile_user",
148
+ column: "user_id",
149
+ references: {
150
+ table: "users",
151
+ column: "id"
152
+ },
153
+ on_delete: "CASCADE",
154
+ on_update: "CASCADE"
155
+ }
156
+ ]
157
+ }
158
+ }
159
+ };
160
+
161
+ // Schema core cho hệ thống
162
+ const coreSchema: DatabaseSchema = {
163
+ version: "1.0.0",
164
+ database_name: "core",
165
+ description: "Core system database",
166
+ schemas: {
167
+ settings: {
168
+ description: "System settings",
169
+ cols: [
170
+ {
171
+ name: "key",
172
+ type: "varchar",
173
+ primary_key: true,
174
+ length: 100
175
+ },
176
+ {
177
+ name: "value",
178
+ type: "text"
179
+ },
180
+ {
181
+ name: "description",
182
+ type: "text"
183
+ }
184
+ ]
185
+ }
186
+ }
187
+ };
188
+ ```
33
189
 
34
- #### Deno
35
- Không cần cài đặt - sử dụng Deno SQLite module
190
+ ## 2. Setup cho React Native
36
191
 
37
- #### Bun
38
- Sử dụng built-in SQLite của Bun
192
+ ### Cài đặt dependencies
39
193
 
40
- #### React Native
41
194
  ```bash
42
- # Standard React Native
43
- npm install react-native-sqlite-storage
44
- # hoặc
45
- npm install expo-sqlite
46
-
47
- # React Native Windows
48
195
  npm install react-native-sqlite-2
49
- # hoặc
50
- npm install react-native-windows-sqlite
196
+ # Hoặc
197
+ npm install react-native-sqlite-storage
51
198
  ```
52
199
 
53
- ## 📖 Sử dụng bản
54
-
55
- ### Import thư viện
200
+ ### Tạo Adapter cho React Native
56
201
 
57
202
  ```typescript
58
- import UniversalSQLite, { SQLiteManager, QueryBuilder } from '@dqcai/sqlite';
203
+ // adapters/ReactNativeAdapter.ts
204
+ import { BaseAdapter } from '@dqcai/sqlite';
205
+ import SQLite from 'react-native-sqlite-2';
206
+
207
+ export class ReactNativeAdapter extends BaseAdapter {
208
+ isSupported(): boolean {
209
+ return typeof SQLite !== 'undefined';
210
+ }
211
+
212
+ async connect(path: string): Promise<any> {
213
+ return new Promise((resolve, reject) => {
214
+ const db = SQLite.openDatabase(
215
+ path,
216
+ '1.0',
217
+ 'Database',
218
+ 200000,
219
+ () => {
220
+ resolve(new ReactNativeConnection(db));
221
+ },
222
+ (error) => {
223
+ reject(error);
224
+ }
225
+ );
226
+ });
227
+ }
228
+ }
229
+
230
+ class ReactNativeConnection {
231
+ constructor(private db: any) {}
232
+
233
+ async execute(sql: string, params: any[] = []): Promise<any> {
234
+ return new Promise((resolve, reject) => {
235
+ this.db.transaction((tx: any) => {
236
+ tx.executeSql(
237
+ sql,
238
+ params,
239
+ (tx: any, results: any) => {
240
+ const rows: any[] = [];
241
+ for (let i = 0; i < results.rows.length; i++) {
242
+ rows.push(results.rows.item(i));
243
+ }
244
+ resolve({
245
+ rows,
246
+ rowsAffected: results.rowsAffected,
247
+ lastInsertRowId: results.insertId
248
+ });
249
+ },
250
+ (tx: any, error: any) => {
251
+ reject(error);
252
+ }
253
+ );
254
+ });
255
+ });
256
+ }
257
+
258
+ async close(): Promise<void> {
259
+ // React Native SQLite không cần close thủ công
260
+ return Promise.resolve();
261
+ }
262
+ }
59
263
  ```
60
264
 
61
- ### Kết nối sử dụng đơn giản
265
+ ### Khởi tạo DatabaseManager (React Native)
62
266
 
63
267
  ```typescript
64
- const db = new UniversalSQLite();
268
+ // services/DatabaseService.ts
269
+ import { DatabaseManager, DatabaseFactory } from '@dqcai/sqlite';
270
+ import { ReactNativeAdapter } from '../adapters/ReactNativeAdapter';
65
271
 
66
- async function basicExample() {
67
- try {
68
- // Kết nối đến database
69
- await db.connect('myapp.db');
70
-
71
- // Tạo bảng
72
- await db.createTable('users', {
73
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
74
- name: 'TEXT NOT NULL',
75
- email: 'TEXT UNIQUE',
76
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
77
- });
78
-
79
- // Thêm dữ liệu
80
- await db.insert('users', {
81
- name: 'John Doe',
82
- email: 'john@example.com'
272
+ export class DatabaseService {
273
+ private static isInitialized = false;
274
+
275
+ static async initialize() {
276
+ if (this.isInitialized) return;
277
+
278
+ // Đăng ký adapter
279
+ DatabaseFactory.registerAdapter(new ReactNativeAdapter());
280
+
281
+ // Đăng ký schemas
282
+ DatabaseManager.registerSchemas({
283
+ core: coreSchema,
284
+ users: userSchema
83
285
  });
84
-
85
- // Truy vấn dữ liệu
86
- const users = await db.select('users');
87
- console.log('All users:', users.rows);
88
-
89
- // Tìm kiếm có điều kiện
90
- const john = await db.select('users', 'name = ?', ['John Doe']);
91
- console.log('John:', john.rows[0]);
92
-
93
- // Cập nhật
94
- await db.update('users',
95
- { email: 'john.doe@example.com' },
96
- 'name = ?',
97
- ['John Doe']
98
- );
99
-
100
- // Xóa
101
- await db.delete('users', 'id = ?', [1]);
102
-
103
- } catch (error) {
104
- console.error('Database error:', error);
105
- } finally {
106
- await db.close();
286
+
287
+ // Đăng roles
288
+ DatabaseManager.registerRoles([
289
+ {
290
+ roleName: 'admin',
291
+ requiredDatabases: ['core', 'users'],
292
+ priority: 1
293
+ },
294
+ {
295
+ roleName: 'user',
296
+ requiredDatabases: ['core'],
297
+ optionalDatabases: ['users'],
298
+ priority: 2
299
+ }
300
+ ]);
301
+
302
+ // Khởi tạo core database
303
+ await DatabaseManager.initializeCoreConnection();
304
+
305
+ this.isInitialized = true;
306
+ console.log('DatabaseService initialized for React Native');
307
+ }
308
+
309
+ static async setUserRole(roles: string[]) {
310
+ await DatabaseManager.setCurrentUserRoles(roles);
311
+ }
312
+
313
+ static getConnection(dbKey: string) {
314
+ return DatabaseManager.get(dbKey);
107
315
  }
108
- }
109
316
 
110
- basicExample();
317
+ static async closeAll() {
318
+ await DatabaseManager.closeAll();
319
+ this.isInitialized = false;
320
+ }
321
+ }
111
322
  ```
112
323
 
113
- ## 🔧 API Chi tiết
324
+ ## 3. Setup cho Node.js
114
325
 
115
- ### UniversalSQLite Class
326
+ ### Cài đặt dependencies
116
327
 
117
- #### Khởi tạo
118
- ```typescript
119
- const db = new UniversalSQLite();
328
+ ```bash
329
+ npm install sqlite3
330
+ # Hoặc
331
+ npm install better-sqlite3
120
332
  ```
121
333
 
122
- #### Phương thức chính
334
+ ### Tạo Adapter cho Node.js
123
335
 
124
- ##### `connect(path: string): Promise<void>`
125
336
  ```typescript
126
- // In-memory database
127
- await db.connect(':memory:');
337
+ // adapters/NodeAdapter.ts
338
+ import { BaseAdapter } from '@dqcai/sqlite';
339
+ import sqlite3 from 'sqlite3';
340
+ import path from 'path';
341
+ import fs from 'fs';
128
342
 
129
- // File database
130
- await db.connect('database.db');
343
+ export class NodeAdapter extends BaseAdapter {
344
+ isSupported(): boolean {
345
+ return typeof process !== 'undefined' && process.versions?.node;
346
+ }
131
347
 
132
- // Browser (file hoặc in-memory)
133
- await db.connect('myapp.db');
134
- ```
348
+ async connect(dbPath: string): Promise<any> {
349
+ const fullPath = path.resolve(dbPath);
350
+
351
+ // Tạo thư mục nếu chưa tồn tại
352
+ const dir = path.dirname(fullPath);
353
+ if (!fs.existsSync(dir)) {
354
+ fs.mkdirSync(dir, { recursive: true });
355
+ }
135
356
 
136
- ##### `query(sql: string, params?: any[]): Promise<SQLiteResult>`
137
- ```typescript
138
- // Truy vấn đơn giản
139
- const result = await db.query('SELECT * FROM users');
357
+ return new Promise((resolve, reject) => {
358
+ const db = new sqlite3.Database(fullPath, (err) => {
359
+ if (err) {
360
+ reject(err);
361
+ } else {
362
+ resolve(new NodeConnection(db));
363
+ }
364
+ });
365
+ });
366
+ }
367
+ }
140
368
 
141
- // Truy vấn với parameters
142
- const result = await db.query('SELECT * FROM users WHERE age > ?', [18]);
369
+ class NodeConnection {
370
+ constructor(private db: sqlite3.Database) {}
143
371
 
144
- console.log(result.rows); // Dữ liệu trả về
145
- console.log(result.rowsAffected); // Số dòng bị ảnh hưởng
146
- console.log(result.lastInsertRowId); // ID của dòng mới insert
147
- ```
372
+ async execute(sql: string, params: any[] = []): Promise<any> {
373
+ return new Promise((resolve, reject) => {
374
+ const isSelect = sql.trim().toUpperCase().startsWith('SELECT');
375
+
376
+ if (isSelect) {
377
+ this.db.all(sql, params, (err, rows) => {
378
+ if (err) {
379
+ reject(err);
380
+ } else {
381
+ resolve({
382
+ rows: rows || [],
383
+ rowsAffected: 0
384
+ });
385
+ }
386
+ });
387
+ } else {
388
+ this.db.run(sql, params, function(err) {
389
+ if (err) {
390
+ reject(err);
391
+ } else {
392
+ resolve({
393
+ rows: [],
394
+ rowsAffected: this.changes,
395
+ lastInsertRowId: this.lastID
396
+ });
397
+ }
398
+ });
399
+ }
400
+ });
401
+ }
148
402
 
149
- ##### `createTable(name: string, schema: Record<string, string>): Promise<void>`
150
- ```typescript
151
- await db.createTable('products', {
152
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
153
- name: 'TEXT NOT NULL',
154
- price: 'REAL',
155
- category_id: 'INTEGER',
156
- 'FOREIGN KEY (category_id)': 'REFERENCES categories(id)'
157
- });
403
+ async close(): Promise<void> {
404
+ return new Promise((resolve, reject) => {
405
+ this.db.close((err) => {
406
+ if (err) {
407
+ reject(err);
408
+ } else {
409
+ resolve();
410
+ }
411
+ });
412
+ });
413
+ }
414
+ }
158
415
  ```
159
416
 
160
- ##### `insert(table: string, data: Record<string, any>): Promise<SQLiteResult>`
417
+ ### Khởi tạo DatabaseManager (Node.js)
418
+
161
419
  ```typescript
162
- const result = await db.insert('users', {
163
- name: 'Alice',
164
- email: 'alice@example.com',
165
- age: 25
166
- });
420
+ // services/DatabaseService.ts
421
+ import { DatabaseManager, DatabaseFactory } from '@dqcai/sqlite';
422
+ import { NodeAdapter } from '../adapters/NodeAdapter';
423
+ import path from 'path';
167
424
 
168
- console.log('New user ID:', result.lastInsertRowId);
169
- ```
425
+ export class DatabaseService {
426
+ private static isInitialized = false;
427
+ private static dbDirectory = './databases';
170
428
 
171
- ##### `select(table: string, where?: string, params?: any[]): Promise<SQLiteResult>`
172
- ```typescript
173
- // Lấy tất cả
174
- const allUsers = await db.select('users');
429
+ static async initialize() {
430
+ if (this.isInitialized) return;
175
431
 
176
- // điều kiện
177
- const adults = await db.select('users', 'age >= ?', [18]);
432
+ // Đăng adapter
433
+ DatabaseFactory.registerAdapter(new NodeAdapter());
178
434
 
179
- // Điều kiện phức tạp
180
- const result = await db.select('users', 'age BETWEEN ? AND ? AND name LIKE ?', [18, 65, 'A%']);
181
- ```
435
+ // Đăng schemas
436
+ DatabaseManager.registerSchemas({
437
+ core: coreSchema,
438
+ users: userSchema
439
+ });
182
440
 
183
- ##### `update(table: string, data: Record<string, any>, where: string, whereParams?: any[]): Promise<SQLiteResult>`
184
- ```typescript
185
- const result = await db.update('users',
186
- { email: 'newemail@example.com', age: 26 },
187
- 'id = ?',
188
- [1]
189
- );
441
+ // Đăng roles
442
+ DatabaseManager.registerRoles([
443
+ {
444
+ roleName: 'admin',
445
+ requiredDatabases: ['core', 'users'],
446
+ priority: 1
447
+ },
448
+ {
449
+ roleName: 'user',
450
+ requiredDatabases: ['core'],
451
+ optionalDatabases: ['users'],
452
+ priority: 2
453
+ }
454
+ ]);
190
455
 
191
- console.log('Updated rows:', result.rowsAffected);
192
- ```
456
+ // Khởi tạo core database
457
+ await DatabaseManager.initializeCoreConnection();
193
458
 
194
- ##### `delete(table: string, where: string, params?: any[]): Promise<SQLiteResult>`
195
- ```typescript
196
- const result = await db.delete('users', 'age < ?', [18]);
197
- console.log('Deleted rows:', result.rowsAffected);
459
+ this.isInitialized = true;
460
+ console.log('DatabaseService initialized for Node.js');
461
+ }
462
+
463
+ static async setUserRole(roles: string[]) {
464
+ await DatabaseManager.setCurrentUserRoles(roles);
465
+ }
466
+
467
+ static getConnection(dbKey: string) {
468
+ return DatabaseManager.get(dbKey);
469
+ }
470
+
471
+ static async closeAll() {
472
+ await DatabaseManager.closeAll();
473
+ this.isInitialized = false;
474
+ }
475
+ }
198
476
  ```
199
477
 
200
- ### QueryBuilder - Xây dựng truy vấn nâng cao
478
+ ## 4. Tạo Services với BaseService
479
+
480
+ ### User Service
201
481
 
202
482
  ```typescript
203
- import { QueryBuilder } from '@dqcai/sqlite';
204
-
205
- // SELECT với QueryBuilder
206
- const selectSQL = QueryBuilder
207
- .table('users')
208
- .select(['id', 'name', 'email'])
209
- .where('age > 18')
210
- .where('status = "active"')
211
- .orderBy('name', 'ASC')
212
- .limit(10)
213
- .offset(0)
214
- .toSQL();
215
-
216
- console.log(selectSQL);
217
- // Output: SELECT id, name, email FROM users WHERE age > 18 AND status = "active" ORDER BY name ASC LIMIT 10 OFFSET 0
218
-
219
- const result = await db.query(selectSQL);
220
-
221
- // INSERT với QueryBuilder
222
- const insertSQL = QueryBuilder.insert('users', {
223
- name: 'Bob',
224
- email: 'bob@example.com',
225
- age: 30
226
- });
483
+ // services/UserService.ts
484
+ import { BaseService } from '@dqcai/sqlite';
227
485
 
228
- await db.query(insertSQL, ['Bob', 'bob@example.com', 30]);
486
+ interface User {
487
+ id?: number;
488
+ username: string;
489
+ email: string;
490
+ password: string;
491
+ created_at?: string;
492
+ updated_at?: string;
493
+ }
229
494
 
230
- // UPDATE với QueryBuilder
231
- const updateSQL = QueryBuilder.update('users',
232
- { email: 'bob.updated@example.com' },
233
- 'id = 1'
234
- );
495
+ interface Profile {
496
+ id?: number;
497
+ user_id: number;
498
+ first_name?: string;
499
+ last_name?: string;
500
+ phone?: string;
501
+ address?: string;
502
+ }
235
503
 
236
- await db.query(updateSQL, ['bob.updated@example.com']);
504
+ export class UserService extends BaseService<User> {
505
+ constructor() {
506
+ super('users', 'users'); // schema name, table name
507
+ this.setPrimaryKeyFields(['id']);
508
+ }
237
509
 
238
- // DELETE với QueryBuilder
239
- const deleteSQL = QueryBuilder.delete('users', 'age < 18');
240
- await db.query(deleteSQL);
241
- ```
510
+ // Tạo user mới
511
+ async createUser(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<User | null> {
512
+ try {
513
+ // Kiểm tra email đã tồn tại chưa
514
+ const existingUser = await this.findFirst({ email: userData.email });
515
+ if (existingUser) {
516
+ throw new Error('Email already exists');
517
+ }
242
518
 
243
- ### SQLiteManager - Quản kết nối thủ công
519
+ // Kiểm tra username đã tồn tại chưa
520
+ const existingUsername = await this.findFirst({ username: userData.username });
521
+ if (existingUsername) {
522
+ throw new Error('Username already exists');
523
+ }
244
524
 
245
- ```typescript
246
- import { SQLiteManager } from '@dqcai/sqlite';
525
+ const newUser = await this.create({
526
+ ...userData,
527
+ created_at: new Date().toISOString()
528
+ });
247
529
 
248
- const manager = new SQLiteManager();
530
+ return newUser;
531
+ } catch (error) {
532
+ console.error('Error creating user:', error);
533
+ throw error;
534
+ }
535
+ }
249
536
 
250
- // Kiểm tra môi trường
251
- console.log('Environment:', manager.getEnvironmentInfo());
537
+ // Cập nhật user
538
+ async updateUser(id: number, userData: Partial<User>): Promise<User | null> {
539
+ try {
540
+ const updatedUser = await this.update(id, {
541
+ ...userData,
542
+ updated_at: new Date().toISOString()
543
+ });
544
+ return updatedUser;
545
+ } catch (error) {
546
+ console.error('Error updating user:', error);
547
+ throw error;
548
+ }
549
+ }
252
550
 
253
- // Kết nối thủ công
254
- const connection = await manager.connect('database.db');
551
+ // Tìm user theo email
552
+ async findByEmail(email: string): Promise<User | null> {
553
+ return await this.findFirst({ email });
554
+ }
255
555
 
256
- // Thực thi truy vấn
257
- const result = await connection.execute('SELECT * FROM users');
556
+ // Tìm user theo username
557
+ async findByUsername(username: string): Promise<User | null> {
558
+ return await this.findFirst({ username });
559
+ }
258
560
 
259
- // Đóng kết nối
260
- await connection.close();
261
- ```
561
+ // Lấy tất cả users với phân trang
562
+ async getAllUsers(page: number = 1, limit: number = 10): Promise<User[]> {
563
+ const offset = (page - 1) * limit;
564
+ return await this.findAll({}, {
565
+ orderBy: [{ name: 'created_at', direction: 'DESC' }],
566
+ limit,
567
+ offset
568
+ });
569
+ }
262
570
 
263
- ## 🌍 dụ theo môi trường
571
+ // Xóa user (soft delete bằng cách cập nhật trường deleted_at)
572
+ async softDeleteUser(id: number): Promise<boolean> {
573
+ const result = await this.update(id, {
574
+ updated_at: new Date().toISOString(),
575
+ // deleted_at: new Date().toISOString() // nếu có field này trong schema
576
+ });
577
+ return result !== null;
578
+ }
264
579
 
265
- ### Node.js Application
580
+ // Đếm tổng số users
581
+ async getTotalUsers(): Promise<number> {
582
+ return await this.count();
583
+ }
266
584
 
267
- ```typescript
268
- // server.js
269
- import UniversalSQLite from '@dqcai/sqlite';
585
+ // Tìm kiếm users
586
+ async searchUsers(searchTerm: string): Promise<User[]> {
587
+ const dao = await this.init().then(() => this.dao!);
588
+ const sql = `
589
+ SELECT * FROM users
590
+ WHERE username LIKE ? OR email LIKE ?
591
+ ORDER BY created_at DESC
592
+ `;
593
+ const params = [`%${searchTerm}%`, `%${searchTerm}%`];
594
+ const result = await dao.execute(sql, params);
595
+ return result.rows as User[];
596
+ }
597
+ }
270
598
 
271
- const db = new UniversalSQLite();
599
+ export class ProfileService extends BaseService<Profile> {
600
+ constructor() {
601
+ super('users', 'profiles');
602
+ this.setPrimaryKeyFields(['id']);
603
+ }
272
604
 
273
- async function setupUserSystem() {
274
- await db.connect('./users.db');
275
-
276
- // Tạo bảng users
277
- await db.createTable('users', {
278
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
279
- username: 'TEXT UNIQUE NOT NULL',
280
- password_hash: 'TEXT NOT NULL',
281
- email: 'TEXT UNIQUE',
282
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
283
- last_login: 'DATETIME'
284
- });
285
-
286
- // Tạo index cho performance
287
- await db.query('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
288
-
289
- console.log('User system ready!');
290
- }
605
+ // Tạo profile cho user
606
+ async createProfile(profileData: Omit<Profile, 'id'>): Promise<Profile | null> {
607
+ return await this.create(profileData);
608
+ }
291
609
 
292
- async function createUser(username: string, email: string, passwordHash: string) {
293
- return await db.insert('users', {
294
- username,
295
- email,
296
- password_hash: passwordHash
297
- });
610
+ // Lấy profile theo user_id
611
+ async getProfileByUserId(userId: number): Promise<Profile | null> {
612
+ return await this.findFirst({ user_id: userId });
613
+ }
614
+
615
+ // Cập nhật profile
616
+ async updateProfile(id: number, profileData: Partial<Profile>): Promise<Profile | null> {
617
+ return await this.update(id, profileData);
618
+ }
619
+
620
+ // Lấy thông tin user và profile
621
+ async getUserWithProfile(userId: number): Promise<any> {
622
+ const dao = await this.init().then(() => this.dao!);
623
+ const sql = `
624
+ SELECT
625
+ u.id, u.username, u.email, u.created_at,
626
+ p.first_name, p.last_name, p.phone, p.address
627
+ FROM users u
628
+ LEFT JOIN profiles p ON u.id = p.user_id
629
+ WHERE u.id = ?
630
+ `;
631
+ const result = await dao.execute(sql, [userId]);
632
+ return result.rows[0] || null;
633
+ }
298
634
  }
635
+ ```
636
+
637
+ ### Core Service
638
+
639
+ ```typescript
640
+ // services/CoreService.ts
641
+ import { BaseService } from '@dqcai/sqlite';
299
642
 
300
- async function getUserByEmail(email: string) {
301
- const result = await db.select('users', 'email = ?', [email]);
302
- return result.rows[0] || null;
643
+ interface Setting {
644
+ key: string;
645
+ value: string;
646
+ description?: string;
303
647
  }
304
648
 
305
- setupUserSystem();
306
- ```
649
+ export class SettingsService extends BaseService<Setting> {
650
+ constructor() {
651
+ super('core', 'settings');
652
+ this.setPrimaryKeyFields(['key']);
653
+ }
307
654
 
308
- ### Browser Application
655
+ // Lấy giá trị setting
656
+ async getSetting(key: string): Promise<string | null> {
657
+ const setting = await this.findById(key);
658
+ return setting?.value || null;
659
+ }
309
660
 
310
- ```html
311
- <!DOCTYPE html>
312
- <html>
313
- <head>
314
- <title>Universal SQLite Browser Demo</title>
315
- </head>
316
- <body>
317
- <div id="app"></div>
661
+ // Đặt giá trị setting
662
+ async setSetting(key: string, value: string, description?: string): Promise<void> {
663
+ const existing = await this.findById(key);
318
664
 
319
- <script type="module">
320
- import UniversalSQLite from './dist/index.js';
321
-
322
- const db = new UniversalSQLite();
323
-
324
- async function browserDemo() {
325
- try {
326
- // Kết nối in-memory database
327
- await db.connect(':memory:');
328
-
329
- // Tạo bảng todos
330
- await db.createTable('todos', {
331
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
332
- title: 'TEXT NOT NULL',
333
- completed: 'BOOLEAN DEFAULT 0',
334
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
335
- });
336
-
337
- // Thêm một số todos
338
- await db.insert('todos', { title: 'Learn SQLite', completed: 0 });
339
- await db.insert('todos', { title: 'Build awesome app', completed: 0 });
340
-
341
- // Hiển thị todos
342
- const todos = await db.select('todos');
343
- document.getElementById('app').innerHTML = `
344
- <h1>My Todos</h1>
345
- <ul>
346
- ${todos.rows.map(todo =>
347
- `<li>${todo.title} - ${todo.completed ? '✅' : '⏳'}</li>`
348
- ).join('')}
349
- </ul>
350
- `;
351
-
352
- } catch (error) {
353
- console.error('Error:', error);
354
- }
355
- }
356
-
357
- browserDemo();
358
- </script>
359
- </body>
360
- </html>
361
- ```
362
-
363
- ### React Native Application
665
+ if (existing) {
666
+ await this.update(key, { value, description });
667
+ } else {
668
+ await this.create({ key, value, description });
669
+ }
670
+ }
364
671
 
365
- ```typescript
366
- // App.tsx
367
- import React, { useState, useEffect } from 'react';
368
- import { View, Text, Button, FlatList } from 'react-native';
369
- import UniversalSQLite from '@dqcai/sqlite';
672
+ // Lấy tất cả settings
673
+ async getAllSettings(): Promise<Setting[]> {
674
+ return await this.findAll({}, {
675
+ orderBy: [{ name: 'key', direction: 'ASC' }]
676
+ });
677
+ }
370
678
 
371
- const db = new UniversalSQLite();
679
+ // Xóa setting
680
+ async deleteSetting(key: string): Promise<boolean> {
681
+ return await this.delete(key);
682
+ }
372
683
 
373
- interface User {
374
- id: number;
375
- name: string;
376
- email: string;
684
+ // Lấy nhiều settings cùng lúc
685
+ async getMultipleSettings(keys: string[]): Promise<Record<string, string>> {
686
+ const dao = await this.init().then(() => this.dao!);
687
+ const placeholders = keys.map(() => '?').join(',');
688
+ const sql = `SELECT key, value FROM settings WHERE key IN (${placeholders})`;
689
+ const result = await dao.execute(sql, keys);
690
+
691
+ const settings: Record<string, string> = {};
692
+ result.rows.forEach(row => {
693
+ settings[row.key] = row.value;
694
+ });
695
+
696
+ return settings;
697
+ }
377
698
  }
699
+ ```
700
+
701
+ ## 5. Sử dụng trong ứng dụng
378
702
 
379
- export default function App() {
380
- const [users, setUsers] = useState<User[]>([]);
381
- const [environment, setEnvironment] = useState<string>('');
703
+ ### Trong React Native
704
+
705
+ ```typescript
706
+ // App.tsx hoặc index.js
707
+ import React, { useEffect, useState } from 'react';
708
+ import { View, Text, Button, Alert } from 'react-native';
709
+ import { DatabaseService } from './services/DatabaseService';
710
+ import { UserService, ProfileService } from './services/UserService';
711
+ import { SettingsService } from './services/CoreService';
712
+
713
+ const App = () => {
714
+ const [isDbReady, setIsDbReady] = useState(false);
715
+ const [userService] = useState(new UserService());
716
+ const [profileService] = useState(new ProfileService());
717
+ const [settingsService] = useState(new SettingsService());
382
718
 
383
719
  useEffect(() => {
384
- initDatabase();
720
+ initializeDatabase();
721
+
722
+ return () => {
723
+ // Cleanup khi component unmount
724
+ DatabaseService.closeAll();
725
+ };
385
726
  }, []);
386
727
 
387
- const initDatabase = async () => {
728
+ const initializeDatabase = async () => {
388
729
  try {
389
- // Kết nối database
390
- await db.connect('myapp.db');
730
+ // Khởi tạo database
731
+ await DatabaseService.initialize();
391
732
 
392
- // Hiển thị môi trường
393
- setEnvironment(db.getEnvironment());
394
-
395
- // Tạo bảng
396
- await db.createTable('users', {
397
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
398
- name: 'TEXT NOT NULL',
399
- email: 'TEXT UNIQUE'
400
- });
733
+ // Đặt role cho user hiện tại
734
+ await DatabaseService.setUserRole(['user']);
401
735
 
402
- // Load dữ liệu
403
- loadUsers();
736
+ // Khởi tạo services
737
+ await userService.init();
738
+ await profileService.init();
739
+ await settingsService.init();
404
740
 
741
+ setIsDbReady(true);
742
+ console.log('Database ready!');
743
+ } catch (error) {
744
+ console.error('Database initialization failed:', error);
745
+ Alert.alert('Error', 'Failed to initialize database');
746
+ }
747
+ };
748
+
749
+ const handleCreateUser = async () => {
750
+ if (!isDbReady) return;
751
+
752
+ try {
753
+ const newUser = await userService.createUser({
754
+ username: 'john_doe',
755
+ email: 'john@example.com',
756
+ password: 'hashed_password'
757
+ });
758
+
759
+ if (newUser) {
760
+ // Tạo profile cho user
761
+ await profileService.createProfile({
762
+ user_id: newUser.id!,
763
+ first_name: 'John',
764
+ last_name: 'Doe',
765
+ phone: '+1234567890'
766
+ });
767
+
768
+ Alert.alert('Success', 'User created successfully');
769
+ }
405
770
  } catch (error) {
406
- console.error('Database init error:', error);
771
+ console.error('Error creating user:', error);
772
+ Alert.alert('Error', 'Failed to create user');
407
773
  }
408
774
  };
409
775
 
410
- const loadUsers = async () => {
776
+ const handleGetAllUsers = async () => {
777
+ if (!isDbReady) return;
778
+
411
779
  try {
412
- const result = await db.select('users');
413
- setUsers(result.rows);
780
+ const users = await userService.getAllUsers(1, 10);
781
+ console.log('Users:', users);
782
+ Alert.alert('Users', `Found ${users.length} users`);
414
783
  } catch (error) {
415
- console.error('Load users error:', error);
784
+ console.error('Error getting users:', error);
416
785
  }
417
786
  };
418
787
 
419
- const addUser = async () => {
788
+ const handleSetSetting = async () => {
789
+ if (!isDbReady) return;
790
+
420
791
  try {
421
- await db.insert('users', {
422
- name: `User ${Date.now()}`,
423
- email: `user${Date.now()}@example.com`
424
- });
425
- loadUsers();
792
+ await settingsService.setSetting(
793
+ 'app_version',
794
+ '1.0.0',
795
+ 'Current app version'
796
+ );
797
+
798
+ const version = await settingsService.getSetting('app_version');
799
+ Alert.alert('Setting', `App version: ${version}`);
426
800
  } catch (error) {
427
- console.error('Add user error:', error);
801
+ console.error('Error setting value:', error);
428
802
  }
429
803
  };
430
804
 
431
805
  return (
432
- <View style={{ flex: 1, padding: 20 }}>
433
- <Text>Environment: {environment}</Text>
434
- <Button title="Add User" onPress={addUser} />
806
+ <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
807
+ <Text>Database Status: {isDbReady ? 'Ready' : 'Initializing...'}</Text>
808
+
809
+ <Button
810
+ title="Create User"
811
+ onPress={handleCreateUser}
812
+ disabled={!isDbReady}
813
+ />
814
+
815
+ <Button
816
+ title="Get All Users"
817
+ onPress={handleGetAllUsers}
818
+ disabled={!isDbReady}
819
+ />
435
820
 
436
- <FlatList
437
- data={users}
438
- keyExtractor={item => item.id.toString()}
439
- renderItem={({ item }) => (
440
- <View style={{ padding: 10, borderBottomWidth: 1 }}>
441
- <Text>{item.name}</Text>
442
- <Text>{item.email}</Text>
443
- </View>
444
- )}
821
+ <Button
822
+ title="Set App Setting"
823
+ onPress={handleSetSetting}
824
+ disabled={!isDbReady}
445
825
  />
446
826
  </View>
447
827
  );
448
- }
828
+ };
829
+
830
+ export default App;
449
831
  ```
450
832
 
451
- ### Deno Application
833
+ ### Trong Node.js
452
834
 
453
835
  ```typescript
454
- // main.ts
455
- import UniversalSQLite from "https://esm.sh/@dqcai/sqlite";
836
+ // app.ts
837
+ import express from 'express';
838
+ import { DatabaseService } from './services/DatabaseService';
839
+ import { UserService, ProfileService } from './services/UserService';
840
+ import { SettingsService } from './services/CoreService';
841
+
842
+ const app = express();
843
+ app.use(express.json());
456
844
 
457
- const db = new UniversalSQLite();
845
+ // Services
846
+ const userService = new UserService();
847
+ const profileService = new ProfileService();
848
+ const settingsService = new SettingsService();
458
849
 
459
- async function denoExample() {
850
+ // Khởi tạo database khi start server
851
+ async function initializeApp() {
460
852
  try {
461
- await db.connect('./deno_database.db');
853
+ console.log('Initializing database...');
462
854
 
463
- // Tạo bảng logs
464
- await db.createTable('logs', {
465
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
466
- level: 'TEXT NOT NULL',
467
- message: 'TEXT NOT NULL',
468
- timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
469
- });
470
-
471
- // Thêm log entries
472
- await db.insert('logs', { level: 'INFO', message: 'Application started' });
473
- await db.insert('logs', { level: 'DEBUG', message: 'Database connected' });
474
- await db.insert('logs', { level: 'ERROR', message: 'Something went wrong' });
855
+ // Khởi tạo DatabaseService
856
+ await DatabaseService.initialize();
475
857
 
476
- // Truy vấn logs theo level
477
- const errorLogs = await db.select('logs', 'level = ?', ['ERROR']);
478
- console.log('Error logs:', errorLogs.rows);
858
+ // Set role admin cho server
859
+ await DatabaseService.setUserRole(['admin']);
479
860
 
480
- // Sử dụng QueryBuilder
481
- const recentLogs = QueryBuilder
482
- .table('logs')
483
- .select(['level', 'message', 'timestamp'])
484
- .where('timestamp > datetime("now", "-1 hour")')
485
- .orderBy('timestamp', 'DESC')
486
- .limit(10)
487
- .toSQL();
861
+ // Khởi tạo services
862
+ await userService.init();
863
+ await profileService.init();
864
+ await settingsService.init();
488
865
 
489
- const result = await db.query(recentLogs);
490
- console.log('Recent logs:', result.rows);
491
-
492
- } finally {
493
- await db.close();
866
+ console.log('Database initialized successfully');
867
+ } catch (error) {
868
+ console.error('Database initialization failed:', error);
869
+ process.exit(1);
494
870
  }
495
871
  }
496
872
 
497
- // Chạy với: deno run --allow-read --allow-write main.ts
498
- denoExample();
499
- ```
500
-
501
- ### Bun Application
873
+ // API Routes
502
874
 
503
- ```typescript
504
- // app.ts
505
- import UniversalSQLite from '@dqcai/sqlite';
875
+ // POST /users - Tạo user mới
876
+ app.post('/users', async (req, res) => {
877
+ try {
878
+ const { username, email, password } = req.body;
879
+
880
+ if (!username || !email || !password) {
881
+ return res.status(400).json({ error: 'Missing required fields' });
882
+ }
506
883
 
507
- const db = new UniversalSQLite();
884
+ const user = await userService.createUser({
885
+ username,
886
+ email,
887
+ password // Nên hash password trước khi lưu
888
+ });
508
889
 
509
- async function bunExample() {
510
- console.log('Environment:', db.getEnvironment()); // "Bun"
511
-
512
- await db.connect('./bun_database.db');
513
-
514
- // Tạo bảng products
515
- await db.createTable('products', {
516
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
517
- name: 'TEXT NOT NULL',
518
- price: 'REAL NOT NULL',
519
- category: 'TEXT',
520
- in_stock: 'BOOLEAN DEFAULT 1'
521
- });
522
-
523
- // Bulk insert
524
- const products = [
525
- { name: 'Laptop', price: 999.99, category: 'Electronics' },
526
- { name: 'Mouse', price: 29.99, category: 'Electronics' },
527
- { name: 'Desk', price: 199.99, category: 'Furniture' }
528
- ];
529
-
530
- for (const product of products) {
531
- await db.insert('products', product);
890
+ res.status(201).json({ success: true, user });
891
+ } catch (error: any) {
892
+ console.error('Error creating user:', error);
893
+ res.status(400).json({ error: error.message });
532
894
  }
533
-
534
- // Truy vấn phức tạp
535
- const expensiveProducts = await db.query(`
536
- SELECT name, price, category
537
- FROM products
538
- WHERE price > ? AND in_stock = 1
539
- ORDER BY price DESC
540
- `, [100]);
541
-
542
- console.log('Expensive products:', expensiveProducts.rows);
543
-
544
- await db.close();
545
- }
895
+ });
546
896
 
547
- // Chạy với: bun run app.ts
548
- bunExample();
549
- ```
897
+ // GET /users - Lấy danh sách users
898
+ app.get('/users', async (req, res) => {
899
+ try {
900
+ const page = parseInt(req.query.page as string) || 1;
901
+ const limit = parseInt(req.query.limit as string) || 10;
902
+
903
+ const users = await userService.getAllUsers(page, limit);
904
+ const total = await userService.getTotalUsers();
905
+
906
+ res.json({
907
+ success: true,
908
+ users,
909
+ pagination: {
910
+ page,
911
+ limit,
912
+ total,
913
+ pages: Math.ceil(total / limit)
914
+ }
915
+ });
916
+ } catch (error: any) {
917
+ console.error('Error getting users:', error);
918
+ res.status(500).json({ error: error.message });
919
+ }
920
+ });
550
921
 
551
- ## 🏗️ dụ nâng cao
552
-
553
- ### 1. Transaction Management
554
-
555
- ```typescript
556
- import { SQLiteManager } from '@dqcai/sqlite';
557
-
558
- async function transactionExample() {
559
- const manager = new SQLiteManager();
560
- const connection = await manager.connect('transactions.db');
561
-
922
+ // GET /users/:id - Lấy user theo ID
923
+ app.get('/users/:id', async (req, res) => {
562
924
  try {
563
- // Bắt đầu transaction
564
- await connection.execute('BEGIN TRANSACTION');
565
-
566
- // Tạo bảng accounts
567
- await connection.execute(`
568
- CREATE TABLE IF NOT EXISTS accounts (
569
- id INTEGER PRIMARY KEY AUTOINCREMENT,
570
- name TEXT NOT NULL,
571
- balance REAL DEFAULT 0.00
572
- )
573
- `);
574
-
575
- // Thêm accounts
576
- await connection.execute('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Alice', 1000]);
577
- await connection.execute('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Bob', 500]);
578
-
579
- // Chuyển tiền từ Alice sang Bob
580
- const transferAmount = 200;
581
-
582
- // Trừ tiền Alice
583
- await connection.execute(
584
- 'UPDATE accounts SET balance = balance - ? WHERE name = ?',
585
- [transferAmount, 'Alice']
586
- );
587
-
588
- // Cộng tiền Bob
589
- await connection.execute(
590
- 'UPDATE accounts SET balance = balance + ? WHERE name = ?',
591
- [transferAmount, 'Bob']
592
- );
925
+ const id = parseInt(req.params.id);
926
+ const user = await userService.findById(id);
593
927
 
594
- // Kiểm tra balance không âm
595
- const aliceBalance = await connection.execute('SELECT balance FROM accounts WHERE name = ?', ['Alice']);
596
- if (aliceBalance.rows[0].balance < 0) {
597
- throw new Error('Insufficient funds');
928
+ if (!user) {
929
+ return res.status(404).json({ error: 'User not found' });
598
930
  }
599
931
 
600
- // Commit transaction
601
- await connection.execute('COMMIT');
602
- console.log('Transfer successful!');
603
-
604
- // Hiển thị kết quả
605
- const allAccounts = await connection.execute('SELECT * FROM accounts');
606
- console.log('Final balances:', allAccounts.rows);
607
-
608
- } catch (error) {
609
- // Rollback nếu có lỗi
610
- await connection.execute('ROLLBACK');
611
- console.error('Transaction failed:', error);
612
- } finally {
613
- await connection.close();
614
- }
615
- }
616
-
617
- transactionExample();
618
- ```
619
-
620
- ### 2. Complex Queries với JOIN
621
-
622
- ```typescript
623
- async function complexQueryExample() {
624
- const db = new UniversalSQLite();
625
- await db.connect('ecommerce.db');
626
-
627
- // Tạo schema phức tạp
628
- await db.createTable('categories', {
629
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
630
- name: 'TEXT NOT NULL UNIQUE',
631
- description: 'TEXT'
632
- });
633
-
634
- await db.createTable('products', {
635
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
636
- name: 'TEXT NOT NULL',
637
- price: 'REAL NOT NULL',
638
- category_id: 'INTEGER NOT NULL',
639
- 'FOREIGN KEY (category_id)': 'REFERENCES categories(id)'
640
- });
641
-
642
- await db.createTable('orders', {
643
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
644
- product_id: 'INTEGER NOT NULL',
645
- quantity: 'INTEGER NOT NULL',
646
- order_date: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
647
- 'FOREIGN KEY (product_id)': 'REFERENCES products(id)'
648
- });
649
-
650
- // Thêm dữ liệu mẫu
651
- await db.insert('categories', { name: 'Electronics', description: 'Electronic devices' });
652
- await db.insert('categories', { name: 'Books', description: 'Books and literature' });
653
-
654
- await db.insert('products', { name: 'iPhone 15', price: 999, category_id: 1 });
655
- await db.insert('products', { name: 'MacBook Pro', price: 2499, category_id: 1 });
656
- await db.insert('products', { name: 'JavaScript Guide', price: 29.99, category_id: 2 });
657
-
658
- await db.insert('orders', { product_id: 1, quantity: 2 });
659
- await db.insert('orders', { product_id: 2, quantity: 1 });
660
- await db.insert('orders', { product_id: 1, quantity: 1 });
661
-
662
- // Query phức tạp với JOIN
663
- const salesReport = await db.query(`
664
- SELECT
665
- c.name as category,
666
- p.name as product,
667
- SUM(o.quantity) as total_sold,
668
- SUM(o.quantity * p.price) as total_revenue,
669
- AVG(p.price) as avg_price
670
- FROM orders o
671
- JOIN products p ON o.product_id = p.id
672
- JOIN categories c ON p.category_id = c.id
673
- GROUP BY c.id, p.id
674
- ORDER BY total_revenue DESC
675
- `);
676
-
677
- console.log('Sales Report:', salesReport.rows);
678
-
679
- // Subquery example
680
- const topSellingCategory = await db.query(`
681
- SELECT
682
- c.name,
683
- (SELECT SUM(quantity) FROM orders o
684
- JOIN products p ON o.product_id = p.id
685
- WHERE p.category_id = c.id) as total_quantity
686
- FROM categories c
687
- ORDER BY total_quantity DESC
688
- LIMIT 1
689
- `);
690
-
691
- console.log('Top selling category:', topSellingCategory.rows[0]);
692
-
693
- await db.close();
694
- }
695
-
696
- complexQueryExample();
697
- ```
698
-
699
- ### 3. Real-time Data Synchronization
700
-
701
- ```typescript
702
- async function realtimeExample() {
703
- const db = new UniversalSQLite();
704
- await db.connect('realtime.db');
705
-
706
- // Tạo bảng cho sync
707
- await db.createTable('sync_queue', {
708
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
709
- table_name: 'TEXT NOT NULL',
710
- operation: 'TEXT NOT NULL', // INSERT, UPDATE, DELETE
711
- data: 'TEXT', // JSON data
712
- synced: 'BOOLEAN DEFAULT 0',
713
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
714
- });
715
-
716
- await db.createTable('users', {
717
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
718
- name: 'TEXT NOT NULL',
719
- email: 'TEXT UNIQUE',
720
- last_modified: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
721
- });
722
-
723
- // Helper function để track changes
724
- async function trackChange(table: string, operation: string, data: any) {
725
- await db.insert('sync_queue', {
726
- table_name: table,
727
- operation: operation,
728
- data: JSON.stringify(data)
729
- });
730
- }
731
-
732
- // Wrapper functions với tracking
733
- async function insertUser(userData: any) {
734
- const result = await db.insert('users', userData);
735
- await trackChange('users', 'INSERT', { ...userData, id: result.lastInsertRowId });
736
- return result;
737
- }
738
-
739
- async function updateUser(id: number, userData: any) {
740
- const result = await db.update('users',
741
- { ...userData, last_modified: new Date().toISOString() },
742
- 'id = ?',
743
- [id]
744
- );
745
- await trackChange('users', 'UPDATE', { id, ...userData });
746
- return result;
747
- }
748
-
749
- async function deleteUser(id: number) {
750
- const result = await db.delete('users', 'id = ?', [id]);
751
- await trackChange('users', 'DELETE', { id });
752
- return result;
753
- }
754
-
755
- // Sync function
756
- async function syncToServer() {
757
- const unsyncedChanges = await db.select('sync_queue', 'synced = 0');
758
-
759
- for (const change of unsyncedChanges.rows) {
760
- try {
761
- // Giả lập API call
762
- console.log(`Syncing ${change.operation} on ${change.table_name}:`,
763
- JSON.parse(change.data));
764
-
765
- // Đánh dấu đã sync
766
- await db.update('sync_queue',
767
- { synced: 1 },
768
- 'id = ?',
769
- [change.id]
770
- );
771
-
772
- } catch (error) {
773
- console.error('Sync failed for change:', change.id, error);
774
- }
775
- }
932
+ res.json({ success: true, user });
933
+ } catch (error: any) {
934
+ console.error('Error getting user:', error);
935
+ res.status(500).json({ error: error.message });
776
936
  }
777
-
778
- // Demo usage
779
- await insertUser({ name: 'Alice', email: 'alice@example.com' });
780
- await insertUser({ name: 'Bob', email: 'bob@example.com' });
781
- await updateUser(1, { email: 'alice.updated@example.com' });
782
-
783
- // Sync changes
784
- await syncToServer();
785
-
786
- await db.close();
787
- }
788
-
789
- realtimeExample();
790
- ```
791
-
792
- ### 4. Database Migration System
937
+ });
793
938
 
794
- ```typescript
795
- class DatabaseMigration {
796
- private db: UniversalSQLite;
797
- private currentVersion: number = 0;
798
-
799
- constructor(db: UniversalSQLite) {
800
- this.db = db;
801
- }
802
-
803
- async getCurrentVersion(): Promise<number> {
804
- try {
805
- const result = await this.db.query('PRAGMA user_version');
806
- return result.rows[0].user_version || 0;
807
- } catch {
808
- return 0;
809
- }
810
- }
811
-
812
- async setVersion(version: number): Promise<void> {
813
- await this.db.query(`PRAGMA user_version = ${version}`);
814
- }
815
-
816
- async migrate() {
817
- this.currentVersion = await this.getCurrentVersion();
818
- console.log(`Current database version: ${this.currentVersion}`);
939
+ // PUT /users/:id - Cập nhật user
940
+ app.put('/users/:id', async (req, res) => {
941
+ try {
942
+ const id = parseInt(req.params.id);
943
+ const updates = req.body;
819
944
 
820
- const migrations = [
821
- this.migration1_initial,
822
- this.migration2_addUserProfiles,
823
- this.migration3_addIndexes,
824
- this.migration4_addAuditLog
825
- ];
945
+ const user = await userService.updateUser(id, updates);
826
946
 
827
- for (let i = this.currentVersion; i < migrations.length; i++) {
828
- console.log(`Running migration ${i + 1}...`);
829
- await migrations[i].call(this);
830
- await this.setVersion(i + 1);
831
- console.log(`Migration ${i + 1} completed`);
947
+ if (!user) {
948
+ return res.status(404).json({ error: 'User not found' });
832
949
  }
833
950
 
834
- console.log('All migrations completed!');
835
- }
836
-
837
- // Migration 1: Initial schema
838
- private async migration1_initial() {
839
- await this.db.createTable('users', {
840
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
841
- username: 'TEXT UNIQUE NOT NULL',
842
- email: 'TEXT UNIQUE NOT NULL',
843
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
844
- });
845
- }
846
-
847
- // Migration 2: Add user profiles
848
- private async migration2_addUserProfiles() {
849
- await this.db.query('ALTER TABLE users ADD COLUMN first_name TEXT');
850
- await this.db.query('ALTER TABLE users ADD COLUMN last_name TEXT');
851
- await this.db.query('ALTER TABLE users ADD COLUMN avatar_url TEXT');
852
- }
853
-
854
- // Migration 3: Add performance indexes
855
- private async migration3_addIndexes() {
856
- await this.db.query('CREATE INDEX idx_users_email ON users(email)');
857
- await this.db.query('CREATE INDEX idx_users_username ON users(username)');
858
- await this.db.query('CREATE INDEX idx_users_created_at ON users(created_at)');
859
- }
860
-
861
- // Migration 4: Add audit log
862
- private async migration4_addAuditLog() {
863
- await this.db.createTable('audit_logs', {
864
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
865
- table_name: 'TEXT NOT NULL',
866
- record_id: 'INTEGER NOT NULL',
867
- action: 'TEXT NOT NULL',
868
- old_values: 'TEXT',
869
- new_values: 'TEXT',
870
- user_id: 'INTEGER',
871
- timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
872
- });
873
-
874
- // Tạo trigger cho audit
875
- await this.db.query(`
876
- CREATE TRIGGER audit_users_update
877
- AFTER UPDATE ON users
878
- FOR EACH ROW
879
- BEGIN
880
- INSERT INTO audit_logs (table_name, record_id, action, old_values, new_values)
881
- VALUES ('users', NEW.id, 'UPDATE',
882
- json_object('username', OLD.username, 'email', OLD.email),
883
- json_object('username', NEW.username, 'email', NEW.email));
884
- END
885
- `);
951
+ res.json({ success: true, user });
952
+ } catch (error: any) {
953
+ console.error('Error updating user:', error);
954
+ res.status(400).json({ error: error.message });
886
955
  }
887
- }
888
-
889
- // Sử dụng migration
890
- async function migrationExample() {
891
- const db = new UniversalSQLite();
892
- await db.connect('app_with_migrations.db');
893
-
894
- const migration = new DatabaseMigration(db);
895
- await migration.migrate();
896
-
897
- // Test data sau migration
898
- await db.insert('users', {
899
- username: 'testuser',
900
- email: 'test@example.com',
901
- first_name: 'Test',
902
- last_name: 'User'
903
- });
904
-
905
- // Update để trigger audit
906
- await db.update('users',
907
- { email: 'test.updated@example.com' },
908
- 'username = ?',
909
- ['testuser']
910
- );
911
-
912
- // Kiểm tra audit log
913
- const auditLogs = await db.select('audit_logs');
914
- console.log('Audit logs:', auditLogs.rows);
915
-
916
- await db.close();
917
- }
918
-
919
- migrationExample();
920
- ```
921
-
922
- ### 5. Full-Text Search
923
-
924
- ```typescript
925
- async function fullTextSearchExample() {
926
- const db = new UniversalSQLite();
927
- await db.connect('search.db');
928
-
929
- // Tạo FTS table
930
- await db.query(`
931
- CREATE VIRTUAL TABLE IF NOT EXISTS articles_fts
932
- USING fts5(id, title, content, author)
933
- `);
934
-
935
- // Tạo bảng articles thường
936
- await db.createTable('articles', {
937
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
938
- title: 'TEXT NOT NULL',
939
- content: 'TEXT NOT NULL',
940
- author: 'TEXT NOT NULL',
941
- published_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
942
- });
943
-
944
- // Thêm dữ liệu mẫu
945
- const articles = [
946
- {
947
- title: 'Introduction to SQLite',
948
- content: 'SQLite is a lightweight database engine that is perfect for mobile and web applications. It provides ACID compliance and supports most of the SQL standard.',
949
- author: 'John Doe'
950
- },
951
- {
952
- title: 'Advanced TypeScript Patterns',
953
- content: 'TypeScript provides powerful type system features including generics, conditional types, and mapped types that enable building robust applications.',
954
- author: 'Jane Smith'
955
- },
956
- {
957
- title: 'React Native Performance Tips',
958
- content: 'Optimizing React Native apps requires understanding of the bridge, native modules, and proper state management patterns.',
959
- author: 'Bob Johnson'
960
- }
961
- ];
962
-
963
- for (const article of articles) {
964
- const result = await db.insert('articles', article);
965
- // Thêm vào FTS index
966
- await db.query(`
967
- INSERT INTO articles_fts (id, title, content, author)
968
- VALUES (?, ?, ?, ?)
969
- `, [result.lastInsertRowId, article.title, article.content, article.author]);
970
- }
971
-
972
- // Full-text search
973
- const searchResults = await db.query(`
974
- SELECT
975
- a.id, a.title, a.author, a.published_at,
976
- snippet(articles_fts, 1, '<b>', '</b>', '...', 50) as title_snippet,
977
- snippet(articles_fts, 2, '<b>', '</b>', '...', 100) as content_snippet
978
- FROM articles_fts
979
- JOIN articles a ON articles_fts.id = a.id
980
- WHERE articles_fts MATCH ?
981
- ORDER BY rank
982
- `, ['SQLite OR TypeScript']);
983
-
984
- console.log('Search results:', searchResults.rows);
985
-
986
- // Advanced search với bxm25 ranking
987
- const advancedSearch = await db.query(`
988
- SELECT
989
- a.*,
990
- bm25(articles_fts) as relevance_score
991
- FROM articles_fts
992
- JOIN articles a ON articles_fts.id = a.id
993
- WHERE articles_fts MATCH ?
994
- ORDER BY relevance_score
995
- `, ['application AND (mobile OR web)']);
996
-
997
- console.log('Advanced search:', advancedSearch.rows);
998
-
999
- await db.close();
1000
- }
1001
-
1002
- fullTextSearchExample();
1003
- ```
1004
-
1005
- ### 6. Data Analytics và Reporting
1006
-
1007
- ```typescript
1008
- async function analyticsExample() {
1009
- const db = new UniversalSQLite();
1010
- await db.connect('analytics.db');
1011
-
1012
- // Tạo schema cho analytics
1013
- await db.createTable('events', {
1014
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1015
- user_id: 'INTEGER NOT NULL',
1016
- event_type: 'TEXT NOT NULL',
1017
- event_data: 'TEXT', // JSON
1018
- timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1019
- });
1020
-
1021
- await db.createTable('users_analytics', {
1022
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1023
- user_id: 'INTEGER UNIQUE NOT NULL',
1024
- total_events: 'INTEGER DEFAULT 0',
1025
- first_seen: 'DATETIME',
1026
- last_seen: 'DATETIME',
1027
- session_count: 'INTEGER DEFAULT 0'
1028
- });
1029
-
1030
- // Thêm dữ liệu events mẫu
1031
- const sampleEvents = [
1032
- { user_id: 1, event_type: 'page_view', event_data: '{"page": "/home"}' },
1033
- { user_id: 1, event_type: 'click', event_data: '{"element": "signup_button"}' },
1034
- { user_id: 2, event_type: 'page_view', event_data: '{"page": "/products"}' },
1035
- { user_id: 1, event_type: 'purchase', event_data: '{"product_id": 123, "amount": 99.99}' },
1036
- { user_id: 3, event_type: 'page_view', event_data: '{"page": "/home"}' },
1037
- { user_id: 2, event_type: 'purchase', event_data: '{"product_id": 456, "amount": 149.99}' }
1038
- ];
1039
-
1040
- for (const event of sampleEvents) {
1041
- await db.insert('events', event);
1042
- }
1043
-
1044
- // Analytics queries
1045
-
1046
- // 1. User engagement report
1047
- const userEngagement = await db.query(`
1048
- SELECT
1049
- user_id,
1050
- COUNT(*) as total_events,
1051
- COUNT(CASE WHEN event_type = 'purchase' THEN 1 END) as purchases,
1052
- MIN(timestamp) as first_seen,
1053
- MAX(timestamp) as last_seen,
1054
- ROUND(
1055
- JULIANDAY(MAX(timestamp)) - JULIANDAY(MIN(timestamp))
1056
- ) as days_active
1057
- FROM events
1058
- GROUP BY user_id
1059
- ORDER BY total_events DESC
1060
- `);
1061
-
1062
- console.log('User Engagement Report:', userEngagement.rows);
1063
-
1064
- // 2. Revenue analytics
1065
- const revenueReport = await db.query(`
1066
- SELECT
1067
- DATE(timestamp) as date,
1068
- COUNT(*) as purchase_count,
1069
- SUM(CAST(json_extract(event_data, '$.amount') AS REAL)) as total_revenue,
1070
- AVG(CAST(json_extract(event_data, '$.amount') AS REAL)) as avg_order_value
1071
- FROM events
1072
- WHERE event_type = 'purchase'
1073
- GROUP BY DATE(timestamp)
1074
- ORDER BY date DESC
1075
- `);
1076
-
1077
- console.log('Revenue Report:', revenueReport.rows);
1078
-
1079
- // 3. Funnel analysis
1080
- const funnelAnalysis = await db.query(`
1081
- WITH user_funnel AS (
1082
- SELECT
1083
- user_id,
1084
- MAX(CASE WHEN event_type = 'page_view' THEN 1 ELSE 0 END) as viewed,
1085
- MAX(CASE WHEN event_type = 'click' THEN 1 ELSE 0 END) as clicked,
1086
- MAX(CASE WHEN event_type = 'purchase' THEN 1 ELSE 0 END) as purchased
1087
- FROM events
1088
- GROUP BY user_id
1089
- )
1090
- SELECT
1091
- SUM(viewed) as total_users,
1092
- SUM(clicked) as users_clicked,
1093
- SUM(purchased) as users_purchased,
1094
- ROUND(SUM(clicked) * 100.0 / SUM(viewed), 2) as click_rate_percent,
1095
- ROUND(SUM(purchased) * 100.0 / SUM(clicked), 2) as conversion_rate_percent
1096
- FROM user_funnel
1097
- `);
1098
-
1099
- console.log('Funnel Analysis:', funnelAnalysis.rows[0]);
1100
-
1101
- await db.close();
1102
- }
1103
-
1104
- analyticsExample();
1105
- ```
1106
-
1107
- ## 🔍 Debugging và Monitoring
1108
-
1109
- ### 1. Query Performance Monitoring
956
+ });
1110
957
 
1111
- ```typescript
1112
- class PerformanceMonitor {
1113
- private db: UniversalSQLite;
1114
-
1115
- constructor(db: UniversalSQLite) {
1116
- this.db = db;
1117
- }
1118
-
1119
- async queryWithTiming(sql: string, params?: any[]): Promise<{result: any, duration: number}> {
1120
- const startTime = performance.now();
1121
- const result = await this.db.query(sql, params);
1122
- const endTime = performance.now();
1123
- const duration = endTime - startTime;
958
+ // DELETE /users/:id - Xóa user
959
+ app.delete('/users/:id', async (req, res) => {
960
+ try {
961
+ const id = parseInt(req.params.id);
962
+ const success = await userService.delete(id);
1124
963
 
1125
- console.log(`Query executed in ${duration.toFixed(2)}ms: ${sql.substring(0, 100)}...`);
964
+ if (!success) {
965
+ return res.status(404).json({ error: 'User not found' });
966
+ }
1126
967
 
1127
- return { result, duration };
1128
- }
1129
-
1130
- async explainQuery(sql: string): Promise<void> {
1131
- const explanation = await this.db.query(`EXPLAIN QUERY PLAN ${sql}`);
1132
- console.log('Query Plan:');
1133
- explanation.rows.forEach(row => {
1134
- console.log(` ${row.detail}`);
1135
- });
968
+ res.json({ success: true, message: 'User deleted' });
969
+ } catch (error: any) {
970
+ console.error('Error deleting user:', error);
971
+ res.status(500).json({ error: error.message });
1136
972
  }
1137
- }
973
+ });
1138
974
 
1139
- async function performanceExample() {
1140
- const db = new UniversalSQLite();
1141
- await db.connect('performance_test.db');
1142
- const monitor = new PerformanceMonitor(db);
1143
-
1144
- // Tạo bảng với nhiều dữ liệu
1145
- await db.createTable('large_table', {
1146
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1147
- name: 'TEXT',
1148
- value: 'INTEGER',
1149
- category: 'TEXT'
1150
- });
1151
-
1152
- // Insert bulk data
1153
- console.log('Inserting test data...');
1154
- for (let i = 0; i < 1000; i++) {
1155
- await db.insert('large_table', {
1156
- name: `Item ${i}`,
1157
- value: Math.floor(Math.random() * 1000),
1158
- category: `Category ${i % 10}`
1159
- });
975
+ // POST /users/:id/profile - Tạo profile cho user
976
+ app.post('/users/:id/profile', async (req, res) => {
977
+ try {
978
+ const user_id = parseInt(req.params.id);
979
+ const profileData = { ...req.body, user_id };
980
+
981
+ const profile = await profileService.createProfile(profileData);
982
+ res.status(201).json({ success: true, profile });
983
+ } catch (error: any) {
984
+ console.error('Error creating profile:', error);
985
+ res.status(400).json({ error: error.message });
1160
986
  }
1161
-
1162
- // Test query performance
1163
- const { result, duration } = await monitor.queryWithTiming(`
1164
- SELECT category, COUNT(*) as count, AVG(value) as avg_value
1165
- FROM large_table
1166
- GROUP BY category
1167
- ORDER BY count DESC
1168
- `);
1169
-
1170
- console.log('Aggregation result:', result.rows);
1171
-
1172
- // Explain query
1173
- await monitor.explainQuery('SELECT * FROM large_table WHERE value > 500');
1174
-
1175
- await db.close();
1176
- }
1177
-
1178
- performanceExample();
1179
- ```
1180
-
1181
- ### 2. Connection Pool (Advanced)
987
+ });
1182
988
 
1183
- ```typescript
1184
- class ConnectionPool {
1185
- private connections: Map<string, UniversalSQLite> = new Map();
1186
- private maxConnections: number = 5;
1187
-
1188
- async getConnection(dbPath: string): Promise<UniversalSQLite> {
1189
- if (this.connections.has(dbPath)) {
1190
- return this.connections.get(dbPath)!;
1191
- }
989
+ // GET /users/:id/full - Lấy user với profile
990
+ app.get('/users/:id/full', async (req, res) => {
991
+ try {
992
+ const user_id = parseInt(req.params.id);
993
+ const userWithProfile = await profileService.getUserWithProfile(user_id);
1192
994
 
1193
- if (this.connections.size >= this.maxConnections) {
1194
- throw new Error('Connection pool exhausted');
995
+ if (!userWithProfile) {
996
+ return res.status(404).json({ error: 'User not found' });
1195
997
  }
1196
998
 
1197
- const db = new UniversalSQLite();
1198
- await db.connect(dbPath);
1199
- this.connections.set(dbPath, db);
1200
-
1201
- return db;
999
+ res.json({ success: true, user: userWithProfile });
1000
+ } catch (error: any) {
1001
+ console.error('Error getting user with profile:', error);
1002
+ res.status(500).json({ error: error.message });
1202
1003
  }
1203
-
1204
- async closeAll(): Promise<void> {
1205
- for (const [path, db] of this.connections) {
1206
- await db.close();
1207
- }
1208
- this.connections.clear();
1209
- }
1210
- }
1004
+ });
1211
1005
 
1212
- async function connectionPoolExample() {
1213
- const pool = new ConnectionPool();
1214
-
1006
+ // Settings API
1007
+ app.get('/settings/:key', async (req, res) => {
1215
1008
  try {
1216
- // Sử dụng multiple databases
1217
- const mainDb = await pool.getConnection('main.db');
1218
- const cacheDb = await pool.getConnection('cache.db');
1219
- const logsDb = await pool.getConnection('logs.db');
1220
-
1221
- // Setup schemas
1222
- await mainDb.createTable('users', {
1223
- id: 'INTEGER PRIMARY KEY',
1224
- name: 'TEXT',
1225
- email: 'TEXT'
1226
- });
1227
-
1228
- await cacheDb.createTable('cache_entries', {
1229
- key: 'TEXT PRIMARY KEY',
1230
- value: 'TEXT',
1231
- expires_at: 'DATETIME'
1232
- });
1233
-
1234
- await logsDb.createTable('access_logs', {
1235
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1236
- ip: 'TEXT',
1237
- path: 'TEXT',
1238
- timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1239
- });
1240
-
1241
- // Concurrent operations
1242
- await Promise.all([
1243
- mainDb.insert('users', { name: 'Alice', email: 'alice@test.com' }),
1244
- cacheDb.insert('cache_entries', { key: 'user:1', value: '{"name":"Alice"}', expires_at: '2024-12-31 23:59:59' }),
1245
- logsDb.insert('access_logs', { ip: '192.168.1.1', path: '/api/users' })
1246
- ]);
1247
-
1248
- console.log('All operations completed successfully');
1249
-
1250
- } finally {
1251
- await pool.closeAll();
1009
+ const value = await settingsService.getSetting(req.params.key);
1010
+ res.json({ success: true, value });
1011
+ } catch (error: any) {
1012
+ console.error('Error getting setting:', error);
1013
+ res.status(500).json({ error: error.message });
1252
1014
  }
1253
- }
1254
-
1255
- connectionPoolExample();
1256
- ```
1257
-
1258
- ## 🛠️ Configuration và Customization
1015
+ });
1259
1016
 
1260
- ### Environment Detection
1017
+ app.post('/settings', async (req, res) => {
1018
+ try {
1019
+ const { key, value, description } = req.body;
1020
+ await settingsService.setSetting(key, value, description);
1021
+ res.json({ success: true, message: 'Setting saved' });
1022
+ } catch (error: any) {
1023
+ console.error('Error setting value:', error);
1024
+ res.status(500).json({ error: error.message });
1025
+ }
1026
+ });
1261
1027
 
1262
- ```typescript
1263
- import { SQLiteManager } from '@dqcai/sqlite';
1264
-
1265
- const manager = new SQLiteManager();
1266
-
1267
- // Kiểm tra môi trường hiện tại
1268
- const env = manager.getEnvironmentInfo();
1269
- console.log('Running on:', env);
1270
-
1271
- // Conditional logic based on environment
1272
- switch (env) {
1273
- case 'Node.js':
1274
- console.log('Server-side database with file system access');
1275
- break;
1276
- case 'Browser':
1277
- console.log('Client-side database with local storage');
1278
- break;
1279
- case 'React Native':
1280
- console.log('Mobile database with native SQLite');
1281
- break;
1282
- case 'React Native Windows':
1283
- console.log('Windows mobile database');
1284
- break;
1285
- case 'Deno':
1286
- console.log('Deno runtime with modern SQLite');
1287
- break;
1288
- case 'Bun':
1289
- console.log('Bun runtime with built-in SQLite');
1290
- break;
1291
- }
1292
- ```
1028
+ // Graceful shutdown
1029
+ process.on('SIGINT', async () => {
1030
+ console.log('Shutting down server...');
1031
+ await DatabaseService.closeAll();
1032
+ process.exit(0);
1033
+ });
1293
1034
 
1294
- ### Custom Error Handling
1035
+ process.on('SIGTERM', async () => {
1036
+ console.log('Shutting down server...');
1037
+ await DatabaseService.closeAll();
1038
+ process.exit(0);
1039
+ });
1295
1040
 
1296
- ```typescript
1297
- class DatabaseErrorHandler {
1298
- static async safeExecute<T>(
1299
- operation: () => Promise<T>,
1300
- fallback?: T
1301
- ): Promise<T | null> {
1302
- try {
1303
- return await operation();
1304
- } catch (error) {
1305
- console.error('Database operation failed:', error);
1306
-
1307
- if (error.message.includes('UNIQUE constraint failed')) {
1308
- console.log('Duplicate entry detected');
1309
- } else if (error.message.includes('no such table')) {
1310
- console.log('Table does not exist');
1311
- } else if (error.message.includes('database is locked')) {
1312
- console.log('Database is busy, retrying...');
1313
- // Implement retry logic
1314
- await new Promise(resolve => setTimeout(resolve, 100));
1315
- return await operation();
1316
- }
1317
-
1318
- return fallback || null;
1319
- }
1320
- }
1321
- }
1041
+ // Start server
1042
+ const PORT = process.env.PORT || 3000;
1322
1043
 
1323
- async function errorHandlingExample() {
1324
- const db = new UniversalSQLite();
1325
- await db.connect('error_test.db');
1326
-
1327
- await db.createTable('users', {
1328
- id: 'INTEGER PRIMARY KEY',
1329
- email: 'TEXT UNIQUE NOT NULL'
1330
- });
1331
-
1332
- // Safe insert với error handling
1333
- const user1 = await DatabaseErrorHandler.safeExecute(async () => {
1334
- return await db.insert('users', { email: 'test@example.com' });
1335
- });
1336
-
1337
- // Duplicate insert sẽ fail gracefully
1338
- const user2 = await DatabaseErrorHandler.safeExecute(async () => {
1339
- return await db.insert('users', { email: 'test@example.com' });
1044
+ initializeApp().then(() => {
1045
+ app.listen(PORT, () => {
1046
+ console.log(`Server running on port ${PORT}`);
1340
1047
  });
1341
-
1342
- console.log('User1 result:', user1);
1343
- console.log('User2 result:', user2); // null due to unique constraint
1344
-
1345
- await db.close();
1346
- }
1347
-
1348
- errorHandlingExample();
1048
+ });
1349
1049
  ```
1350
1050
 
1351
- ## 📱 Platform-Specific Examples
1051
+ ## 6. Quản lý Database với DatabaseManager
1352
1052
 
1353
- ### React Native với Expo
1053
+ ### Mở/Đóng Connections
1354
1054
 
1355
1055
  ```typescript
1356
- // expo-sqlite-example.ts
1357
- import { StatusBar } from 'expo-status-bar';
1358
- import { StyleSheet, Text, View, Button, FlatList } from 'react-native';
1359
- import { useState, useEffect } from 'react';
1360
- import UniversalSQLite from '@dqcai/sqlite';
1361
-
1362
- const db = new UniversalSQLite();
1363
-
1364
- export default function App() {
1365
- const [todos, setTodos] = useState([]);
1366
- const [isLoading, setIsLoading] = useState(true);
1056
+ // DatabaseHelper.ts
1057
+ import { DatabaseManager } from '@dqcai/sqlite';
1367
1058
 
1368
- useEffect(() => {
1369
- initDatabase();
1370
- }, []);
1059
+ export class DatabaseHelper {
1060
+
1061
+ // Kiểm tra trạng thái kết nối
1062
+ static checkConnectionStatus(): void {
1063
+ const connections = DatabaseManager.getConnections();
1064
+ const count = DatabaseManager.getConnectionCount();
1065
+ const activeList = DatabaseManager.listConnections();
1066
+
1067
+ console.log('Active connections:', count);
1068
+ console.log('Connection list:', activeList);
1069
+ console.log('Connection details:', connections);
1070
+ }
1371
1071
 
1372
- const initDatabase = async () => {
1072
+ // Đóng kết nối cụ thể
1073
+ static async closeSpecificConnection(dbKey: string): Promise<void> {
1373
1074
  try {
1374
- await db.connect('todos.db');
1375
-
1376
- await db.createTable('todos', {
1377
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1378
- title: 'TEXT NOT NULL',
1379
- completed: 'BOOLEAN DEFAULT 0',
1380
- priority: 'INTEGER DEFAULT 1',
1381
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1382
- });
1383
-
1384
- loadTodos();
1075
+ await DatabaseManager.closeConnection(dbKey);
1076
+ console.log(`Connection ${dbKey} closed`);
1385
1077
  } catch (error) {
1386
- console.error('Database setup error:', error);
1387
- } finally {
1388
- setIsLoading(false);
1078
+ console.error(`Error closing connection ${dbKey}:`, error);
1389
1079
  }
1390
- };
1080
+ }
1391
1081
 
1392
- const loadTodos = async () => {
1082
+ // Mở lại kết nối
1083
+ static async reopenConnection(dbKey: string): Promise<void> {
1393
1084
  try {
1394
- const result = await db.select('todos', null, []);
1395
- setTodos(result.rows);
1085
+ const dao = await DatabaseManager.getLazyLoading(dbKey);
1086
+ console.log(`Connection ${dbKey} reopened`);
1087
+ return dao;
1396
1088
  } catch (error) {
1397
- console.error('Load todos error:', error);
1089
+ console.error(`Error reopening connection ${dbKey}:`, error);
1090
+ throw error;
1398
1091
  }
1399
- };
1092
+ }
1400
1093
 
1401
- const addTodo = async () => {
1094
+ // Đảm bảo kết nối tồn tại
1095
+ static async ensureConnection(dbKey: string): Promise<void> {
1402
1096
  try {
1403
- await db.insert('todos', {
1404
- title: `Todo ${Date.now()}`,
1405
- priority: Math.floor(Math.random() * 3) + 1
1406
- });
1407
- loadTodos();
1097
+ const dao = await DatabaseManager.ensureDatabaseConnection(dbKey);
1098
+ console.log(`Connection ${dbKey} ensured`);
1099
+ return dao;
1408
1100
  } catch (error) {
1409
- console.error('Add todo error:', error);
1101
+ console.error(`Error ensuring connection ${dbKey}:`, error);
1102
+ throw error;
1410
1103
  }
1411
- };
1104
+ }
1412
1105
 
1413
- const toggleTodo = async (id: number, completed: boolean) => {
1106
+ // Thực hiện transaction cross-schema
1107
+ static async executeTransactionAcrossSchemas(
1108
+ schemas: string[],
1109
+ operations: (daos: Record<string, any>) => Promise<void>
1110
+ ): Promise<void> {
1414
1111
  try {
1415
- await db.update('todos',
1416
- { completed: completed ? 0 : 1 },
1417
- 'id = ?',
1418
- [id]
1419
- );
1420
- loadTodos();
1112
+ await DatabaseManager.executeCrossSchemaTransaction(schemas, operations);
1113
+ console.log('Cross-schema transaction completed successfully');
1421
1114
  } catch (error) {
1422
- console.error('Toggle todo error:', error);
1115
+ console.error('Cross-schema transaction failed:', error);
1116
+ throw error;
1423
1117
  }
1424
- };
1425
-
1426
- if (isLoading) {
1427
- return (
1428
- <View style={styles.container}>
1429
- <Text>Loading database...</Text>
1430
- </View>
1431
- );
1432
1118
  }
1433
1119
 
1434
- return (
1435
- <View style={styles.container}>
1436
- <Text style={styles.title}>Universal SQLite Todos</Text>
1437
- <Text>Environment: {db.getEnvironment()}</Text>
1438
-
1439
- <Button title="Add Todo" onPress={addTodo} />
1440
-
1441
- <FlatList
1442
- data={todos}
1443
- keyExtractor={item => item.id.toString()}
1444
- renderItem={({ item }) => (
1445
- <View style={styles.todoItem}>
1446
- <Text style={[styles.todoText, item.completed && styles.completed]}>
1447
- {item.title}
1448
- </Text>
1449
- <Button
1450
- title={item.completed ? "Undo" : "Done"}
1451
- onPress={() => toggleTodo(item.id, item.completed)}
1452
- />
1453
- </View>
1454
- )}
1455
- />
1456
-
1457
- <StatusBar style="auto" />
1458
- </View>
1459
- );
1460
- }
1461
-
1462
- const styles = StyleSheet.create({
1463
- container: {
1464
- flex: 1,
1465
- backgroundColor: '#fff',
1466
- alignItems: 'center',
1467
- justifyContent: 'center',
1468
- padding: 20,
1469
- },
1470
- title: {
1471
- fontSize: 24,
1472
- fontWeight: 'bold',
1473
- marginBottom: 20,
1474
- },
1475
- todoItem: {
1476
- flexDirection: 'row',
1477
- alignItems: 'center',
1478
- padding: 10,
1479
- borderBottomWidth: 1,
1480
- borderBottomColor: '#ccc',
1481
- },
1482
- todoText: {
1483
- flex: 1,
1484
- fontSize: 16,
1485
- },
1486
- completed: {
1487
- textDecorationLine: 'line-through',
1488
- color: '#888',
1489
- },
1490
- });
1491
- ```
1492
-
1493
- ### Next.js API Routes
1494
-
1495
- ```typescript
1496
- // pages/api/users/[id].ts
1497
- import { NextApiRequest, NextApiResponse } from 'next';
1498
- import UniversalSQLite from '@dqcai/sqlite';
1499
-
1500
- const db = new UniversalSQLite();
1501
- let isConnected = false;
1120
+ // Event listeners cho reconnection
1121
+ static setupReconnectionHandlers(): void {
1122
+ DatabaseManager.onDatabaseReconnect('users', (dao) => {
1123
+ console.log('Users database reconnected');
1124
+ // Re-initialize services nếu cần
1125
+ });
1502
1126
 
1503
- async function ensureConnection() {
1504
- if (!isConnected) {
1505
- await db.connect(process.env.DATABASE_PATH || './api_database.db');
1506
-
1507
- await db.createTable('users', {
1508
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1509
- name: 'TEXT NOT NULL',
1510
- email: 'TEXT UNIQUE NOT NULL',
1511
- avatar: 'TEXT',
1512
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
1513
- updated_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1127
+ DatabaseManager.onDatabaseReconnect('core', (dao) => {
1128
+ console.log('Core database reconnected');
1129
+ // Re-initialize settings
1514
1130
  });
1515
-
1516
- isConnected = true;
1517
1131
  }
1518
- }
1519
1132
 
1520
- export default async function handler(req: NextApiRequest, res: NextApiResponse) {
1521
- const { id } = req.query;
1522
-
1523
- try {
1524
- await ensureConnection();
1133
+ // Health check tất cả connections
1134
+ static async performHealthCheck(): Promise<void> {
1135
+ const connections = DatabaseManager.getConnections();
1525
1136
 
1526
- switch (req.method) {
1527
- case 'GET':
1528
- if (id === 'all') {
1529
- const users = await db.select('users');
1530
- res.status(200).json(users.rows);
1531
- } else {
1532
- const user = await db.select('users', 'id = ?', [Number(id)]);
1533
- if (user.rows.length === 0) {
1534
- res.status(404).json({ error: 'User not found' });
1535
- } else {
1536
- res.status(200).json(user.rows[0]);
1537
- }
1538
- }
1539
- break;
1540
-
1541
- case 'POST':
1542
- const { name, email, avatar } = req.body;
1543
- const newUser = await db.insert('users', { name, email, avatar });
1544
- res.status(201).json({
1545
- id: newUser.lastInsertRowId,
1546
- message: 'User created successfully'
1547
- });
1548
- break;
1549
-
1550
- case 'PUT':
1551
- const updateData = { ...req.body, updated_at: new Date().toISOString() };
1552
- const updateResult = await db.update('users', updateData, 'id = ?', [Number(id)]);
1553
-
1554
- if (updateResult.rowsAffected === 0) {
1555
- res.status(404).json({ error: 'User not found' });
1556
- } else {
1557
- res.status(200).json({ message: 'User updated successfully' });
1558
- }
1559
- break;
1560
-
1561
- case 'DELETE':
1562
- const deleteResult = await db.delete('users', 'id = ?', [Number(id)]);
1137
+ for (const [dbKey, dao] of Object.entries(connections)) {
1138
+ try {
1139
+ await dao.execute('SELECT 1');
1140
+ console.log(`${dbKey}: Healthy`);
1141
+ } catch (error) {
1142
+ console.error(`${dbKey}: Unhealthy -`, error);
1563
1143
 
1564
- if (deleteResult.rowsAffected === 0) {
1565
- res.status(404).json({ error: 'User not found' });
1566
- } else {
1567
- res.status(200).json({ message: 'User deleted successfully' });
1144
+ // Thử reconnect nếu cần
1145
+ try {
1146
+ await DatabaseManager.ensureDatabaseConnection(dbKey);
1147
+ console.log(`${dbKey}: Reconnected successfully`);
1148
+ } catch (reconnectError) {
1149
+ console.error(`${dbKey}: Failed to reconnect -`, reconnectError);
1568
1150
  }
1569
- break;
1570
-
1571
- default:
1572
- res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']);
1573
- res.status(405).end(`Method ${req.method} Not Allowed`);
1151
+ }
1574
1152
  }
1575
-
1576
- } catch (error) {
1577
- console.error('API Error:', error);
1578
- res.status(500).json({ error: 'Internal server error' });
1579
1153
  }
1580
1154
  }
1581
1155
  ```
1582
1156
 
1583
- ## 🧪 Testing
1157
+ ## 7. Import/Export Dữ liệu
1584
1158
 
1585
- ### Unit Tests với Jest
1159
+ ### Import từ CSV
1586
1160
 
1587
1161
  ```typescript
1588
- // __tests__/universal-sqlite.test.ts
1589
- import UniversalSQLite from '../src/index';
1162
+ // services/DataImportService.ts
1163
+ import { DatabaseManager, ImportResult, ColumnMapping } from '@dqcai/sqlite';
1590
1164
 
1591
- describe('UniversalSQLite', () => {
1592
- let db: UniversalSQLite;
1593
-
1594
- beforeEach(async () => {
1595
- db = new UniversalSQLite();
1596
- await db.connect(':memory:');
1597
- });
1598
-
1599
- afterEach(async () => {
1600
- await db.close();
1601
- });
1602
-
1603
- test('should create and query table', async () => {
1604
- await db.createTable('test_table', {
1605
- id: 'INTEGER PRIMARY KEY',
1606
- name: 'TEXT'
1607
- });
1608
-
1609
- await db.insert('test_table', { name: 'Test Item' });
1610
-
1611
- const result = await db.select('test_table');
1612
- expect(result.rows).toHaveLength(1);
1613
- expect(result.rows[0].name).toBe('Test Item');
1614
- });
1615
-
1616
- test('should handle parameters correctly', async () => {
1617
- await db.createTable('users', {
1618
- id: 'INTEGER PRIMARY KEY',
1619
- email: 'TEXT UNIQUE'
1620
- });
1621
-
1622
- await db.insert('users', { email: 'test@example.com' });
1623
-
1624
- const user = await db.select('users', 'email = ?', ['test@example.com']);
1625
- expect(user.rows).toHaveLength(1);
1626
-
1627
- const noUser = await db.select('users', 'email = ?', ['nonexistent@example.com']);
1628
- expect(noUser.rows).toHaveLength(0);
1629
- });
1165
+ export class DataImportService {
1630
1166
 
1631
- test('should handle SQL injection attempts', async () => {
1632
- await db.createTable('users', {
1633
- id: 'INTEGER PRIMARY KEY',
1634
- name: 'TEXT'
1635
- });
1636
-
1637
- await db.insert('users', { name: 'Regular User' });
1638
-
1639
- // Attempt SQL injection
1640
- const maliciousInput = "'; DROP TABLE users; --";
1641
- await db.insert('users', { name: maliciousInput });
1642
-
1643
- const users = await db.select('users');
1644
- expect(users.rows).toHaveLength(2); // Table should still exist
1645
- });
1646
- });
1647
- ```
1648
-
1649
- ## 🔐 Best Practices
1167
+ // Import users từ CSV
1168
+ static async importUsersFromCSV(csvData: string): Promise<ImportResult> {
1169
+ try {
1170
+ const result = await DatabaseManager.importFromCSV(
1171
+ 'users', // database key
1172
+ 'users', // table name
1173
+ csvData,
1174
+ {
1175
+ hasHeader: true,
1176
+ delimiter: ',',
1177
+ skipErrors: false,
1178
+ validateData: true,
1179
+ batchSize: 500,
1180
+ onProgress: (processed, total) => {
1181
+ console.log(`Import progress: ${processed}/${total}`);
1182
+ },
1183
+ onError: (error, rowIndex, rowData) => {
1184
+ console.error(`Row ${rowIndex} error:`, error, rowData);
1185
+ }
1186
+ }
1187
+ );
1650
1188
 
1651
- ### 1. Security
1189
+ console.log('Import completed:', {
1190
+ total: result.totalRows,
1191
+ success: result.successRows,
1192
+ errors: result.errorRows,
1193
+ time: result.executionTime
1194
+ });
1652
1195
 
1653
- ```typescript
1654
- class SecureDatabase {
1655
- private db: UniversalSQLite;
1656
-
1657
- constructor() {
1658
- this.db = new UniversalSQLite();
1659
- }
1660
-
1661
- async connect(path: string) {
1662
- await this.db.connect(path);
1663
-
1664
- // Enable foreign key constraints
1665
- await this.db.query('PRAGMA foreign_keys = ON');
1666
-
1667
- // Set secure defaults
1668
- await this.db.query('PRAGMA journal_mode = WAL'); // Write-Ahead Logging
1669
- await this.db.query('PRAGMA synchronous = NORMAL');
1670
- }
1671
-
1672
- // Validate input before queries
1673
- validateEmail(email: string): boolean {
1674
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1675
- return emailRegex.test(email);
1676
- }
1677
-
1678
- validateUsername(username: string): boolean {
1679
- return /^[a-zA-Z0-9_]{3,20}$/.test(username);
1680
- }
1681
-
1682
- async createUser(userData: {name: string, email: string, username: string}) {
1683
- // Validate input
1684
- if (!this.validateEmail(userData.email)) {
1685
- throw new Error('Invalid email format');
1686
- }
1687
-
1688
- if (!this.validateUsername(userData.username)) {
1689
- throw new Error('Invalid username format');
1196
+ return result;
1197
+ } catch (error) {
1198
+ console.error('Import failed:', error);
1199
+ throw error;
1690
1200
  }
1691
-
1692
- // Sanitize input
1693
- const sanitizedData = {
1694
- name: userData.name.trim(),
1695
- email: userData.email.toLowerCase().trim(),
1696
- username: userData.username.trim()
1697
- };
1698
-
1699
- return await this.db.insert('users', sanitizedData);
1700
1201
  }
1701
- }
1702
- ```
1703
-
1704
- ### 2. Performance Optimization
1705
1202
 
1706
- ```typescript
1707
- class OptimizedDatabase {
1708
- private db: UniversalSQLite;
1709
-
1710
- constructor() {
1711
- this.db = new UniversalSQLite();
1712
- }
1713
-
1714
- async connect(path: string) {
1715
- await this.db.connect(path);
1716
-
1717
- // Performance tuning
1718
- await this.db.query('PRAGMA cache_size = 10000');
1719
- await this.db.query('PRAGMA temp_store = memory');
1720
- await this.db.query('PRAGMA mmap_size = 268435456'); // 256MB
1721
- await this.db.query('PRAGMA optimize');
1722
- }
1723
-
1724
- async bulkInsert(table: string, records: Record<string, any>[]) {
1725
- await this.db.query('BEGIN TRANSACTION');
1726
-
1203
+ // Import với column mapping
1204
+ static async importUsersWithMapping(
1205
+ data: Record<string, any>[],
1206
+ columnMappings: ColumnMapping[]
1207
+ ): Promise<ImportResult> {
1727
1208
  try {
1728
- for (const record of records) {
1729
- await this.db.insert(table, record);
1730
- }
1731
- await this.db.query('COMMIT');
1209
+ const result = await DatabaseManager.importDataWithMapping(
1210
+ 'users',
1211
+ 'users',
1212
+ data,
1213
+ columnMappings,
1214
+ {
1215
+ batchSize: 1000,
1216
+ skipErrors: true,
1217
+ updateOnConflict: true,
1218
+ conflictColumns: ['email']
1219
+ }
1220
+ );
1221
+
1222
+ return result;
1732
1223
  } catch (error) {
1733
- await this.db.query('ROLLBACK');
1224
+ console.error('Import with mapping failed:', error);
1734
1225
  throw error;
1735
1226
  }
1736
1227
  }
1737
-
1738
- async createOptimizedIndexes(table: string, columns: string[]) {
1739
- for (const column of columns) {
1740
- await this.db.query(`
1741
- CREATE INDEX IF NOT EXISTS idx_${table}_${column}
1742
- ON ${table}(${column})
1743
- `);
1744
- }
1745
- }
1746
- }
1747
- ```
1748
-
1749
- ## 🚨 Troubleshooting
1750
-
1751
- ### Common Issues
1752
1228
 
1753
- #### 1. "No supported SQLite adapter found"
1754
- ```typescript
1755
- // Kiểm tra adapter availability
1756
- const manager = new SQLiteManager();
1757
- const adapters = [
1758
- 'NodeAdapter',
1759
- 'BrowserAdapter',
1760
- 'DenoAdapter',
1761
- 'BunAdapter',
1762
- 'ReactNativeAdapter'
1763
- ];
1764
-
1765
- for (const adapterName of adapters) {
1766
- try {
1767
- const adapter = new (require(`./adapters/${adapterName.toLowerCase()}`))();
1768
- console.log(`${adapterName}: ${adapter.isSupported() ? '✅' : '❌'}`);
1769
- } catch {
1770
- console.log(`${adapterName}: ❌ (not available)`);
1771
- }
1772
- }
1773
- ```
1774
-
1775
- #### 2. React Native Windows Setup
1776
-
1777
- ```typescript
1778
- // Kiểm tra Windows environment
1779
- async function checkReactNativeWindows() {
1780
- const db = new UniversalSQLite();
1781
-
1782
- console.log('Environment:', db.getEnvironment());
1783
-
1784
- if (db.getEnvironment() === 'React Native Windows') {
1785
- console.log('✅ React Native Windows detected');
1786
-
1229
+ // Bulk import nhiều bảng cùng lúc
1230
+ static async bulkImportData(importConfigs: Array<{
1231
+ databaseKey: string;
1232
+ tableName: string;
1233
+ data: Record<string, any>[];
1234
+ }>): Promise<void> {
1787
1235
  try {
1788
- await db.connect('windows_test.db');
1789
- console.log('✅ Database connection successful');
1236
+ const result = await DatabaseManager.bulkImport(importConfigs);
1790
1237
 
1791
- await db.createTable('test', { id: 'INTEGER', name: 'TEXT' });
1792
- console.log('✅ Table creation successful');
1793
-
1794
- } catch (error) {
1795
- console.error('❌ Windows SQLite error:', error);
1796
- console.log('💡 Try installing: npm install react-native-sqlite-2');
1797
- }
1798
- }
1799
- }
1800
- ```
1801
-
1802
- #### 3. Browser CORS Issues
1803
-
1804
- ```typescript
1805
- // browser-cors-fix.js
1806
- async function setupBrowserDatabase() {
1807
- const db = new UniversalSQLite();
1808
-
1809
- try {
1810
- // In-memory database (không cần file access)
1811
- await db.connect(':memory:');
1812
- console.log('✅ Browser in-memory database ready');
1813
-
1814
- } catch (error) {
1815
- console.error('❌ Browser setup failed:', error);
1816
- console.log('💡 Fallback: Using in-memory database');
1817
-
1818
- // Fallback strategy
1819
- await db.connect(':memory:');
1820
- }
1821
- }
1822
- ```
1823
-
1824
- ## 📊 Performance Benchmarks
1825
-
1826
- ```typescript
1827
- async function benchmarkExample() {
1828
- const db = new UniversalSQLite();
1829
- await db.connect('benchmark.db');
1830
-
1831
- await db.createTable('benchmark_table', {
1832
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1833
- data: 'TEXT',
1834
- timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1835
- });
1836
-
1837
- // Benchmark bulk insert
1838
- console.log('Starting bulk insert benchmark...');
1839
- const insertStart = performance.now();
1840
-
1841
- await db.query('BEGIN TRANSACTION');
1842
- for (let i = 0; i < 10000; i++) {
1843
- await db.insert('benchmark_table', {
1844
- data: `Sample data ${i}`
1845
- });
1846
- }
1847
- await db.query('COMMIT');
1848
-
1849
- const insertEnd = performance.now();
1850
- console.log(`Bulk insert (10k records): ${(insertEnd - insertStart).toFixed(2)}ms`);
1851
-
1852
- // Benchmark select
1853
- const selectStart = performance.now();
1854
- const allRecords = await db.select('benchmark_table');
1855
- const selectEnd = performance.now();
1856
-
1857
- console.log(`Select all (${allRecords.rows.length} records): ${(selectEnd - selectStart).toFixed(2)}ms`);
1858
-
1859
- // Benchmark with index
1860
- await db.query('CREATE INDEX idx_data ON benchmark_table(data)');
1861
-
1862
- const indexedSelectStart = performance.now();
1863
- const searchResult = await db.select('benchmark_table', 'data LIKE ?', ['Sample data 50%']);
1864
- const indexedSelectEnd = performance.now();
1865
-
1866
- console.log(`Indexed search (${searchResult.rows.length} results): ${(indexedSelectEnd - indexedSelectStart).toFixed(2)}ms`);
1867
-
1868
- await db.close();
1869
- }
1870
-
1871
- benchmarkExample();
1872
- ```
1873
-
1874
- ## 📚 Advanced Use Cases
1875
-
1876
- ### 1. Multi-Database Application
1877
-
1878
- ```typescript
1879
- class MultiDatabaseApp {
1880
- private userDb: UniversalSQLite;
1881
- private logDb: UniversalSQLite;
1882
- private cacheDb: UniversalSQLite;
1883
-
1884
- constructor() {
1885
- this.userDb = new UniversalSQLite();
1886
- this.logDb = new UniversalSQLite();
1887
- this.cacheDb = new UniversalSQLite();
1888
- }
1889
-
1890
- async initialize() {
1891
- // Separate databases for different concerns
1892
- await this.userDb.connect('users.db');
1893
- await this.logDb.connect('logs.db');
1894
- await this.cacheDb.connect(':memory:'); // Cache in memory
1895
-
1896
- // Setup schemas
1897
- await this.setupUserDatabase();
1898
- await this.setupLogDatabase();
1899
- await this.setupCacheDatabase();
1900
- }
1901
-
1902
- private async setupUserDatabase() {
1903
- await this.userDb.createTable('users', {
1904
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1905
- username: 'TEXT UNIQUE NOT NULL',
1906
- email: 'TEXT UNIQUE NOT NULL',
1907
- profile_data: 'TEXT', // JSON
1908
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1909
- });
1910
-
1911
- await this.userDb.createTable('user_sessions', {
1912
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1913
- user_id: 'INTEGER NOT NULL',
1914
- session_token: 'TEXT UNIQUE NOT NULL',
1915
- expires_at: 'DATETIME NOT NULL',
1916
- 'FOREIGN KEY (user_id)': 'REFERENCES users(id) ON DELETE CASCADE'
1917
- });
1918
- }
1919
-
1920
- private async setupLogDatabase() {
1921
- await this.logDb.createTable('access_logs', {
1922
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1923
- user_id: 'INTEGER',
1924
- action: 'TEXT NOT NULL',
1925
- ip_address: 'TEXT',
1926
- user_agent: 'TEXT',
1927
- timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1928
- });
1929
-
1930
- await this.logDb.createTable('error_logs', {
1931
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1932
- error_type: 'TEXT NOT NULL',
1933
- error_message: 'TEXT NOT NULL',
1934
- stack_trace: 'TEXT',
1935
- context: 'TEXT', // JSON
1936
- timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1937
- });
1938
- }
1939
-
1940
- private async setupCacheDatabase() {
1941
- await this.cacheDb.createTable('cache_entries', {
1942
- key: 'TEXT PRIMARY KEY',
1943
- value: 'TEXT NOT NULL',
1944
- expires_at: 'DATETIME NOT NULL'
1945
- });
1946
- }
1947
-
1948
- // User operations
1949
- async createUser(userData: any) {
1950
- const result = await this.userDb.insert('users', userData);
1951
-
1952
- // Log the action
1953
- await this.logAction(result.lastInsertRowId!, 'USER_CREATED', 'User registration');
1954
-
1955
- return result;
1956
- }
1957
-
1958
- async getUserById(id: number) {
1959
- // Check cache first
1960
- const cached = await this.getFromCache(`user:${id}`);
1961
- if (cached) {
1962
- return JSON.parse(cached);
1963
- }
1964
-
1965
- // Query from database
1966
- const result = await this.userDb.select('users', 'id = ?', [id]);
1967
- if (result.rows.length > 0) {
1968
- const user = result.rows[0];
1969
- // Cache for 1 hour
1970
- await this.setCache(`user:${id}`, JSON.stringify(user), 3600);
1971
- return user;
1972
- }
1973
-
1974
- return null;
1975
- }
1976
-
1977
- // Logging operations
1978
- async logAction(userId: number, action: string, details: string) {
1979
- await this.logDb.insert('access_logs', {
1980
- user_id: userId,
1981
- action: action,
1982
- ip_address: '127.0.0.1', // Would get from request
1983
- user_agent: 'Universal SQLite App'
1984
- });
1985
- }
1986
-
1987
- async logError(error: Error, context?: any) {
1988
- await this.logDb.insert('error_logs', {
1989
- error_type: error.name,
1990
- error_message: error.message,
1991
- stack_trace: error.stack,
1992
- context: context ? JSON.stringify(context) : null
1993
- });
1994
- }
1995
-
1996
- // Cache operations
1997
- async setCache(key: string, value: string, ttlSeconds: number) {
1998
- const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
1999
-
2000
- await this.cacheDb.query(`
2001
- INSERT OR REPLACE INTO cache_entries (key, value, expires_at)
2002
- VALUES (?, ?, ?)
2003
- `, [key, value, expiresAt]);
2004
- }
2005
-
2006
- async getFromCache(key: string): Promise<string | null> {
2007
- // Clean expired entries
2008
- await this.cacheDb.delete('cache_entries', 'expires_at < ?', [new Date().toISOString()]);
2009
-
2010
- const result = await this.cacheDb.select('cache_entries', 'key = ?', [key]);
2011
- return result.rows.length > 0 ? result.rows[0].value : null;
2012
- }
2013
-
2014
- // Analytics across databases
2015
- async getUserAnalytics(userId: number) {
2016
- const user = await this.getUserById(userId);
2017
- if (!user) return null;
2018
-
2019
- const logs = await this.logDb.query(`
2020
- SELECT
2021
- action,
2022
- COUNT(*) as count,
2023
- MAX(timestamp) as last_action
2024
- FROM access_logs
2025
- WHERE user_id = ?
2026
- GROUP BY action
2027
- ORDER BY count DESC
2028
- `, [userId]);
2029
-
2030
- return {
2031
- user: user,
2032
- activity: logs.rows,
2033
- total_actions: logs.rows.reduce((sum, row) => sum + row.count, 0)
2034
- };
2035
- }
2036
-
2037
- async cleanup() {
2038
- await this.userDb.close();
2039
- await this.logDb.close();
2040
- await this.cacheDb.close();
1238
+ console.log('Bulk import completed:', {
1239
+ totalDatabases: result.totalDatabases,
1240
+ successDatabases: result.successDatabases,
1241
+ executionTime: result.executionTime
1242
+ });
1243
+
1244
+ // Log chi tiết từng bảng
1245
+ Object.entries(result.results).forEach(([key, importResult]) => {
1246
+ console.log(`${key}: ${importResult.successRows}/${importResult.totalRows} rows imported`);
1247
+ });
1248
+
1249
+ // Log lỗi nếu
1250
+ if (Object.keys(result.errors).length > 0) {
1251
+ console.error('Import errors:', result.errors);
1252
+ }
1253
+
1254
+ } catch (error) {
1255
+ console.error('Bulk import failed:', error);
1256
+ throw error;
1257
+ }
2041
1258
  }
2042
1259
  }
2043
1260
 
2044
- // Usage
2045
- async function multiDbExample() {
2046
- const app = new MultiDatabaseApp();
2047
-
1261
+ // Ví dụ sử dụng import service
1262
+ async function exampleImportUsage() {
1263
+ // CSV data mẫu
1264
+ const csvData = `username,email,password,first_name,last_name
1265
+ john_doe,john@example.com,password123,John,Doe
1266
+ jane_smith,jane@example.com,password456,Jane,Smith
1267
+ bob_wilson,bob@example.com,password789,Bob,Wilson`;
1268
+
2048
1269
  try {
2049
- await app.initialize();
2050
-
2051
- // Create user
2052
- const newUser = await app.createUser({
2053
- username: 'johndoe',
2054
- email: 'john@example.com',
2055
- profile_data: JSON.stringify({ bio: 'Software developer' })
2056
- });
2057
-
2058
- const userId = newUser.lastInsertRowId!;
2059
-
2060
- // Simulate user activity
2061
- await app.logAction(userId, 'LOGIN', 'User logged in');
2062
- await app.logAction(userId, 'VIEW_PROFILE', 'Viewed profile page');
2063
- await app.logAction(userId, 'UPDATE_PROFILE', 'Updated profile');
2064
-
2065
- // Get analytics
2066
- const analytics = await app.getUserAnalytics(userId);
2067
- console.log('User Analytics:', analytics);
2068
-
1270
+ // Import từ CSV
1271
+ const importResult = await DataImportService.importUsersFromCSV(csvData);
1272
+ console.log('CSV Import result:', importResult);
1273
+
1274
+ // Import với column mapping
1275
+ const mappedData = [
1276
+ { user_name: 'alice', user_email: 'alice@test.com', pwd: 'pass123' },
1277
+ { user_name: 'charlie', user_email: 'charlie@test.com', pwd: 'pass456' }
1278
+ ];
1279
+
1280
+ const columnMappings: ColumnMapping[] = [
1281
+ { sourceColumn: 'user_name', targetColumn: 'username' },
1282
+ { sourceColumn: 'user_email', targetColumn: 'email' },
1283
+ {
1284
+ sourceColumn: 'pwd',
1285
+ targetColumn: 'password',
1286
+ transform: (value) => `hashed_${value}` // Hash password
1287
+ }
1288
+ ];
1289
+
1290
+ const mappingResult = await DataImportService.importUsersWithMapping(
1291
+ mappedData,
1292
+ columnMappings
1293
+ );
1294
+ console.log('Mapping Import result:', mappingResult);
1295
+
2069
1296
  } catch (error) {
2070
- console.error('Multi-db error:', error);
2071
- } finally {
2072
- await app.cleanup();
1297
+ console.error('Import example failed:', error);
2073
1298
  }
2074
1299
  }
2075
-
2076
- multiDbExample();
2077
1300
  ```
2078
1301
 
2079
- ### 2. Real-time Sync với WebSocket
1302
+ ### Export dữ liệu
2080
1303
 
2081
1304
  ```typescript
2082
- // realtime-sync.ts
2083
- import UniversalSQLite, { QueryBuilder } from '@dqcai/sqlite';
2084
- import { WebSocket } from 'ws'; // Node.js example
2085
-
2086
- class RealtimeSync {
2087
- private db: UniversalSQLite;
2088
- private ws: WebSocket | null = null;
2089
- private syncQueue: any[] = [];
1305
+ // services/DataExportService.ts
1306
+ import { DatabaseManager } from '@dqcai/sqlite';
1307
+
1308
+ export class DataExportService {
2090
1309
 
2091
- constructor() {
2092
- this.db = new UniversalSQLite();
1310
+ // Export users ra CSV
1311
+ static async exportUsersToCSV(): Promise<string> {
1312
+ try {
1313
+ const dao = DatabaseManager.get('users');
1314
+ const sql = `
1315
+ SELECT u.username, u.email, u.created_at,
1316
+ p.first_name, p.last_name, p.phone
1317
+ FROM users u
1318
+ LEFT JOIN profiles p ON u.id = p.user_id
1319
+ ORDER BY u.created_at DESC
1320
+ `;
1321
+
1322
+ const result = await dao.execute(sql);
1323
+
1324
+ if (result.rows.length === 0) {
1325
+ return 'No data to export';
1326
+ }
1327
+
1328
+ // Tạo CSV header
1329
+ const headers = Object.keys(result.rows[0]);
1330
+ let csvContent = headers.join(',') + '\n';
1331
+
1332
+ // Thêm data rows
1333
+ result.rows.forEach(row => {
1334
+ const values = headers.map(header => {
1335
+ const value = row[header];
1336
+ // Escape quotes và wrap trong quotes nếu chứa comma
1337
+ if (value === null || value === undefined) {
1338
+ return '';
1339
+ }
1340
+ const stringValue = String(value);
1341
+ if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
1342
+ return `"${stringValue.replace(/"/g, '""')}"`;
1343
+ }
1344
+ return stringValue;
1345
+ });
1346
+ csvContent += values.join(',') + '\n';
1347
+ });
1348
+
1349
+ return csvContent;
1350
+ } catch (error) {
1351
+ console.error('Export to CSV failed:', error);
1352
+ throw error;
1353
+ }
2093
1354
  }
2094
-
2095
- async initialize() {
2096
- await this.db.connect('realtime.db');
2097
-
2098
- // Setup sync tables
2099
- await this.db.createTable('messages', {
2100
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2101
- content: 'TEXT NOT NULL',
2102
- sender_id: 'INTEGER NOT NULL',
2103
- room_id: 'INTEGER NOT NULL',
2104
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
2105
- synced: 'BOOLEAN DEFAULT 0'
2106
- });
2107
-
2108
- await this.db.createTable('sync_log', {
2109
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2110
- operation: 'TEXT NOT NULL',
2111
- table_name: 'TEXT NOT NULL',
2112
- record_id: 'INTEGER',
2113
- data: 'TEXT',
2114
- sync_status: 'TEXT DEFAULT "pending"',
2115
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
2116
- });
2117
-
2118
- this.startSyncProcess();
1355
+
1356
+ // Export với điều kiện tùy chỉnh
1357
+ static async exportUsersWithConditions(
1358
+ whereClause?: string,
1359
+ params?: any[]
1360
+ ): Promise<Record<string, any>[]> {
1361
+ try {
1362
+ const dao = DatabaseManager.get('users');
1363
+ let sql = `
1364
+ SELECT u.*, p.first_name, p.last_name, p.phone, p.address
1365
+ FROM users u
1366
+ LEFT JOIN profiles p ON u.id = p.user_id
1367
+ `;
1368
+
1369
+ if (whereClause) {
1370
+ sql += ` WHERE ${whereClause}`;
1371
+ }
1372
+
1373
+ sql += ` ORDER BY u.created_at DESC`;
1374
+
1375
+ const result = await dao.execute(sql, params || []);
1376
+ return result.rows;
1377
+ } catch (error) {
1378
+ console.error('Export with conditions failed:', error);
1379
+ throw error;
1380
+ }
2119
1381
  }
2120
-
2121
- async addMessage(content: string, senderId: number, roomId: number) {
2122
- const result = await this.db.insert('messages', {
2123
- content,
2124
- sender_id: senderId,
2125
- room_id: roomId
2126
- });
2127
-
2128
- // Queue for sync
2129
- await this.queueSync('INSERT', 'messages', result.lastInsertRowId!, {
2130
- id: result.lastInsertRowId,
2131
- content,
2132
- sender_id: senderId,
2133
- room_id: roomId
2134
- });
2135
-
2136
- return result;
1382
+
1383
+ // Export dữ liệu backup toàn bộ database
1384
+ static async createDatabaseBackup(dbKey: string): Promise<{
1385
+ tables: Record<string, any[]>;
1386
+ metadata: any;
1387
+ }> {
1388
+ try {
1389
+ const dao = DatabaseManager.get(dbKey);
1390
+
1391
+ // Lấy danh sách tables
1392
+ const tablesResult = await dao.execute(
1393
+ "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
1394
+ );
1395
+
1396
+ const backup: Record<string, any[]> = {};
1397
+
1398
+ // Export từng table
1399
+ for (const tableRow of tablesResult.rows) {
1400
+ const tableName = tableRow.name;
1401
+ const dataResult = await dao.execute(`SELECT * FROM ${tableName}`);
1402
+ backup[tableName] = dataResult.rows;
1403
+ }
1404
+
1405
+ // Thêm metadata
1406
+ const dbInfo = await dao.getDatabaseInfo();
1407
+
1408
+ return {
1409
+ tables: backup,
1410
+ metadata: {
1411
+ ...dbInfo,
1412
+ exportDate: new Date().toISOString(),
1413
+ version: await dao.getSchemaVersion()
1414
+ }
1415
+ };
1416
+ } catch (error) {
1417
+ console.error('Database backup failed:', error);
1418
+ throw error;
1419
+ }
2137
1420
  }
2138
-
2139
- async getMessages(roomId: number, limit: number = 50) {
2140
- return await this.db.query(`
2141
- SELECT m.*, u.username
2142
- FROM messages m
2143
- LEFT JOIN users u ON m.sender_id = u.id
2144
- WHERE m.room_id = ?
2145
- ORDER BY m.created_at DESC
2146
- LIMIT ?
2147
- `, [roomId, limit]);
1421
+ }
1422
+
1423
+ // dụ sử dụng export
1424
+ async function exampleExportUsage() {
1425
+ try {
1426
+ // Export users ra CSV
1427
+ const csvContent = await DataExportService.exportUsersToCSV();
1428
+ console.log('CSV Export:', csvContent);
1429
+
1430
+ // Export users với điều kiện
1431
+ const recentUsers = await DataExportService.exportUsersWithConditions(
1432
+ "u.created_at > ?",
1433
+ [new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()] // 30 ngày gần đây
1434
+ );
1435
+ console.log('Recent users:', recentUsers);
1436
+
1437
+ // Backup toàn bộ database users
1438
+ const backup = await DataExportService.createDatabaseBackup('users');
1439
+ console.log('Database backup:', backup);
1440
+
1441
+ } catch (error) {
1442
+ console.error('Export example failed:', error);
2148
1443
  }
2149
-
2150
- private async queueSync(operation: string, tableName: string, recordId: number, data: any) {
2151
- await this.db.insert('sync_log', {
2152
- operation,
2153
- table_name: tableName,
2154
- record_id: recordId,
2155
- data: JSON.stringify(data)
1444
+ }
1445
+ ```
1446
+
1447
+ ## 8. Best Practices & Tips
1448
+
1449
+ ### Error Handling
1450
+
1451
+ ```typescript
1452
+ // utils/ErrorHandler.ts
1453
+ export class DatabaseErrorHandler {
1454
+
1455
+ static handleServiceError(error: any, context: string): void {
1456
+ console.error(`Database Error in ${context}:`, {
1457
+ message: error.message,
1458
+ stack: error.stack,
1459
+ code: error.code,
1460
+ timestamp: new Date().toISOString()
2156
1461
  });
1462
+
1463
+ // Specific error handling
1464
+ if (error.message?.includes('UNIQUE constraint failed')) {
1465
+ throw new Error('Duplicate entry detected');
1466
+ } else if (error.message?.includes('database is locked')) {
1467
+ throw new Error('Database is busy, please try again');
1468
+ } else if (error.message?.includes('no such table')) {
1469
+ throw new Error('Database table not found');
1470
+ } else {
1471
+ throw new Error(`Database operation failed: ${error.message}`);
1472
+ }
2157
1473
  }
2158
-
2159
- private async startSyncProcess() {
2160
- setInterval(async () => {
2161
- await this.processSyncQueue();
2162
- }, 5000); // Sync every 5 seconds
2163
- }
2164
-
2165
- private async processSyncQueue() {
2166
- const pendingSyncs = await this.db.select('sync_log', 'sync_status = ?', ['pending']);
1474
+
1475
+ static async withRetry<T>(
1476
+ operation: () => Promise<T>,
1477
+ maxRetries: number = 3,
1478
+ delay: number = 1000
1479
+ ): Promise<T> {
1480
+ let lastError: any;
2167
1481
 
2168
- for (const sync of pendingSyncs.rows) {
1482
+ for (let i = 0; i < maxRetries; i++) {
2169
1483
  try {
2170
- // Simulate API call
2171
- const response = await this.syncToServer(sync);
1484
+ return await operation();
1485
+ } catch (error) {
1486
+ lastError = error;
2172
1487
 
2173
- if (response.success) {
2174
- await this.db.update('sync_log',
2175
- { sync_status: 'completed' },
2176
- 'id = ?',
2177
- [sync.id]
2178
- );
2179
-
2180
- // Mark original record as synced
2181
- if (sync.table_name === 'messages') {
2182
- await this.db.update('messages',
2183
- { synced: 1 },
2184
- 'id = ?',
2185
- [sync.record_id]
2186
- );
2187
- }
1488
+ if (i < maxRetries - 1) {
1489
+ console.log(`Operation failed, retrying in ${delay}ms... (${i + 1}/${maxRetries})`);
1490
+ await new Promise(resolve => setTimeout(resolve, delay));
1491
+ delay *= 2; // Exponential backoff
2188
1492
  }
2189
- } catch (error) {
2190
- await this.db.update('sync_log',
2191
- { sync_status: 'failed' },
2192
- 'id = ?',
2193
- [sync.id]
2194
- );
2195
- console.error('Sync failed:', error);
2196
1493
  }
2197
1494
  }
2198
- }
2199
-
2200
- private async syncToServer(syncData: any): Promise<{success: boolean}> {
2201
- // Simulate server sync
2202
- return new Promise((resolve) => {
2203
- setTimeout(() => {
2204
- resolve({ success: Math.random() > 0.1 }); // 90% success rate
2205
- }, 100);
2206
- });
1495
+
1496
+ throw lastError;
2207
1497
  }
2208
1498
  }
2209
1499
 
2210
- // Usage
2211
- async function realtimeSyncExample() {
2212
- const sync = new RealtimeSync();
2213
- await sync.initialize();
2214
-
2215
- // Simulate adding messages
2216
- await sync.addMessage('Hello World!', 1, 1);
2217
- await sync.addMessage('How are you?', 2, 1);
2218
-
2219
- // Get messages
2220
- const messages = await sync.getMessages(1);
2221
- console.log('Room messages:', messages.rows);
2222
-
2223
- // Let sync process run
2224
- await new Promise(resolve => setTimeout(resolve, 6000));
1500
+ // Sử dụng error handler
1501
+ class SafeUserService extends UserService {
1502
+ async createUser(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<User | null> {
1503
+ return DatabaseErrorHandler.withRetry(async () => {
1504
+ try {
1505
+ return await super.createUser(userData);
1506
+ } catch (error) {
1507
+ DatabaseErrorHandler.handleServiceError(error, 'createUser');
1508
+ throw error; // Re-throw sau khi handle
1509
+ }
1510
+ });
1511
+ }
2225
1512
  }
2226
-
2227
- realtimeSyncExample();
2228
1513
  ```
2229
1514
 
2230
- ### 3. Database Backup và Restore
1515
+ ### Performance Optimization
2231
1516
 
2232
1517
  ```typescript
2233
- class DatabaseBackup {
2234
- private db: UniversalSQLite;
2235
-
2236
- constructor() {
2237
- this.db = new UniversalSQLite();
2238
- }
2239
-
2240
- async connect(path: string) {
2241
- await this.db.connect(path);
2242
- }
2243
-
2244
- async createBackup(): Promise<string> {
2245
- // Get all table schemas
2246
- const tables = await this.db.query(`
2247
- SELECT name, sql FROM sqlite_master
2248
- WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
2249
- `);
2250
-
2251
- let backupSQL = '';
2252
- backupSQL += '-- Database Backup\n';
2253
- backupSQL += `-- Generated: ${new Date().toISOString()}\n\n`;
2254
-
2255
- // Add schema
2256
- for (const table of tables.rows) {
2257
- backupSQL += `${table.sql};\n\n`;
2258
- }
2259
-
2260
- // Add data
2261
- for (const table of tables.rows) {
2262
- const data = await this.db.select(table.name);
1518
+ // utils/PerformanceOptimizer.ts
1519
+ export class PerformanceOptimizer {
1520
+
1521
+ // Batch operations để giảm số lần gọi database
1522
+ static async batchCreateUsers(
1523
+ userService: UserService,
1524
+ users: Array<Omit<User, 'id' | 'created_at' | 'updated_at'>>
1525
+ ): Promise<void> {
1526
+ const batchSize = 100;
1527
+
1528
+ for (let i = 0; i < users.length; i += batchSize) {
1529
+ const batch = users.slice(i, i + batchSize);
2263
1530
 
2264
- if (data.rows.length > 0) {
2265
- // Get column names
2266
- const columns = Object.keys(data.rows[0]);
2267
-
2268
- backupSQL += `-- Data for ${table.name}\n`;
2269
- for (const row of data.rows) {
2270
- const values = columns.map(col => {
2271
- const value = row[col];
2272
- if (value === null) return 'NULL';
2273
- if (typeof value === 'string') return `'${value.replace(/'/g, "''")}'`;
2274
- return String(value);
1531
+ await userService.executeTransaction(async () => {
1532
+ for (const userData of batch) {
1533
+ await userService.create({
1534
+ ...userData,
1535
+ created_at: new Date().toISOString()
2275
1536
  });
2276
-
2277
- backupSQL += `INSERT INTO ${table.name} (${columns.join(', ')}) VALUES (${values.join(', ')});\n`;
2278
1537
  }
2279
- backupSQL += '\n';
2280
- }
1538
+ });
1539
+
1540
+ console.log(`Processed batch ${Math.ceil((i + batchSize) / batchSize)}`);
2281
1541
  }
2282
-
2283
- return backupSQL;
2284
1542
  }
2285
-
2286
- async restoreFromBackup(backupSQL: string): Promise<void> {
2287
- const statements = backupSQL
2288
- .split(';')
2289
- .map(stmt => stmt.trim())
2290
- .filter(stmt => stmt && !stmt.startsWith('--'));
1543
+
1544
+ // Index optimization
1545
+ static async optimizeQueries(dao: any): Promise<void> {
1546
+ // Enable query optimization
1547
+ await dao.execute('PRAGMA optimize');
2291
1548
 
2292
- await this.db.query('BEGIN TRANSACTION');
1549
+ // Analyze tables for better query planning
1550
+ await dao.execute('ANALYZE');
2293
1551
 
2294
- try {
2295
- for (const statement of statements) {
2296
- if (statement) {
2297
- await this.db.query(statement);
2298
- }
2299
- }
2300
- await this.db.query('COMMIT');
2301
- console.log('Backup restored successfully');
2302
- } catch (error) {
2303
- await this.db.query('ROLLBACK');
2304
- throw new Error(`Restore failed: ${error}`);
2305
- }
1552
+ // Set optimal cache size
1553
+ await dao.execute('PRAGMA cache_size = 10000');
1554
+
1555
+ console.log('Database optimization completed');
2306
1556
  }
2307
-
2308
- async scheduleBackups(intervalMinutes: number = 60) {
2309
- setInterval(async () => {
2310
- try {
2311
- const backup = await this.createBackup();
2312
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
2313
- const filename = `backup_${timestamp}.sql`;
2314
-
2315
- // In Node.js environment, save to file
2316
- if (typeof require !== 'undefined' && this.db.getEnvironment() === 'Node.js') {
2317
- const fs = require('fs').promises;
2318
- await fs.writeFile(filename, backup);
2319
- console.log(`Backup saved: ${filename}`);
2320
- } else {
2321
- // In browser/other environments, log or send to server
2322
- console.log('Backup created (implement save logic for your environment)');
2323
- }
2324
-
2325
- } catch (error) {
2326
- console.error('Backup failed:', error);
1557
+
1558
+ // Connection pooling check
1559
+ static monitorConnections(): void {
1560
+ setInterval(() => {
1561
+ const count = DatabaseManager.getConnectionCount();
1562
+ const connections = DatabaseManager.listConnections();
1563
+
1564
+ console.log(`Active connections: ${count}`, connections);
1565
+
1566
+ if (count > 5) {
1567
+ console.warn('High number of database connections detected');
2327
1568
  }
2328
- }, intervalMinutes * 60 * 1000);
2329
- }
2330
-
2331
- async close() {
2332
- await this.db.close();
1569
+ }, 30000); // Check every 30 seconds
2333
1570
  }
2334
1571
  }
2335
-
2336
- // Usage
2337
- async function backupExample() {
2338
- const backup = new DatabaseBackup();
2339
- await backup.connect('main_app.db');
2340
-
2341
- // Create some test data
2342
- const db = new UniversalSQLite();
2343
- await db.connect('main_app.db');
2344
-
2345
- await db.createTable('posts', {
2346
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2347
- title: 'TEXT NOT NULL',
2348
- content: 'TEXT',
2349
- author_id: 'INTEGER'
2350
- });
2351
-
2352
- await db.insert('posts', { title: 'First Post', content: 'Hello World!', author_id: 1 });
2353
- await db.insert('posts', { title: 'Second Post', content: 'More content', author_id: 2 });
2354
-
2355
- // Create backup
2356
- const backupSQL = await backup.createBackup();
2357
- console.log('Backup created, size:', backupSQL.length, 'characters');
2358
-
2359
- // Test restore (to new database)
2360
- const restoreDb = new UniversalSQLite();
2361
- await restoreDb.connect(':memory:');
2362
-
2363
- const restoreBackup = new DatabaseBackup();
2364
- await restoreBackup.connect(':memory:');
2365
- await restoreBackup.restoreFromBackup(backupSQL);
2366
-
2367
- // Verify restore
2368
- const restoredData = await restoreDb.select('posts');
2369
- console.log('Restored data:', restoredData.rows);
2370
-
2371
- await db.close();
2372
- await restoreDb.close();
2373
- await backup.close();
2374
- await restoreBackup.close();
2375
- }
2376
-
2377
- backupExample();
2378
1572
  ```
2379
1573
 
2380
- ## 🌐 Production Deployment
2381
-
2382
- ### Environment Variables
1574
+ ### Testing Utilities
2383
1575
 
2384
1576
  ```typescript
2385
- // config.ts
2386
- interface DatabaseConfig {
2387
- path: string;
2388
- backupInterval?: number;
2389
- maxConnections?: number;
2390
- enableWAL?: boolean;
2391
- }
2392
-
2393
- function getDatabaseConfig(): DatabaseConfig {
2394
- const env = process.env.NODE_ENV || 'development';
2395
-
2396
- const configs: Record<string, DatabaseConfig> = {
2397
- development: {
2398
- path: './dev_database.db',
2399
- backupInterval: 60, // 1 hour
2400
- enableWAL: true
2401
- },
2402
- test: {
2403
- path: ':memory:',
2404
- enableWAL: false
2405
- },
2406
- production: {
2407
- path: process.env.DATABASE_PATH || './prod_database.db',
2408
- backupInterval: 15, // 15 minutes
2409
- maxConnections: 10,
2410
- enableWAL: true
2411
- }
2412
- };
2413
-
2414
- return configs[env] || configs.development;
2415
- }
1577
+ // utils/TestHelpers.ts
1578
+ import { DatabaseManager } from '@dqcai/sqlite';
2416
1579
 
2417
- // app.ts
2418
- async function productionSetup() {
2419
- const config = getDatabaseConfig();
2420
- const db = new UniversalSQLite();
1580
+ export class DatabaseTestHelpers {
2421
1581
 
2422
- await db.connect(config.path);
2423
-
2424
- if (config.enableWAL) {
2425
- await db.query('PRAGMA journal_mode = WAL');
2426
- await db.query('PRAGMA synchronous = NORMAL');
2427
- await db.query('PRAGMA cache_size = 10000');
2428
- await db.query('PRAGMA temp_store = memory');
1582
+ static async setupTestDatabase(): Promise<void> {
1583
+ // Use in-memory database for testing
1584
+ const testSchema = {
1585
+ ...userSchema,
1586
+ database_name: ':memory:'
1587
+ };
1588
+
1589
+ DatabaseManager.registerSchema('test_users', testSchema);
1590
+ await DatabaseManager.initLazySchema(['test_users']);
2429
1591
  }
2430
-
2431
- // Setup production tables
2432
- await setupProductionSchema(db);
2433
-
2434
- // Setup monitoring
2435
- if (process.env.NODE_ENV === 'production') {
2436
- setupProductionMonitoring(db);
1592
+
1593
+ static async cleanupTestData(service: any): Promise<void> {
1594
+ await service.truncate();
2437
1595
  }
2438
-
2439
- return db;
2440
- }
2441
1596
 
2442
- async function setupProductionSchema(db: UniversalSQLite) {
2443
- // Users table với full audit trail
2444
- await db.createTable('users', {
2445
- id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2446
- email: 'TEXT UNIQUE NOT NULL',
2447
- password_hash: 'TEXT NOT NULL',
2448
- status: 'TEXT DEFAULT "active" CHECK(status IN ("active", "inactive", "suspended"))',
2449
- created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
2450
- updated_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
2451
- last_login: 'DATETIME'
2452
- });
2453
-
2454
- // Indexes for performance
2455
- await db.query('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
2456
- await db.query('CREATE INDEX IF NOT EXISTS idx_users_status ON users(status)');
2457
- await db.query('CREATE INDEX IF NOT EXISTS idx_users_last_login ON users(last_login)');
2458
-
2459
- // Trigger for updated_at
2460
- await db.query(`
2461
- CREATE TRIGGER IF NOT EXISTS update_users_timestamp
2462
- AFTER UPDATE ON users
2463
- FOR EACH ROW
2464
- BEGIN
2465
- UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
2466
- END
2467
- `);
2468
- }
1597
+ static async seedTestData(userService: UserService): Promise<User[]> {
1598
+ const testUsers = [
1599
+ { username: 'test1', email: 'test1@example.com', password: 'pass1' },
1600
+ { username: 'test2', email: 'test2@example.com', password: 'pass2' },
1601
+ { username: 'test3', email: 'test3@example.com', password: 'pass3' }
1602
+ ];
2469
1603
 
2470
- function setupProductionMonitoring(db: UniversalSQLite) {
2471
- // Monitor database size
2472
- setInterval(async () => {
2473
- const sizeInfo = await db.query('PRAGMA page_count; PRAGMA page_size;');
2474
- const pages = sizeInfo.rows[0].page_count;
2475
- const pageSize = sizeInfo.rows[1].page_size;
2476
- const sizeBytes = pages * pageSize;
2477
- const sizeMB = (sizeBytes / 1024 / 1024).toFixed(2);
2478
-
2479
- console.log(`Database size: ${sizeMB} MB`);
2480
-
2481
- if (sizeBytes > 500 * 1024 * 1024) { // 500MB warning
2482
- console.warn('Database size exceeds 500MB, consider archiving old data');
1604
+ const createdUsers: User[] = [];
1605
+ for (const userData of testUsers) {
1606
+ const user = await userService.createUser(userData);
1607
+ if (user) createdUsers.push(user);
2483
1608
  }
2484
- }, 300000); // Check every 5 minutes
2485
-
2486
- // Monitor query performance
2487
- setInterval(async () => {
2488
- const longQueries = await db.query(`
2489
- SELECT * FROM pragma_stats
2490
- WHERE name LIKE '%time%'
2491
- `);
2492
-
2493
- // Log slow queries for optimization
2494
- console.log('Database stats:', longQueries.rows);
2495
- }, 600000); // Check every 10 minutes
2496
- }
2497
-
2498
- productionSetup();
2499
- ```
2500
-
2501
- ## 📖 API Reference
2502
1609
 
2503
- ### Core Types
2504
-
2505
- ```typescript
2506
- interface SQLiteRow {
2507
- [key: string]: any;
2508
- }
2509
-
2510
- interface SQLiteResult {
2511
- rows: SQLiteRow[]; // Dữ liệu trả về
2512
- rowsAffected: number; // Số dòng bị ảnh hưởng
2513
- lastInsertRowId?: number; // ID của record mới insert
1610
+ return createdUsers;
1611
+ }
2514
1612
  }
2515
1613
 
2516
- interface SQLiteConnection {
2517
- execute(sql: string, params?: any[]): Promise<SQLiteResult>;
2518
- close(): Promise<void>;
2519
- }
1614
+ // Example test
1615
+ describe('UserService Tests', () => {
1616
+ let userService: UserService;
2520
1617
 
2521
- interface SQLiteConfig {
2522
- path: string; // Đường dẫn database
2523
- timeout?: number; // Timeout kết nối
2524
- busyTimeout?: number; // Timeout khi database busy
2525
- }
2526
- ```
1618
+ beforeAll(async () => {
1619
+ await DatabaseTestHelpers.setupTestDatabase();
1620
+ userService = new UserService();
1621
+ await userService.init();
1622
+ });
2527
1623
 
2528
- ### Error Codes
1624
+ beforeEach(async () => {
1625
+ await DatabaseTestHelpers.cleanupTestData(userService);
1626
+ });
2529
1627
 
2530
- | Error | Description | Solution |
2531
- |-------|-------------|----------|
2532
- | `No supported SQLite adapter found` | Không tìm thấy adapter phù hợp | Cài đặt SQLite dependencies cho môi trường |
2533
- | `Database connection not available` | Chưa kết nối database | Gọi `connect()` trước khi query |
2534
- | `SQLite error: no such table` | Bảng không tồn tại | Tạo bảng bằng `createTable()` hoặc SQL |
2535
- | `UNIQUE constraint failed` | Vi phạm ràng buộc unique | Kiểm tra dữ liệu trước khi insert |
2536
- | `database is locked` | Database đang bị khóa | Retry sau một khoảng thời gian |
1628
+ afterAll(async () => {
1629
+ await DatabaseManager.closeAll();
1630
+ });
2537
1631
 
2538
- ## 🤝 Contributing
1632
+ test('should create user successfully', async () => {
1633
+ const userData = {
1634
+ username: 'testuser',
1635
+ email: 'test@example.com',
1636
+ password: 'password123'
1637
+ };
2539
1638
 
2540
- ### Development Setup
1639
+ const user = await userService.createUser(userData);
1640
+ expect(user).toBeTruthy();
1641
+ expect(user?.username).toBe(userData.username);
1642
+ expect(user?.email).toBe(userData.email);
1643
+ });
1644
+ });
1645
+ ```
2541
1646
 
2542
- ```bash
2543
- # Clone repository
2544
- git clone https://github.com/dqcai/sqlite
2545
- cd sqlite
1647
+ ## 9. Troubleshooting Common Issues
2546
1648
 
2547
- # Install dependencies
2548
- npm install
1649
+ ### Database Locked
2549
1650
 
2550
- # Run tests
2551
- npm test
1651
+ ```typescript
1652
+ // Giải quyết database locked
1653
+ const handleDatabaseLocked = async () => {
1654
+ try {
1655
+ // Enable WAL mode để tránh lock
1656
+ const dao = DatabaseManager.get('users');
1657
+ await dao.execute('PRAGMA journal_mode = WAL');
1658
+ await dao.execute('PRAGMA busy_timeout = 30000'); // 30 giây timeout
1659
+ } catch (error) {
1660
+ console.error('Error setting WAL mode:', error);
1661
+ }
1662
+ };
1663
+ ```
2552
1664
 
2553
- # Build
2554
- npm run build
1665
+ ### Connection Issues
2555
1666
 
2556
- # Test in different environments
2557
- npm run test:node
2558
- npm run test:browser
2559
- npm run test:deno
2560
- npm run test:bun
1667
+ ```typescript
1668
+ // Kiểm tra và khôi phục kết nối
1669
+ const ensureConnectionHealth = async (dbKey: string) => {
1670
+ try {
1671
+ const dao = DatabaseManager.get(dbKey);
1672
+ await dao.execute('SELECT 1');
1673
+ } catch (error) {
1674
+ console.log(`Connection ${dbKey} unhealthy, reconnecting...`);
1675
+ await DatabaseManager.closeConnection(dbKey);
1676
+ await DatabaseManager.getLazyLoading(dbKey);
1677
+ console.log(`Connection ${dbKey} restored`);
1678
+ }
1679
+ };
2561
1680
  ```
2562
1681
 
2563
- ### Testing New Adapters
1682
+ ## 10. Migration & Schema Updates
2564
1683
 
2565
1684
  ```typescript
2566
- // tests/adapter-test.ts
2567
- import { SQLiteAdapter } from '../src/types';
1685
+ // migrations/001_add_user_status.ts
1686
+ import { UniversalDAO } from '@dqcai/sqlite';
2568
1687
 
2569
- export async function testAdapter(adapter: SQLiteAdapter, testDbPath: string) {
2570
- console.log(`Testing adapter: ${adapter.constructor.name}`);
2571
-
2572
- // Test 1: Support detection
2573
- console.log(`✓ isSupported(): ${adapter.isSupported()}`);
2574
-
2575
- if (!adapter.isSupported()) {
2576
- console.log('❌ Adapter not supported in current environment');
2577
- return;
2578
- }
1688
+ export const migration_001 = {
1689
+ version: '1.0.1',
1690
+ description: 'Add status column to users table',
2579
1691
 
2580
- try {
2581
- // Test 2: Connection
2582
- const connection = await adapter.connect(testDbPath);
2583
- console.log('✓ Connection established');
2584
-
2585
- // Test 3: Table creation
2586
- await connection.execute(`
2587
- CREATE TABLE IF NOT EXISTS test_table (
2588
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2589
- name TEXT NOT NULL,
2590
- value INTEGER
2591
- )
1692
+ async up(dao: UniversalDAO): Promise<void> {
1693
+ await dao.execute(`
1694
+ ALTER TABLE users
1695
+ ADD COLUMN status VARCHAR(20) DEFAULT 'active'
2592
1696
  `);
2593
- console.log('✓ Table created');
2594
-
2595
- // Test 4: Insert
2596
- const insertResult = await connection.execute(
2597
- 'INSERT INTO test_table (name, value) VALUES (?, ?)',
2598
- ['Test Item', 42]
2599
- );
2600
- console.log('✓ Insert successful, ID:', insertResult.lastInsertRowId);
2601
1697
 
2602
- // Test 5: Select
2603
- const selectResult = await connection.execute('SELECT * FROM test_table');
2604
- console.log('✓ Select successful, rows:', selectResult.rows.length);
1698
+ await dao.execute(`
1699
+ CREATE INDEX idx_user_status ON users(status)
1700
+ `);
1701
+ },
1702
+
1703
+ async down(dao: UniversalDAO): Promise<void> {
1704
+ await dao.execute(`
1705
+ DROP INDEX IF EXISTS idx_user_status
1706
+ `);
2605
1707
 
2606
- // Test 6: Update
2607
- const updateResult = await connection.execute(
2608
- 'UPDATE test_table SET value = ? WHERE id = ?',
2609
- [100, insertResult.lastInsertRowId]
2610
- );
2611
- console.log('✓ Update successful, affected:', updateResult.rowsAffected);
1708
+ // SQLite không hỗ trợ DROP COLUMN, cần recreate table
1709
+ await dao.execute(`
1710
+ CREATE TABLE users_backup AS
1711
+ SELECT id, username, email, password, created_at, updated_at
1712
+ FROM users
1713
+ `);
2612
1714
 
2613
- // Test 7: Delete
2614
- const deleteResult = await connection.execute(
2615
- 'DELETE FROM test_table WHERE id = ?',
2616
- [insertResult.lastInsertRowId]
2617
- );
2618
- console.log('✓ Delete successful, affected:', deleteResult.rowsAffected);
1715
+ await dao.execute(`DROP TABLE users`);
2619
1716
 
2620
- // Test 8: Close
2621
- await connection.close();
2622
- console.log('✓ Connection closed');
1717
+ await dao.execute(`
1718
+ CREATE TABLE users (
1719
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1720
+ username VARCHAR(50) UNIQUE NOT NULL,
1721
+ email VARCHAR(100) UNIQUE NOT NULL,
1722
+ password VARCHAR(255) NOT NULL,
1723
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1724
+ updated_at DATETIME
1725
+ )
1726
+ `);
2623
1727
 
2624
- console.log(`🎉 All tests passed for ${adapter.constructor.name}`);
1728
+ await dao.execute(`
1729
+ INSERT INTO users SELECT * FROM users_backup
1730
+ `);
2625
1731
 
2626
- } catch (error) {
2627
- console.error(`❌ Test failed:`, error);
1732
+ await dao.execute(`DROP TABLE users_backup`);
2628
1733
  }
2629
- }
1734
+ };
1735
+
1736
+ // Chạy migration
1737
+ const runMigration = async () => {
1738
+ const dao = DatabaseManager.get('users');
1739
+ const currentVersion = await dao.getSchemaVersion();
1740
+
1741
+ if (currentVersion < '1.0.1') {
1742
+ await migration_001.up(dao);
1743
+ await dao.setSchemaVersion('1.0.1');
1744
+ console.log('Migration 001 completed');
1745
+ }
1746
+ };
2630
1747
  ```
2631
1748
 
1749
+ ## Kết luận
1750
+
1751
+ Universal SQLite cung cấp một giải pháp mạnh mẽ và linh hoạt để quản lý cơ sở dữ liệu SQLite across platforms. Với DatabaseManager và BaseService, bạn có thể:
1752
+
1753
+ - Dễ dàng quản lý nhiều database connections
1754
+ - Thực hiện CRUD operations một cách type-safe
1755
+ - Import/Export dữ liệu hiệu quả
1756
+ - Quản lý schema và migrations
1757
+ - Handle errors và performance optimization
1758
+
1759
+ Thư viện hỗ trợ tốt cho cả React Native và Node.js, giúp bạn xây dựng ứng dụng database-driven một cách nhất quán và maintainable.
1760
+
1761
+ ## API Reference
1762
+
1763
+ - **UniversalSQLite**: Singleton chính, methods: initialize, connect, getDAO, query, execute, importData, etc.
1764
+ - **UniversalDAO**: Core DAO cho CRUD, execute, importData.
1765
+ - **QueryBuilder**: Xây dựng query với fluent API.
1766
+ - **MigrationManager**: Quản lý migration.
1767
+ - **CSVImporter**: Import/export CSV.
1768
+ - **BaseService**: Base cho service layer.
1769
+ - **DatabaseFactory**: Factory để tạo DAO.
1770
+ - **DatabaseManager**: Quản lý connections, roles.
1771
+
1772
+ Xem source code để biết chi tiết types và methods.
1773
+
1774
+
1775
+ ## Best Practices
1776
+
1777
+ 1. **Always use transactions for multi-step operations**
1778
+ 2. **Define schemas for type safety and validation**
1779
+ 3. **Use parameterized queries to prevent SQL injection**
1780
+ 4. **Implement proper error handling**
1781
+ 5. **Close connections when done**
1782
+ 6. **Use migrations for schema changes**
1783
+ 7. **Batch large operations for better performance**
1784
+
1785
+ ## Contributing
1786
+
1787
+ 1. Fork the repository
1788
+ 2. Create a feature branch
1789
+ 3. Make your changes
1790
+ 4. Add tests
1791
+ 5. Submit a pull request
1792
+
1793
+
2632
1794
  ## 📄 License
2633
1795
 
2634
- MIT License - see LICENSE file for details.
1796
+ MIT © [Cuong Doan](https://github.com/cuongdqpayment)
2635
1797
 
2636
1798
  ## 🙏 Acknowledgments
2637
1799
 
@@ -2646,8 +1808,9 @@ MIT License - see LICENSE file for details.
2646
1808
  - [Documentation](https://github.com/cuongdqpayment/dqcai-sqlite/docs)
2647
1809
  - [Examples Repository](https://github.com/cuongdqpayment/dqcai-sqlite)
2648
1810
  - [Issue Tracker](https://github.com/cuongdqpayment/dqcai-sqlite/issues)
1811
+ - [Issue Facebook](https://www.facebook.com/share/p/19esHGbaGj/)
2649
1812
  - [NPM Package](https://www.npmjs.com/package/@dqcai/sqlite)
2650
1813
 
2651
1814
  ---
2652
1815
 
2653
- **Universal SQLite** - One library, all platforms! 🚀
1816
+ 🔥 **@dqcai/sqlite** One library, all platforms! 🚀