• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Persisting RDB Store Data (ArkTS)
2
3
4## When to Use
5
6A relational database (RDB) store is used to store data in complex relational models, such as the student information including names, student IDs, and scores of each subject, or employee information including names, employee IDs, and positions, based on SQLite. The data is more complex than key-value (KV) pairs due to strict mappings. You can use **RelationalStore** to implement persistence of this type of data.
7
8Querying data from a large amount of data may take time or even cause application suspension. In this case, you can perform batch operations. For details, see [Batch Database Operations](../arkts-utils/batch-database-operations-guide.md). Moreover, observe the following:
9- The maximum number of data records to query at a time is 5000.
10- Use [TaskPool](../reference/apis-arkts/js-apis-taskpool.md) if a large amount of data needs to be queried.
11- Keep concatenated SQL statements as concise as possible.
12- Query data in batches.
13
14## Basic Concepts
15
16- **Predicates**: a representation of the property or feature of a data entity, or the relationship between data entities. It is used to define operation conditions.
17
18- **ResultSet**: a set of query results, which allows access to the required data in flexible modes.
19
20
21## Working Principles
22
23**RelationalStore** provides APIs for data operations. With SQLite as the underlying persistent storage engine, **RelationalStore** provides SQLite database features, including transactions, indexes, views, triggers, foreign keys, parameterized queries, prepared SQL statements, and more.
24
25**Figure 1** Working mechanism
26
27![relationStore_local](figures/relationStore_local.jpg)
28
29
30## Constraints
31
32- The default logging mode is Write Ahead Log (WAL), and the default flushing mode is **FULL** mode.
33
34- The RDB store supports four read connections and one write connection. Read connections can be dynamically expanded. If no read connection is available, a read connection is created to execute the read operation. Write connections cannot be dynamically expanded. If no write connection is available, the write operation is executed after the connection is released.
35
36- To ensure data accuracy, only one write operation is allowed at a time.
37
38- Once an application is uninstalled, related database files and temporary files on the device are automatically deleted.
39
40- ArkTS supports the following basic data types: number, string, binary, and boolean.
41
42- The maximum size of a data record is 2 MB. If a data record exceeds 2 MB, it can be inserted successfully but cannot be read.
43
44## Available APIs
45
46The following table lists the APIs used for RDB data persistence. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis-arkdata/js-apis-data-relationalStore.md).
47
48| API| Description|
49| -------- | -------- |
50| getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback<RdbStore>): void | Obtains an **RdbStore** instance to implement RDB store operations. You can set **RdbStore** parameters based on actual requirements and use **RdbStore** APIs to perform data operations.|
51| executeSql(sql: string, bindArgs: Array<ValueType>, callback: AsyncCallback<void>):void | Executes an SQL statement that contains specified arguments but returns no value.|
52| insert(table: string, values: ValuesBucket, callback: AsyncCallback<number>):void | Inserts a row of data into a table.|
53| update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void | Updates data in the RDB store based on the specified **predicates** instance.|
54| delete(predicates: RdbPredicates, callback: AsyncCallback<number>):void | Deletes data from the RDB store based on the specified **predicates** instance.|
55| query(predicates: RdbPredicates, columns: Array<string>, callback: AsyncCallback<ResultSet>):void | Queries data in the RDB store based on specified conditions.|
56| deleteRdbStore(context: Context, name: string, callback: AsyncCallback<void>): void | Deletes an RDB store.|
57| isTokenizerSupported(tokenizer: Tokenizer): boolean | Checks whether the specified tokenizer is supported. (Tokenizer is a tool for breaking down text into smaller units, which can be words, subwords, characters, or other language fragments.)|
58
59## How to Develop
60Unless otherwise specified, the sample code without "stage model" or "FA model" applies to both models.
61
62If error 14800011 is thrown, you need to rebuild the database and restore data to ensure normal application development. For details, see [Rebuilding an RDB Store](data-backup-and-restore.md#rebuilding-an-rdb-store).
63
641. Obtain an **RdbStore** instance, which includes operations of creating an RDB store and tables, and upgrading or downgrading the RDB store. <br>Example:
65
66   Stage model:
67
68   ```ts
69   import { relationalStore} from '@kit.ArkData'; // Import the relationalStore module.
70   import { UIAbility } from '@kit.AbilityKit';
71   import { BusinessError } from '@kit.BasicServicesKit';
72   import { window } from '@kit.ArkUI';
73
74   // In this example, Ability is used to obtain an RdbStore instance. You can use other implementations as required.
75   class EntryAbility extends UIAbility {
76     onWindowStageCreate(windowStage: window.WindowStage) {
77       // Before using a tokenizer, call isStorageTypeSupported to check whether the tokenizer is supported by the current platform.
78       let tokenType = relationalStore.Tokenizer.ICU_TOKENIZER;
79       let tokenTypeSupported = relationalStore.isTokenizerSupported(tokenType);
80       if (!tokenTypeSupported) {
81         console.error(`ICU_TOKENIZER is not supported on this platform.`);
82       }
83       const STORE_CONFIG: relationalStore.StoreConfig = {
84         name: 'RdbTest.db', // Database file name.
85         securityLevel: relationalStore.SecurityLevel.S3, // Database security level.
86         encrypt: false, // Whether to encrypt the database. This parameter is optional. By default, the database is not encrypted.
87         customDir: 'customDir/subCustomDir', // (Optional) Customized database path. The database is created in the context.databaseDir + '/rdb/' + customDir directory, where context.databaseDir indicates the application sandbox path, '/rdb/' indicates a relational database, and customDir indicates the customized path. If this parameter is not specified, an RdbStore instance is created in the sandbox directory of the application.
88         isReadOnly: false, // (Optional) Specify whether the RDB store is opened in read-only mode. The default value is false, which means the RDB store is readable and writable. If this parameter is true, data can only be read from the RDB store. If write operation is performed, error code 801 is returned.
89         tokenizer: tokenType // (Optional) Type of the tokenizer used in full-text search (FTS). If this parameter is left blank, only English word segmentation is supported in FTS.
90       };
91
92       // Check the RDB store version. If the version is incorrect, upgrade or downgrade the RDB store.
93       // For example, the RDB store version is 3 and the table structure is EMPLOYEE (NAME, AGE, SALARY, CODES, IDENTITY).
94       const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB, IDENTITY UNLIMITED INT)'; // SQL statement used to create a table. In the statement, the IDENTITY type bigint should be UNLIMITED INT.
95
96       relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
97         if (err) {
98           console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
99           return;
100         }
101         console.info('Succeeded in getting RdbStore.');
102
103         // When the RDB store is created, the default version is 0.
104         if (store.version === 0) {
105           store.executeSql(SQL_CREATE_TABLE) // Create a table.
106             .then(() => {
107               // Set the RDB store version, which must be an integer greater than 0.
108               store.version = 3;
109             })
110             .catch((err: BusinessError) => {
111               console.error(`Failed to executeSql. Code:${err.code}, message:${err.message}`);
112             });
113         }
114
115         // If the RDB store version is not 0 and does not match the current version, upgrade or downgrade the RDB store.
116         // For example, upgrade the RDB store from version 1 to version 2.
117         if (store.version === 1) {
118           // Upgrade the RDB store from version 1 to version 2, and change the table structure from EMPLOYEE (NAME, SALARY, CODES, ADDRESS) to EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS).
119           store.executeSql('ALTER TABLE EMPLOYEE ADD COLUMN AGE INTEGER')
120             .then(() => {
121               store.version = 2;
122             }).catch((err: BusinessError) => {
123               console.error(`Failed to executeSql. Code:${err.code}, message:${err.message}`);
124             });
125         }
126
127         // For example, upgrade the RDB store from version 2 to version 3.
128         if (store.version === 2) {
129           // Upgrade the RDB store from version 2 to version 3, and change the table structure from EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS) to EMPLOYEE (NAME, AGE, SALARY, CODES).
130           store.executeSql('ALTER TABLE EMPLOYEE DROP COLUMN ADDRESS')
131             .then(() => {
132               store.version = 3;
133             }).catch((err: BusinessError) => {
134               console.error(`Failed to executeSql. Code:${err.code}, message:${err.message}`);
135             });
136         }
137         // Before adding, deleting, modifying, and querying data in an RDB store, obtain an RdbStore instance and create a table.
138       });
139     }
140   }
141   ```
142
143   FA model:
144
145   ```ts
146   import { relationalStore} from '@kit.ArkData'; // Import the relationalStore module.
147   import { featureAbility } from '@kit.AbilityKit';
148   import { BusinessError } from '@kit.BasicServicesKit';
149
150   let context = featureAbility.getContext();
151
152   const STORE_CONFIG: relationalStore.StoreConfig = {
153     name: 'RdbTest.db', // Database file name.
154     securityLevel: relationalStore.SecurityLevel.S3 // Database security level.
155   };
156
157   // For example, the RDB store version is 3 and the table structure is EMPLOYEE (NAME, AGE, SALARY, CODES, IDENTITY).
158   const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB, IDENTITY UNLIMITED INT)'; // SQL statement used to create a table. In the statement, the IDENTITY type bigint should be UNLIMITED INT.
159
160   relationalStore.getRdbStore(context, STORE_CONFIG, (err, store) => {
161     if (err) {
162       console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
163       return;
164     }
165     console.info('Succeeded in getting RdbStore.');
166
167     // When the RDB store is created, the default version is 0.
168     if (store.version === 0) {
169       store.executeSql(SQL_CREATE_TABLE); // Create a table.
170         .then(() => {
171           // Set the RDB store version, which must be an integer greater than 0.
172           store.version = 3;
173         })
174         .catch((err: BusinessError) => {
175           console.error(`Failed to executeSql. Code:${err.code}, message:${err.message}`);
176         });
177     }
178
179     // If the RDB store version is not 0 and does not match the current version, upgrade or downgrade the RDB store.
180     // For example, upgrade the RDB store from version 1 to version 2.
181     if (store.version === 1) {
182       // Upgrade the RDB store from version 1 to version 2, and change the table structure from EMPLOYEE (NAME, SALARY, CODES, ADDRESS) to EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS).
183       store.executeSql('ALTER TABLE EMPLOYEE ADD COLUMN AGE INTEGER')
184         .then(() => {
185           store.version = 2;
186         }).catch((err: BusinessError) => {
187           console.error(`Failed to executeSql. Code:${err.code}, message:${err.message}`);
188         });
189     }
190
191     // For example, upgrade the RDB store from version 2 to version 3.
192     if (store.version === 2) {
193       // Upgrade the RDB store from version 2 to version 3, and change the table structure from EMPLOYEE (NAME, AGE, SALARY, CODES, ADDRESS) to EMPLOYEE (NAME, AGE, SALARY, CODES).
194       store.executeSql('ALTER TABLE EMPLOYEE DROP COLUMN ADDRESS')
195         .then(() => {
196           store.version = 3;
197         }).catch((err: BusinessError) => {
198           console.error(`Failed to executeSql. Code:${err.code}, message:${err.message}`);
199         });
200     }
201     // Before adding, deleting, modifying, and querying data in an RDB store, obtain an RdbStore instance and create a table.
202   });
203
204   ```
205
206   > **NOTE**
207   >
208   > - The RDB store created by an application varies with the context. Multiple RDB stores are created for the same database name with different application contexts. For example, each UIAbility has its own context.
209   >
210   > - When an application calls **getRdbStore()** to obtain an RDB store instance for the first time, the corresponding database file is generated in the application sandbox. When the RDB store is used, temporary files ended with **-wal** and **-shm** may be generated in the same directory as the database file. If you want to move the database files to other places, you must also move these temporary files. After the application is uninstalled, the database files and temporary files generated on the device are also removed.
211   >
212   > - For details about the error codes, see [Universal Error Codes](../reference/errorcode-universal.md) and [RDB Store Error Codes](../reference/apis-arkdata/errorcode-data-rdb.md).
213
2142. Call **insert()** to insert data. <br>Example:
215
216   ```ts
217   let store: relationalStore.RdbStore | undefined = undefined;
218
219   let value1 = 'Lisa';
220   let value2 = 18;
221   let value3 = 100.5;
222   let value4 = new Uint8Array([1, 2, 3, 4, 5]);
223   let value5 = BigInt('15822401018187971961171');
224   // You can use either of the following:
225   const valueBucket1: relationalStore.ValuesBucket = {
226     'NAME': value1,
227     'AGE': value2,
228     'SALARY': value3,
229     'CODES': value4,
230     'IDENTITY': value5,
231   };
232   const valueBucket2: relationalStore.ValuesBucket = {
233     NAME: value1,
234     AGE: value2,
235     SALARY: value3,
236     CODES: value4,
237     IDENTITY: value5,
238   };
239   const valueBucket3: relationalStore.ValuesBucket = {
240     "NAME": value1,
241     "AGE": value2,
242     "SALARY": value3,
243     "CODES": value4,
244     "IDENTITY": value5,
245   };
246
247   if (store !== undefined) {
248     (store as relationalStore.RdbStore).insert('EMPLOYEE', valueBucket1, (err: BusinessError, rowId: number) => {
249       if (err) {
250         console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
251         return;
252       }
253       console.info(`Succeeded in inserting data. rowId:${rowId}`);
254     })
255   }
256   ```
257
258   > **NOTE**
259   >
260   > **RelationalStore** does not provide explicit flush operations for data persistence. The **insert()** method stores data persistently.
261
2623. Modify or delete data based on the specified **Predicates** instance.
263
264   Call **update()** to modify data and **delete()** to delete data. <br>Example:
265
266   ```ts
267   let value6 = 'Rose';
268   let value7 = 22;
269   let value8 = 200.5;
270   let value9 = new Uint8Array([1, 2, 3, 4, 5]);
271   let value10 = BigInt('15822401018187971967863');
272   // You can use either of the following:
273   const valueBucket4: relationalStore.ValuesBucket = {
274     'NAME': value6,
275     'AGE': value7,
276     'SALARY': value8,
277     'CODES': value9,
278     'IDENTITY': value10,
279   };
280   const valueBucket5: relationalStore.ValuesBucket = {
281     NAME: value6,
282     AGE: value7,
283     SALARY: value8,
284     CODES: value9,
285     IDENTITY: value10,
286   };
287   const valueBucket6: relationalStore.ValuesBucket = {
288     "NAME": value6,
289     "AGE": value7,
290     "SALARY": value8,
291     "CODES": value9,
292     "IDENTITY": value10,
293   };
294
295   // Modify data.
296   let predicates1 = new relationalStore.RdbPredicates('EMPLOYEE'); // Create predicates for the table named EMPLOYEE.
297   predicates1.equalTo('NAME', 'Lisa'); // Modify the data of Lisa in the EMPLOYEE table to the specified data.
298   if (store !== undefined) {
299     (store as relationalStore.RdbStore).update(valueBucket4, predicates1, (err: BusinessError, rows: number) => {
300       if (err) {
301         console.error(`Failed to update data. Code:${err.code}, message:${err.message}`);
302        return;
303      }
304      console.info(`Succeeded in updating data. row count: ${rows}`);
305     })
306   }
307
308   // Delete data.
309   predicates1 = new relationalStore.RdbPredicates('EMPLOYEE');
310   predicates1.equalTo('NAME', 'Lisa');
311   if (store !== undefined) {
312     (store as relationalStore.RdbStore).delete(predicates1, (err: BusinessError, rows: number) => {
313       if (err) {
314         console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`);
315         return;
316       }
317       console.info(`Delete rows: ${rows}`);
318     })
319   }
320   ```
321
3224. Query data based on the conditions specified by **Predicates**.
323
324   Call **query()** to query data. The data obtained is returned in a **ResultSet** object. <br>Example:
325
326   ```ts
327   let predicates2 = new relationalStore.RdbPredicates('EMPLOYEE');
328   predicates2.equalTo('NAME', 'Rose');
329   if (store !== undefined) {
330     (store as relationalStore.RdbStore).query(predicates2, ['ID', 'NAME', 'AGE', 'SALARY', 'IDENTITY'], (err: BusinessError, resultSet) => {
331       if (err) {
332         console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
333         return;
334       }
335       console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
336       // resultSet is a cursor of a data set. By default, the cursor points to the -1st record. Valid data starts from 0.
337       while (resultSet.goToNextRow()) {
338         const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
339         const name = resultSet.getString(resultSet.getColumnIndex('NAME'));
340         const age = resultSet.getLong(resultSet.getColumnIndex('AGE'));
341         const salary = resultSet.getDouble(resultSet.getColumnIndex('SALARY'));
342         const identity = resultSet.getValue(resultSet.getColumnIndex('IDENTITY'));
343         console.info(`id=${id}, name=${name}, age=${age}, salary=${salary}, identity=${identity}`);
344       }
345       // Release the data set memory.
346       resultSet.close();
347     })
348   }
349   ```
350
351   > **NOTE**
352   >
353   > Use **close()** to close the **ResultSet** that is no longer used in a timely manner so that the memory allocated can be released.
354
355   The RDB store also supports full-text search (FTS) in Chinese or English. The ICU tokenizer is supported.
356
357   The following example demonstrates how to perform FTS with Chinese keywords:
358
359   ```ts
360   let store: relationalStore.RdbStore | undefined = undefined;
361   if (store !== undefined) {
362     // Create an FTS table.
363     const  SQL_CREATE_TABLE = "CREATE VIRTUAL TABLE example USING fts4(name, content, tokenize=icu zh_CN)";
364     (store as relationalStore.RdbStore).executeSql(SQL_CREATE_TABLE, (err: BusinessError) => {
365       if (err) {
366         console.error(`Failed to creating fts table.`);
367         return;
368       }
369       console.info(`Succeeded in creating fts table.`);
370     })
371   }
372   if(store != undefined) {
373      (store as relationalStore.RdbStore).querySql("SELECT name FROM example WHERE example MATCH 'Text'", (err, resultSet) => {
374        if (err) {
375          console.error(`Query failed.`);
376          return;
377        }
378        while (resultSet.goToNextRow()) {
379          const name = resultSet.getString(resultSet.getColumnIndex("name"));
380          console.info(`name=${name}`);
381        }
382        resultSet.close();
383      })
384   }
385   ```
386
3875. Back up the database in the same directory. <br>Two backup modes are available: manual backup and automatic backup (available only for system applications). For details, see [Backing Up an RDB Store](data-backup-and-restore.md#backing-up-an-rdb-store).
388
389   Example: Perform manual backup of an RDB store.
390
391   ```ts
392   if (store !== undefined) {
393     // Backup.db indicates the name of the database backup file. By default, it is in the same directory as the RdbStore file. You can also specify the directory, which is in the customDir + backup.db format.
394     (store as relationalStore.RdbStore).backup("Backup.db", (err: BusinessError) => {
395       if (err) {
396         console.error(`Failed to backup RdbStore. Code:${err.code}, message:${err.message}`);
397         return;
398       }
399       console.info(`Succeeded in backing up RdbStore.`);
400     })
401   }
402   ```
403
4046. Restore data from the database backup. <br>You can restore an RDB store from the manual backup data or automatic backup data (available only for system applications). For details, see [Restoring RDB Store Data](data-backup-and-restore.md#restoring-rdb-store-data).
405
406   Example: Call [restore](../reference/apis-arkdata/js-apis-data-relationalStore.md#restore) to restore an RDB store from the data that is manually backed up.
407
408   ```ts
409   if (store !== undefined) {
410     (store as relationalStore.RdbStore).restore("Backup.db", (err: BusinessError) => {
411       if (err) {
412         console.error(`Failed to restore RdbStore. Code:${err.code}, message:${err.message}`);
413         return;
414       }
415       console.info(`Succeeded in restoring RdbStore.`);
416     })
417   }
418   ```
419
4207. Delete the RDB store.
421
422   Call **deleteRdbStore()** to delete the RDB store and related database files. <br>Example:
423
424   Stage model:
425
426   ```ts
427   relationalStore.deleteRdbStore(this.context, 'RdbTest.db', (err: BusinessError) => {
428    if (err) {
429       console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
430       return;
431     }
432     console.info('Succeeded in deleting RdbStore.');
433   });
434   ```
435
436   FA model:
437
438   ```ts
439   relationalStore.deleteRdbStore(context, 'RdbTest.db', (err: BusinessError) => {
440     if (err) {
441       console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
442       return;
443     }
444     console.info('Succeeded in deleting RdbStore.');
445   });
446   ```
447
448