Tools: How can relational functional tables be created with RDB Store?

Tools: How can relational functional tables be created with RDB Store?

Source: Dev.to

Requirement Description ## Background Knowledge ## Implementation Steps ## Code Snippet / Configuration ## Test Results ## Limitations or Considerations ## Related Documents or Links ## Written by Mehmet Algul Read the original article:How can relational functional tables be created with RDB Store? When developing HarmonyOS applications, developers frequently require persistent data storage mechanisms that allow applications to retain state and manage user data across sessions. For lightweight storage of simple key-value pairs, Shared Preferences provides a convenient solution. However, when applications need to handle structured, complex, or relational data, developers often turn to RDB Store. Both technologies complement each other as local database services, enabling developers to select the most suitable option based on their use cases. RDB Store is an SQL-based local database technology integrated into HarmonyOS. It provides developers with the ability to define schemas, create tables, and manage structured datasets with powerful querying capabilities. Unlike Shared Preferences, which is designed for simple data storage, RDB Store supports the creation of multiple tables that can be related to each other through primary keys, foreign keys, and joins, enabling relational database design within an application. This relational model allows developers to: By leveraging RDB Store, HarmonyOS applications can achieve robust local data management, improve data consistency, and deliver a richer offline experience for end users. For example, consider an application that allows users to organize their contacts into custom groups they create, and provides functionality to manage both the groups and the associated contacts. Accordingly, the implementation steps we need to follow are as follows: 1.First, in order to ensure that the relevant database and tables are created as soon as the application is launched, we perform the necessary operations within the EntryAbility. 2.Secondly, we create a class called ContactUtils that handles CRUD operations on the contacts we have created. In the example, we also use @kit.ContactsKit for handling contact operations. 3.Finally, to perform operations on groups, we need to create a class called GroupsUtils. The following screenshots illustrate the results of the database operations that have been performed. The technologies used in this example are compatible with real HarmonyOS mobile and wearable devices. https://developer.huawei.com/consumer/en/doc/harmonyos-guides/data-persistence-by-rdb-store https://developer.huawei.com/consumer/en/doc/harmonyos-guides/contacts-kit Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK: import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; export let rdbStore: relationalStore.RdbStore; interface StoreConfig { name: string; securityLevel: relationalStore.SecurityLevel; encrypt?: boolean; } const DOMAIN = 0x0000; export default class EntryAbility extends UIAbility { public async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'); const CONTACTSTREE_CONFIG: StoreConfig = { name: 'ContactsTreeDB.db', securityLevel: relationalStore.SecurityLevel.S1, encrypt: false } const SQL_CREATE_CONTACTS_TABLE = ` CREATE TABLE IF NOT EXISTS contacts ( id INTEGER PRIMARY KEY, key TEXT NOT NULL, name TEXT NOT NULL, phone TEXT NOT NULL ) `; const SQL_CREATE_GROUPS_TABLE = ` CREATE TABLE IF NOT EXISTS groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE ) `; const SQL_CREATE_CONTACT_GROUPS_TABLE = ` CREATE TABLE IF NOT EXISTS contact_groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, contact_id INTEGER NOT NULL, group_id INTEGER NOT NULL, FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE, FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE, UNIQUE(contact_id, group_id) ) `; try { rdbStore = await relationalStore.getRdbStore(this.context, CONTACTSTREE_CONFIG); await rdbStore.executeSql(SQL_CREATE_CONTACTS_TABLE); await rdbStore.executeSql(SQL_CREATE_GROUPS_TABLE); await rdbStore.executeSql(SQL_CREATE_CONTACT_GROUPS_TABLE); } catch (error) { hilog.error(DOMAIN, 'DataAbility', 'RDB error: %{public}s', JSON.stringify(error)); } } onDestroy(): void { hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); } onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); return; } hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); }); } onWindowStageDestroy(): void { // Main window is destroyed, release UI related resources hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); } onForeground(): void { // Ability has brought to foreground hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground'); } onBackground(): void { // Ability has back to background hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground'); } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; export let rdbStore: relationalStore.RdbStore; interface StoreConfig { name: string; securityLevel: relationalStore.SecurityLevel; encrypt?: boolean; } const DOMAIN = 0x0000; export default class EntryAbility extends UIAbility { public async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'); const CONTACTSTREE_CONFIG: StoreConfig = { name: 'ContactsTreeDB.db', securityLevel: relationalStore.SecurityLevel.S1, encrypt: false } const SQL_CREATE_CONTACTS_TABLE = ` CREATE TABLE IF NOT EXISTS contacts ( id INTEGER PRIMARY KEY, key TEXT NOT NULL, name TEXT NOT NULL, phone TEXT NOT NULL ) `; const SQL_CREATE_GROUPS_TABLE = ` CREATE TABLE IF NOT EXISTS groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE ) `; const SQL_CREATE_CONTACT_GROUPS_TABLE = ` CREATE TABLE IF NOT EXISTS contact_groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, contact_id INTEGER NOT NULL, group_id INTEGER NOT NULL, FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE, FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE, UNIQUE(contact_id, group_id) ) `; try { rdbStore = await relationalStore.getRdbStore(this.context, CONTACTSTREE_CONFIG); await rdbStore.executeSql(SQL_CREATE_CONTACTS_TABLE); await rdbStore.executeSql(SQL_CREATE_GROUPS_TABLE); await rdbStore.executeSql(SQL_CREATE_CONTACT_GROUPS_TABLE); } catch (error) { hilog.error(DOMAIN, 'DataAbility', 'RDB error: %{public}s', JSON.stringify(error)); } } onDestroy(): void { hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); } onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); return; } hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); }); } onWindowStageDestroy(): void { // Main window is destroyed, release UI related resources hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); } onForeground(): void { // Ability has brought to foreground hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground'); } onBackground(): void { // Ability has back to background hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground'); } } COMMAND_BLOCK: import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; export let rdbStore: relationalStore.RdbStore; interface StoreConfig { name: string; securityLevel: relationalStore.SecurityLevel; encrypt?: boolean; } const DOMAIN = 0x0000; export default class EntryAbility extends UIAbility { public async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'); const CONTACTSTREE_CONFIG: StoreConfig = { name: 'ContactsTreeDB.db', securityLevel: relationalStore.SecurityLevel.S1, encrypt: false } const SQL_CREATE_CONTACTS_TABLE = ` CREATE TABLE IF NOT EXISTS contacts ( id INTEGER PRIMARY KEY, key TEXT NOT NULL, name TEXT NOT NULL, phone TEXT NOT NULL ) `; const SQL_CREATE_GROUPS_TABLE = ` CREATE TABLE IF NOT EXISTS groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE ) `; const SQL_CREATE_CONTACT_GROUPS_TABLE = ` CREATE TABLE IF NOT EXISTS contact_groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, contact_id INTEGER NOT NULL, group_id INTEGER NOT NULL, FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE, FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE, UNIQUE(contact_id, group_id) ) `; try { rdbStore = await relationalStore.getRdbStore(this.context, CONTACTSTREE_CONFIG); await rdbStore.executeSql(SQL_CREATE_CONTACTS_TABLE); await rdbStore.executeSql(SQL_CREATE_GROUPS_TABLE); await rdbStore.executeSql(SQL_CREATE_CONTACT_GROUPS_TABLE); } catch (error) { hilog.error(DOMAIN, 'DataAbility', 'RDB error: %{public}s', JSON.stringify(error)); } } onDestroy(): void { hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); } onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); return; } hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); }); } onWindowStageDestroy(): void { // Main window is destroyed, release UI related resources hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); } onForeground(): void { // Ability has brought to foreground hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground'); } onBackground(): void { // Ability has back to background hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground'); } } COMMAND_BLOCK: import { contact } from '@kit.ContactsKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; import {Contact} from '../types/Contact'; import {rdbStore} from '../entryability/EntryAbility' export async function addContact(context: common.UIAbilityContext, fullName: string, phoneNumber: string) { try { let contactInfo: contact.Contact = { name: { fullName: fullName }, phoneNumbers: [{ phoneNumber: phoneNumber }] } contact.addContact(context, contactInfo, async (err: BusinessError, data) => { if (err) { console.error('Adding Error:', err.code); promptAction.showToast({ message: 'Adding Error: ' + err.code, duration: 3000 }); return; } let valueBucket: relationalStore.ValuesBucket = { 'id': data, 'key': data, 'name': fullName, 'phone': phoneNumber }; await rdbStore.insert('contacts', valueBucket) promptAction.showToast({ message: 'Contact added successfully', duration: 3000 }); }) } catch (error) { console.error('Unexpected error in addContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while adding contact', duration: 3000 }); } } export async function deleteContact(context: common.UIAbilityContext, key: string) { try { contact.deleteContact(context, key); promptAction.showToast({ message: 'Contact deleted successfully', duration: 3000 }); } catch (error) { console.error('Unexpected error in deleteContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while deleting contact', duration: 3000 }); } } export async function updateContact(context: common.UIAbilityContext, id: number, fullName: string, phoneNumber: string) { try { let newContactInfo: contact.Contact = { name: { fullName: fullName }, phoneNumbers: [{ phoneNumber: phoneNumber }], id: id } contact.updateContact(context, newContactInfo); let predicates = new relationalStore.RdbPredicates('contacts'); predicates.equalTo('id', id); let valueBucket: relationalStore.ValuesBucket = { 'name': fullName, 'phone': phoneNumber }; await rdbStore.update(valueBucket, predicates); promptAction.showToast({ message: 'Contact updated successfully', duration: 3000 }); } catch (error) { console.error('Unexpected error in updateContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while updating contact', duration: 3000 }); } } export async function getContacts(context: common.UIAbilityContext): Promise<Contact[]> { return new Promise((resolve, reject) => { try { contact.queryContacts(context, (err: BusinessError, data: contact.Contact[]) => { if (err) { console.error('Query contacts error:', err.code); reject(err); return; } if (!data || data.length === 0) { resolve([]); return; } const contactsList: Contact[] = []; data.forEach((contactItem) => { try { contactsList.push({ id: contactItem.id || 0, key: contactItem.key || '', name: contactItem.name?.fullName || 'Unknown Name', phone: contactItem.phoneNumbers?.[0]?.phoneNumber || 'No Phone' }); } catch (itemError) { console.warn('Skipping invalid contact item:', itemError); } }); resolve(contactsList); }) } catch (error) { console.error('Unexpected error in getContacts:', error); promptAction.showToast({ message: 'Error: ' + (error as BusinessError).code, duration: 3000 }); reject(error); } }) } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import { contact } from '@kit.ContactsKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; import {Contact} from '../types/Contact'; import {rdbStore} from '../entryability/EntryAbility' export async function addContact(context: common.UIAbilityContext, fullName: string, phoneNumber: string) { try { let contactInfo: contact.Contact = { name: { fullName: fullName }, phoneNumbers: [{ phoneNumber: phoneNumber }] } contact.addContact(context, contactInfo, async (err: BusinessError, data) => { if (err) { console.error('Adding Error:', err.code); promptAction.showToast({ message: 'Adding Error: ' + err.code, duration: 3000 }); return; } let valueBucket: relationalStore.ValuesBucket = { 'id': data, 'key': data, 'name': fullName, 'phone': phoneNumber }; await rdbStore.insert('contacts', valueBucket) promptAction.showToast({ message: 'Contact added successfully', duration: 3000 }); }) } catch (error) { console.error('Unexpected error in addContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while adding contact', duration: 3000 }); } } export async function deleteContact(context: common.UIAbilityContext, key: string) { try { contact.deleteContact(context, key); promptAction.showToast({ message: 'Contact deleted successfully', duration: 3000 }); } catch (error) { console.error('Unexpected error in deleteContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while deleting contact', duration: 3000 }); } } export async function updateContact(context: common.UIAbilityContext, id: number, fullName: string, phoneNumber: string) { try { let newContactInfo: contact.Contact = { name: { fullName: fullName }, phoneNumbers: [{ phoneNumber: phoneNumber }], id: id } contact.updateContact(context, newContactInfo); let predicates = new relationalStore.RdbPredicates('contacts'); predicates.equalTo('id', id); let valueBucket: relationalStore.ValuesBucket = { 'name': fullName, 'phone': phoneNumber }; await rdbStore.update(valueBucket, predicates); promptAction.showToast({ message: 'Contact updated successfully', duration: 3000 }); } catch (error) { console.error('Unexpected error in updateContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while updating contact', duration: 3000 }); } } export async function getContacts(context: common.UIAbilityContext): Promise<Contact[]> { return new Promise((resolve, reject) => { try { contact.queryContacts(context, (err: BusinessError, data: contact.Contact[]) => { if (err) { console.error('Query contacts error:', err.code); reject(err); return; } if (!data || data.length === 0) { resolve([]); return; } const contactsList: Contact[] = []; data.forEach((contactItem) => { try { contactsList.push({ id: contactItem.id || 0, key: contactItem.key || '', name: contactItem.name?.fullName || 'Unknown Name', phone: contactItem.phoneNumbers?.[0]?.phoneNumber || 'No Phone' }); } catch (itemError) { console.warn('Skipping invalid contact item:', itemError); } }); resolve(contactsList); }) } catch (error) { console.error('Unexpected error in getContacts:', error); promptAction.showToast({ message: 'Error: ' + (error as BusinessError).code, duration: 3000 }); reject(error); } }) } COMMAND_BLOCK: import { contact } from '@kit.ContactsKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; import {Contact} from '../types/Contact'; import {rdbStore} from '../entryability/EntryAbility' export async function addContact(context: common.UIAbilityContext, fullName: string, phoneNumber: string) { try { let contactInfo: contact.Contact = { name: { fullName: fullName }, phoneNumbers: [{ phoneNumber: phoneNumber }] } contact.addContact(context, contactInfo, async (err: BusinessError, data) => { if (err) { console.error('Adding Error:', err.code); promptAction.showToast({ message: 'Adding Error: ' + err.code, duration: 3000 }); return; } let valueBucket: relationalStore.ValuesBucket = { 'id': data, 'key': data, 'name': fullName, 'phone': phoneNumber }; await rdbStore.insert('contacts', valueBucket) promptAction.showToast({ message: 'Contact added successfully', duration: 3000 }); }) } catch (error) { console.error('Unexpected error in addContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while adding contact', duration: 3000 }); } } export async function deleteContact(context: common.UIAbilityContext, key: string) { try { contact.deleteContact(context, key); promptAction.showToast({ message: 'Contact deleted successfully', duration: 3000 }); } catch (error) { console.error('Unexpected error in deleteContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while deleting contact', duration: 3000 }); } } export async function updateContact(context: common.UIAbilityContext, id: number, fullName: string, phoneNumber: string) { try { let newContactInfo: contact.Contact = { name: { fullName: fullName }, phoneNumbers: [{ phoneNumber: phoneNumber }], id: id } contact.updateContact(context, newContactInfo); let predicates = new relationalStore.RdbPredicates('contacts'); predicates.equalTo('id', id); let valueBucket: relationalStore.ValuesBucket = { 'name': fullName, 'phone': phoneNumber }; await rdbStore.update(valueBucket, predicates); promptAction.showToast({ message: 'Contact updated successfully', duration: 3000 }); } catch (error) { console.error('Unexpected error in updateContact:', error); promptAction.showToast({ message: 'Unexpected error occurred while updating contact', duration: 3000 }); } } export async function getContacts(context: common.UIAbilityContext): Promise<Contact[]> { return new Promise((resolve, reject) => { try { contact.queryContacts(context, (err: BusinessError, data: contact.Contact[]) => { if (err) { console.error('Query contacts error:', err.code); reject(err); return; } if (!data || data.length === 0) { resolve([]); return; } const contactsList: Contact[] = []; data.forEach((contactItem) => { try { contactsList.push({ id: contactItem.id || 0, key: contactItem.key || '', name: contactItem.name?.fullName || 'Unknown Name', phone: contactItem.phoneNumbers?.[0]?.phoneNumber || 'No Phone' }); } catch (itemError) { console.warn('Skipping invalid contact item:', itemError); } }); resolve(contactsList); }) } catch (error) { console.error('Unexpected error in getContacts:', error); promptAction.showToast({ message: 'Error: ' + (error as BusinessError).code, duration: 3000 }); reject(error); } }) } COMMAND_BLOCK: import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; import {Group} from '../types/Group'; import { Contact } from '../types/Contact'; import {rdbStore} from '../entryability/EntryAbility' export async function getGroups(): Promise<Group[]> { const groups: Group[] = []; try { const predicates = new relationalStore.RdbPredicates('groups'); const resultSet = await rdbStore.query(predicates, ['id', 'name']); while (resultSet.goToNextRow()) { groups.push({ id: resultSet.getLong(resultSet.getColumnIndex('id')), name: resultSet.getString(resultSet.getColumnIndex('name')) }); } resultSet.close(); } catch (error) { console.error(`Get groups failed, code: ${error.code}, message: ${error.message}`); } return groups; } export async function addGroup(name: string){ if (!name || name.trim().length === 0) { throw new Error('Name cannot be empty'); } try { const valueBucket: relationalStore.ValuesBucket = { 'name': name.trim() }; await rdbStore.insert('groups', valueBucket); } catch (error) { throw new Error(error); } } export async function updateGroup(groupId: number, newGroupName: string) { if (!groupId || groupId <= 0) { throw new Error('Invalid Id'); } if (!newGroupName || newGroupName.trim().length === 0) { throw new Error('Group name cannot be empty'); } const trimmedName = newGroupName.trim(); try { const predicatesUpdate = new relationalStore.RdbPredicates('groups'); predicatesUpdate.equalTo('id', groupId); const valueBucket: relationalStore.ValuesBucket = { 'name': trimmedName }; await rdbStore.update(valueBucket, predicatesUpdate); } catch(error) { throw new Error(error); } } export async function deleteGroup(groupId: number) { if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const predicates = new relationalStore.RdbPredicates('groups'); predicates.equalTo('id', groupId); await rdbStore.delete(predicates); } catch (error) { throw new Error(error); } } export async function addContactToGroup(contactId: number, groupId: number) { if (!contactId || contactId <= 0) { throw new Error('Invalid contact ID'); } if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const valueBucket: relationalStore.ValuesBucket = { 'contact_id': contactId, 'group_id': groupId }; await rdbStore.insert('contact_groups', valueBucket); } catch (error) { const err = error as BusinessError; if (err.code === 2067) { throw new Error('This contact is already in the group!'); } if (err.code === 2069) { throw new Error('Contact or group not found!'); } console.error(`Add contact to group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to add contact to group: ${err.message}`); } } export async function removeContactFromGroup(contactId: number, groupId: number) { if (!contactId || contactId <= 0) { throw new Error('Invalid contact ID'); } if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const predicates = new relationalStore.RdbPredicates('contact_groups'); predicates.equalTo('contact_id', contactId); predicates.equalTo('group_id', groupId); const affectedRows = await rdbStore.delete(predicates); if (affectedRows === 0) { throw new Error('Contact is not in this group!'); } } catch (error) { const err = error as BusinessError; console.error(`Remove contact from group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to remove contact from group: ${err.message}`); } } export async function getContactsInGroup(groupId: number): Promise<Contact[]> { const contacts: Contact[] = []; let resultSet: relationalStore.ResultSet | null = null; try { const query = ` SELECT c.* FROM contacts c INNER JOIN contact_groups cg ON c.id = cg.contact_id WHERE cg.group_id = ? `; resultSet = await rdbStore.querySql(query, [groupId]) as relationalStore.ResultSet; while (resultSet.goToNextRow()) { contacts.push({ id: resultSet.getLong(resultSet.getColumnIndex('id')), key: resultSet.getString(resultSet.getColumnIndex('key')), name: resultSet.getString(resultSet.getColumnIndex('name')), phone: resultSet.getString(resultSet.getColumnIndex('phone')) }); } return contacts; } catch (error) { const err = error as BusinessError; console.error(`Get contacts in group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to get contacts in group: ${err.message}`); } finally { if (resultSet !== null) { resultSet.close(); } } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; import {Group} from '../types/Group'; import { Contact } from '../types/Contact'; import {rdbStore} from '../entryability/EntryAbility' export async function getGroups(): Promise<Group[]> { const groups: Group[] = []; try { const predicates = new relationalStore.RdbPredicates('groups'); const resultSet = await rdbStore.query(predicates, ['id', 'name']); while (resultSet.goToNextRow()) { groups.push({ id: resultSet.getLong(resultSet.getColumnIndex('id')), name: resultSet.getString(resultSet.getColumnIndex('name')) }); } resultSet.close(); } catch (error) { console.error(`Get groups failed, code: ${error.code}, message: ${error.message}`); } return groups; } export async function addGroup(name: string){ if (!name || name.trim().length === 0) { throw new Error('Name cannot be empty'); } try { const valueBucket: relationalStore.ValuesBucket = { 'name': name.trim() }; await rdbStore.insert('groups', valueBucket); } catch (error) { throw new Error(error); } } export async function updateGroup(groupId: number, newGroupName: string) { if (!groupId || groupId <= 0) { throw new Error('Invalid Id'); } if (!newGroupName || newGroupName.trim().length === 0) { throw new Error('Group name cannot be empty'); } const trimmedName = newGroupName.trim(); try { const predicatesUpdate = new relationalStore.RdbPredicates('groups'); predicatesUpdate.equalTo('id', groupId); const valueBucket: relationalStore.ValuesBucket = { 'name': trimmedName }; await rdbStore.update(valueBucket, predicatesUpdate); } catch(error) { throw new Error(error); } } export async function deleteGroup(groupId: number) { if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const predicates = new relationalStore.RdbPredicates('groups'); predicates.equalTo('id', groupId); await rdbStore.delete(predicates); } catch (error) { throw new Error(error); } } export async function addContactToGroup(contactId: number, groupId: number) { if (!contactId || contactId <= 0) { throw new Error('Invalid contact ID'); } if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const valueBucket: relationalStore.ValuesBucket = { 'contact_id': contactId, 'group_id': groupId }; await rdbStore.insert('contact_groups', valueBucket); } catch (error) { const err = error as BusinessError; if (err.code === 2067) { throw new Error('This contact is already in the group!'); } if (err.code === 2069) { throw new Error('Contact or group not found!'); } console.error(`Add contact to group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to add contact to group: ${err.message}`); } } export async function removeContactFromGroup(contactId: number, groupId: number) { if (!contactId || contactId <= 0) { throw new Error('Invalid contact ID'); } if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const predicates = new relationalStore.RdbPredicates('contact_groups'); predicates.equalTo('contact_id', contactId); predicates.equalTo('group_id', groupId); const affectedRows = await rdbStore.delete(predicates); if (affectedRows === 0) { throw new Error('Contact is not in this group!'); } } catch (error) { const err = error as BusinessError; console.error(`Remove contact from group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to remove contact from group: ${err.message}`); } } export async function getContactsInGroup(groupId: number): Promise<Contact[]> { const contacts: Contact[] = []; let resultSet: relationalStore.ResultSet | null = null; try { const query = ` SELECT c.* FROM contacts c INNER JOIN contact_groups cg ON c.id = cg.contact_id WHERE cg.group_id = ? `; resultSet = await rdbStore.querySql(query, [groupId]) as relationalStore.ResultSet; while (resultSet.goToNextRow()) { contacts.push({ id: resultSet.getLong(resultSet.getColumnIndex('id')), key: resultSet.getString(resultSet.getColumnIndex('key')), name: resultSet.getString(resultSet.getColumnIndex('name')), phone: resultSet.getString(resultSet.getColumnIndex('phone')) }); } return contacts; } catch (error) { const err = error as BusinessError; console.error(`Get contacts in group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to get contacts in group: ${err.message}`); } finally { if (resultSet !== null) { resultSet.close(); } } } COMMAND_BLOCK: import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; import relationalStore from '@ohos.data.relationalStore'; import {Group} from '../types/Group'; import { Contact } from '../types/Contact'; import {rdbStore} from '../entryability/EntryAbility' export async function getGroups(): Promise<Group[]> { const groups: Group[] = []; try { const predicates = new relationalStore.RdbPredicates('groups'); const resultSet = await rdbStore.query(predicates, ['id', 'name']); while (resultSet.goToNextRow()) { groups.push({ id: resultSet.getLong(resultSet.getColumnIndex('id')), name: resultSet.getString(resultSet.getColumnIndex('name')) }); } resultSet.close(); } catch (error) { console.error(`Get groups failed, code: ${error.code}, message: ${error.message}`); } return groups; } export async function addGroup(name: string){ if (!name || name.trim().length === 0) { throw new Error('Name cannot be empty'); } try { const valueBucket: relationalStore.ValuesBucket = { 'name': name.trim() }; await rdbStore.insert('groups', valueBucket); } catch (error) { throw new Error(error); } } export async function updateGroup(groupId: number, newGroupName: string) { if (!groupId || groupId <= 0) { throw new Error('Invalid Id'); } if (!newGroupName || newGroupName.trim().length === 0) { throw new Error('Group name cannot be empty'); } const trimmedName = newGroupName.trim(); try { const predicatesUpdate = new relationalStore.RdbPredicates('groups'); predicatesUpdate.equalTo('id', groupId); const valueBucket: relationalStore.ValuesBucket = { 'name': trimmedName }; await rdbStore.update(valueBucket, predicatesUpdate); } catch(error) { throw new Error(error); } } export async function deleteGroup(groupId: number) { if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const predicates = new relationalStore.RdbPredicates('groups'); predicates.equalTo('id', groupId); await rdbStore.delete(predicates); } catch (error) { throw new Error(error); } } export async function addContactToGroup(contactId: number, groupId: number) { if (!contactId || contactId <= 0) { throw new Error('Invalid contact ID'); } if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const valueBucket: relationalStore.ValuesBucket = { 'contact_id': contactId, 'group_id': groupId }; await rdbStore.insert('contact_groups', valueBucket); } catch (error) { const err = error as BusinessError; if (err.code === 2067) { throw new Error('This contact is already in the group!'); } if (err.code === 2069) { throw new Error('Contact or group not found!'); } console.error(`Add contact to group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to add contact to group: ${err.message}`); } } export async function removeContactFromGroup(contactId: number, groupId: number) { if (!contactId || contactId <= 0) { throw new Error('Invalid contact ID'); } if (!groupId || groupId <= 0) { throw new Error('Invalid group ID'); } try { const predicates = new relationalStore.RdbPredicates('contact_groups'); predicates.equalTo('contact_id', contactId); predicates.equalTo('group_id', groupId); const affectedRows = await rdbStore.delete(predicates); if (affectedRows === 0) { throw new Error('Contact is not in this group!'); } } catch (error) { const err = error as BusinessError; console.error(`Remove contact from group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to remove contact from group: ${err.message}`); } } export async function getContactsInGroup(groupId: number): Promise<Contact[]> { const contacts: Contact[] = []; let resultSet: relationalStore.ResultSet | null = null; try { const query = ` SELECT c.* FROM contacts c INNER JOIN contact_groups cg ON c.id = cg.contact_id WHERE cg.group_id = ? `; resultSet = await rdbStore.querySql(query, [groupId]) as relationalStore.ResultSet; while (resultSet.goToNextRow()) { contacts.push({ id: resultSet.getLong(resultSet.getColumnIndex('id')), key: resultSet.getString(resultSet.getColumnIndex('key')), name: resultSet.getString(resultSet.getColumnIndex('name')), phone: resultSet.getString(resultSet.getColumnIndex('phone')) }); } return contacts; } catch (error) { const err = error as BusinessError; console.error(`Get contacts in group failed, code: ${err.code}, message: ${err.message}`); throw new Error(`Failed to get contacts in group: ${err.message}`); } finally { if (resultSet !== null) { resultSet.close(); } } } - Define entities such as users, products, or transactions as separate tables. - Establish relationships between these entities (e.g., one-to-many or many-to-many). - Perform advanced operations locally, such as filtering, aggregating, and joining data across related tables. - Import the RDB Store into the project - Create three interrelated tables named contacts, groups, and contact_groups - Implement the corresponding CRUD operations