基于Data Ability的关系型数据库操作方法介绍
头像 lili123123 2022-05-06 11:47:00    发布
1561 浏览 17 点赞 18 收藏

1. 介绍

HarmonyOS支持应用以Ability为单位进行部署,Ability可以分为FA(Feature Ability)和PA(Particle Ability)两种类型,其中PA支持Data Ability(即Data模板),用于对应用内及应用间提供统一的数据访问抽象。使用Data Ability(以下简称"Data")有助于应用管理自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data既可用于同设备应用间数据共享,也支持跨设备应用间数据共享。Data支持的数据存储方式,包括文件数据(如文本、图片、音乐等)和结构化数据(如数据库等)。
同时,HarmonyOS提供多种数据管理能力,例如:关系型数据库、对象关系映射数据库、轻量级偏好数据库、分布式数据服务等。
Data Ability
通过本教程,您将实现基于Data Ability创建数据库服务,对外提供访问数据库的服务端接口(类PersonDataAbility),并在MainAbilitySlice中通过DataAbilityHelper与提供方的Data Ability进行通信,最后通过日志查看数据库操作结果。

2. 创建一个Data Ability

在工程中添加Empty Data Ability,用于创建数据库并提供API接口。例如,本教程创建的数据库用于存储人员信息,将创建的Empty Data Ability命名为PersonDataAbility,其操作过程为:在HUAWEI DevEco Studio"Project"窗口当前工程的主目录("entry > src > main > java > com.xxx.xxx")选择"File > New > Ability > Empty Data Ability",设置"Data Name"为PersonDataAbility并完成创建。此时,HUAWEI DevEco Studio将自动生成类PersonDataAbility及相关方法,其继承类Ability(默认实现了数据库的增/删/改/查API方法),示例代码如下:


public class PersonDataAbility extends Ability { 
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo"); 
  
    @Override 
    public void onStart(Intent intent) { 
        super.onStart(intent); 
        HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    } 
  
    @Override 
    public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) { 
        return null; 
    } 
  
    @Override 
    public int insert(Uri uri, ValuesBucket value) { 
        HiLog.info(LABEL_LOG, "PersonDataAbility insert"); 
        return 999; 
    } 
  
    @Override 
    public int delete(Uri uri, DataAbilityPredicates predicates) { 
        return 0; 
    } 
  
    @Override 
    public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) { 
        return 0; 
    } 
}

同时,HUAWEI DevEco Studio将自动在工程配置文件config.json中添加如下"data"信息:


{ 
  "permissions": [ 
    "com.huawei.codelab.DataAbilityShellProvider.PROVIDER" 
  ], 
  "name": "com.huawei.codelab.PersonDataAbility", 
  "icon": "$media:icon", 
  "description": "$string:userdataability_description", 
  "type": "data", 
  "uri": "dataability://com.huawei.codelab.PersonDataAbility" 
}

uri

3. 定义Data Ability数据库相关常量

为了方便操作,在PersonDataAbility类中定义数据库相关常量(实际编码过程中,可以单独定义常量类),包括数据库名、表名、表字段名、数据库版本号等。例如,定义一个名为person的表,其包含四列:一个唯一的身份id 、名字name 、性别gender和年龄age,示例代码如下:


public class PersonDataAbility extends Ability { 
...... 
    private static final String DB_NAME = "persondataability.db"; 
  
    private static final String DB_TAB_NAME = "person"; 
  
    private static final String DB_COLUMN_PERSON_ID = "id"; 
  
    private static final String DB_COLUMN_NAME = "name"; 
  
    private static final String DB_COLUMN_GENDER = "gender"; 
  
    private static final String DB_COLUMN_AGE = "age"; 
  
    private static final int DB_VERSION = 1; 
...... 
}

4. 创建关系型数据库

开发指导


private StoreConfig config = StoreConfig.newDefaultConfig(DB_NAME); 
  
private RdbStore rdbStore; 
  
private RdbOpenCallback rdbOpenCallback = new RdbOpenCallback() { 
    @Override 
    public void onCreate(RdbStore store) { 
        store.executeSql("create table if not exists " 
                + DB_TAB_NAME + " (" 
                + DB_COLUMN_PERSON_ID + " integer primary key, " 
                + DB_COLUMN_NAME + " text not null, " 
                + DB_COLUMN_GENDER + " text not null, " 
                + DB_COLUMN_AGE + " integer)"); 
    } 
  
    @Override 
    public void onUpgrade(RdbStore store, int oldVersion, int newVersion) { 
    } 
};

5. 初始化数据库连接

在应用启动时,系统会调用Data Ability的onStart()方法创建Data实例。在此方法中,您需要创建数据库连接,并获取连接对象,以便后续对数据库进行操作。为了避免影响应用启动速度,请勿在此方法中执行所有任务的初始化,对于非必要的耗时任务,应推迟到使用阶段执行。
在PersonDataAbility初始化的时候连接数据库,示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

6. 重写数据库操作方法

创建Data Ability时,HUAWEI DevEco Studio自动生成数据库增/删/改/查空方法,开发者按需重写相关方法即可。

DataAbilityPredicates


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

ValuesBucket


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

说明:当对多个表格进行操作时可根据path进行差异处理,且实际编码过程中还需要对操作异常进行处理,可捕获rdbStore.insert操作的异常,例如catch (DataAbilityRemoteException | IllegalStateException exception);当表格数据插入成功时,可执行DataAbilityHelper.creator(this, uri).notifyChange(uri),通知该表格数据的订阅者。


delete():删除方法。该方法接收两个参数,分别是删除的目标路径和删除条件。删除条件由类DataAbilityPredicates构建,服务端在接收到该删除条件参数之后可以从中解析出要删除的数据,然后到数据库中执行。此方法返回一个int类型的值用于标识结果。根据传入的条件删除数据库数据的示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

update():更新方法。该方法接收三个参数,分别是更新的目标路径,更新的数据值以及更新条件。您可以在ValuesBucket参数中指定要更新的数据,在DataAbilityPredicates中构建更新的条件等。此方法返回一个int类型的值用于标识结果。更新person表数据的示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

另外,开发者还可以根据需求重写batchInsert()和executeBatch()方法来执行批量操作。

7. 访问Data Ability

开发者可以通过DataAbilityHelper类来访问当前应用或其他应用提供的共享数据。DataAbilityHelper作为客户端,与提供方的Data进行通信。Data接收到请求后,执行相应的处理(如上一个章节中重写的insert等方法),并返回结果。DataAbilityHelper提供了一系列与Data Ability通信的方法。下面介绍DataAbilityHelper具体的使用步骤。

声明使用权限

权限申请字段说明


"reqPermissions": [ 
  { 
    "name": " com.huawei.codelab.DataAbilityShellProvider.PROVIDER " 
  } 
]

创建DataAbilityHelper

DataAbilityHelper为开发者提供了creator()方法来创建DataAbilityHelper实例,该方法为静态方法,有多个重载。最常见的方法是通过传入一个context对象来创建DataAbilityHelper对象。
为了方便操作,直接在MainAbilitySlice中定义DataAbilityHelper变量,并在onStart()方法中创建其实例,示例代码如下:


public class MainAbilitySlice extends AbilitySlice { 
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "MainAbilitySlice"); 
...... 
    private DataAbilityHelper databaseHelper; 
  
    @Override 
    public void onStart(Intent intent) { 
        super.onStart(intent); 
        super.setUIContent(ResourceTable.Layout_ability_main); 
        databaseHelper = DataAbilityHelper.creator(this); 
    } 
...... 
}

访问DataAbility

DataAbilityHelper为开发者提供了增、删、改、查以及批量处理等方法来操作数据库。同样的,为了方便操作,我们直接在MainAbilitySlice中定义相关的常量及数据库增、删、改、查方法,并通过系统提供的日志接口HiLog.info记录相关结果。

客户端访问数据库常量定义:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

说明:因为本示例仅需要访问本地数据库,故BASE_URI为"dataability:///com.huawei.codelab.PersonDataAbility"。

query():查询方法,其中uri为目标资源路径,columns为想要查询的字段(即数据表格具体的列),开发者的查询条件可以通过DataAbilityPredicates来构建。开发者无需编写复杂的SQL语句,仅通过调用该类中条件相关的方法,如between、equalTo、notEqualTo、groupBy、orderByAsc、beginsWith等,就可自动完成SQL语句拼接,方便用户聚焦业务操作。根据年龄区间、名字及性别等查询条件,查询数据库的示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

insert():新增方法,其中uri为目标资源路径,ValuesBucket为要新增的对象。插入一条人员信息的示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

update():更新方法,更新数据ValuesBuckett传入,更新条件由DataAbilityPredicates来构建。例如,将id为102的表格数据,姓名修改为"ZhangSanPlus",年龄修改为28,示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

delete():删除方法,删除条件由DataAbilityPredicates来构建。例如,删除id为100的数据项,示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
    DatabaseHelper databaseHelper = new DatabaseHelper(this); 
    rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
}

访问Data

8. 订阅数据变化

通常情况下,当数据库表格的内容产生变化时,需要主动通知与该表格数据相关联的进程或者应用,从而使得相关进程或者应用接收到数据变化后完成相应的处理。如前所述,对于数据提供方,当数据库表格内容变化,可以通过如下方法通知数据订阅者:


@Override 
insert/delete/update() {  // 为了方便,此处将插入、删除、更新方法写在一起 
...... 
    DataAbilityHelper.creator(this, uri).notifyChange(uri); 
...... 
}

IDataAbilityObserver


IDataAbilityObserver dataAbilityObserver; 
  
private void personDatabaseObserver() { 
    dataAbilityObserver = new IDataAbilityObserver() { 
        @Override 
        public void onChange() { 
            // 订阅者接收目标数据表格产生变化的通知,通过查询获取最新的数据 
            query(); 
        } 
    }; 
    // 根据uri指定订阅的数据表 
    databaseHelper.registerObserver(Uri.parse(BASE_URI + DATA_PATH), dataAbilityObserver); 
}

unregisterObserver

9. 测试支持

为了快速验证结果,我们在MainAbilitySlice定义了query()、insert()、update()、delete()方法,并在onStart()中调用相关方法,其示例代码如下:


@Override 
public void onStart(Intent intent) { 
    super.onStart(intent); 
    super.setUIContent(ResourceTable.Layout_ability_main); 
    databaseHelper = DataAbilityHelper.creator(this); 
    query(); 
    insert(100, "Tom", "male", 20); 
    insert(101, "Jerry", "female", 21); 
    insert(102, "Bob", "male", 22); 
    query(); // 查看插入后的结果 
    update(); 
    query(); // 查看更新后的结果 
    delete(); 
    query(); // 查看删除后的结果 
}

连接手机或者模拟器,运行该工程,通过HUAWEI DevEco Studio HiLog工具查看结果,显示如下:

基于Data Ability的关系型数据库操作方法介绍-鸿蒙开发者社区

10. 完整示例

本示例工程主要基于Data Ability创建数据库服务并对外提供访问数据库的服务端接口(类PersonDataAbility),并在MainAbilitySlice中通过DataAbilityHelper客户端,与提供方的Data进行通信,最后通过日志查看数据库操作结果。完整示例代码如下:
类PersonDataAbility(参考第2小节,通过HUAWEI DevEco Studio添加Empty Data Ability):


public class PersonDataAbility extends Ability { 
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo"); 
  
    private static final String DB_NAME = "persondataability.db"; 
  
    private static final String DB_TAB_NAME = "person"; 
  
    private static final String DB_COLUMN_PERSON_ID = "id"; 
  
    private static final String DB_COLUMN_NAME = "name"; 
  
    private static final String DB_COLUMN_GENDER = "gender"; 
  
    private static final String DB_COLUMN_AGE = "age"; 
  
    private static final int DB_VERSION = 1; 
  
    private StoreConfig config = StoreConfig.newDefaultConfig(DB_NAME); 
  
    private RdbStore rdbStore; 
  
    private RdbOpenCallback rdbOpenCallback = new RdbOpenCallback() { 
        @Override 
        public void onCreate(RdbStore store) { 
            store.executeSql("create table if not exists " 
                    + DB_TAB_NAME + " (" 
                    + DB_COLUMN_PERSON_ID + " integer primary key, " 
                    + DB_COLUMN_NAME + " text not null, " 
                    + DB_COLUMN_GENDER + " text not null, " 
                    + DB_COLUMN_AGE + " integer)"); 
        } 
  
        @Override 
        public void onUpgrade(RdbStore store, int oldVersion, int newVersion) { 
        } 
    }; 
  
    @Override 
    public void onStart(Intent intent) { 
        super.onStart(intent); 
        HiLog.info(LABEL_LOG, "PersonDataAbility onStart"); 
        DatabaseHelper databaseHelper = new DatabaseHelper(this); 
        rdbStore = databaseHelper.getRdbStore(config, DB_VERSION, rdbOpenCallback, null); 
    } 
  
    @Override 
    public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) { 
        RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, DB_TAB_NAME); 
        ResultSet resultSet = rdbStore.query(rdbPredicates, columns); 
        if (resultSet == null) { 
            HiLog.info(LABEL_LOG, "resultSet is null"); 
        } 
        return resultSet; 
    } 
  
    @Override 
    public int insert(Uri uri, ValuesBucket value) { 
        HiLog.info(LABEL_LOG, "PersonDataAbility insert"); 
        String path = uri.getLastPath(); 
        if (!"person".equals(path)) { 
            HiLog.info(LABEL_LOG, "DataAbility insert path is not matched"); 
            return -1; 
        } 
        ValuesBucket values = new ValuesBucket(); 
        values.putInteger(DB_COLUMN_PERSON_ID, value.getInteger(DB_COLUMN_PERSON_ID)); 
        values.putString(DB_COLUMN_NAME, value.getString(DB_COLUMN_NAME)); 
        values.putString(DB_COLUMN_GENDER, value.getString(DB_COLUMN_GENDER)); 
        values.putInteger(DB_COLUMN_AGE, value.getInteger(DB_COLUMN_AGE)); 
        int index = (int) rdbStore.insert(DB_TAB_NAME, values); 
        DataAbilityHelper.creator(this, uri).notifyChange(uri); 
        return index; 
    } 
  
    @Override 
    public int delete(Uri uri, DataAbilityPredicates predicates) { 
        RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, DB_TAB_NAME); 
        int index = rdbStore.delete(rdbPredicates); 
        HiLog.info(LABEL_LOG, "delete: " + index); 
        DataAbilityHelper.creator(this, uri).notifyChange(uri); 
        return index; 
    } 
  
    @Override 
    public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) { 
        RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, DB_TAB_NAME); 
        int index = rdbStore.update(value, rdbPredicates); 
        HiLog.info(LABEL_LOG, "update: " + index); 
        DataAbilityHelper.creator(this, uri).notifyChange(uri); 
        return index; 
    } 
  
    @Override 
    public FileDescriptor openFile(Uri uri, String mode) { 
        return null; 
    } 
  
    @Override 
    public String[] getFileTypes(Uri uri, String mimeTypeFilter) { 
        return new String[0]; 
    } 
  
    @Override 
    public PacMap call(String method, String arg, PacMap extras) { 
        return null; 
    } 
  
    @Override 
    public String getType(Uri uri) { 
        return null; 
    } 
}

说明:以上代码仅demo演示参考使用。

MainAbilitySlice:


public class MainAbilitySlice extends AbilitySlice { 
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "MainAbilitySlice"); 
  
    private static final String BASE_URI = "dataability:///com.huawei.codelab.PersonDataAbility"; 
  
    private static final String DATA_PATH = "/person"; 
  
    private static final String DB_COLUMN_PERSON_ID = "id"; 
  
    private static final String DB_COLUMN_NAME = "name"; 
  
    private static final String DB_COLUMN_GENDER = "gender"; 
  
    private static final String DB_COLUMN_AGE = "age"; 
  
    private DataAbilityHelper databaseHelper; 
  
    @Override 
    public void onStart(Intent intent) { 
        super.onStart(intent); 
        super.setUIContent(ResourceTable.Layout_ability_main); 
        databaseHelper = DataAbilityHelper.creator(this); 
        query(); 
        insert(100, "Tom", "male", 20); 
        insert(101, "Jerry", "female", 21); 
        insert(102, "Bob", "male", 22); 
        query(); // 查看插入后的结果 
        update(); 
        query(); // 查询更新后的结果 
        delete(); 
        query(); // 查看删除后的结果 
    } 
  
    @Override 
    public void onActive() { 
        super.onActive(); 
    } 
  
    @Override 
    public void onForeground(Intent intent) { 
        super.onForeground(intent); 
    } 
  
    private void query() { 
        String[] columns = new String[] {DB_COLUMN_PERSON_ID, 
                DB_COLUMN_NAME, DB_COLUMN_GENDER, DB_COLUMN_AGE}; 
        // 构造查询条件 
        DataAbilityPredicates predicates = new DataAbilityPredicates(); 
        predicates.between(DB_COLUMN_AGE, 15, 40); 
        try { 
            ResultSet resultSet = databaseHelper.query(Uri.parse(BASE_URI + DATA_PATH), 
                    columns, predicates); 
            if (resultSet == null || resultSet.getRowCount() == 0) { 
                HiLog.info(LABEL_LOG, "query: resultSet is null or no result found"); 
                return; 
            } 
            resultSet.goToFirstRow(); 
            do { 
                int id = resultSet.getInt(resultSet.getColumnIndexForName(DB_COLUMN_PERSON_ID)); 
                String name = resultSet.getString(resultSet.getColumnIndexForName(DB_COLUMN_NAME)); 
                String gender = resultSet.getString(resultSet.getColumnIndexForName(DB_COLUMN_GENDER)); 
                int age = resultSet.getInt(resultSet.getColumnIndexForName(DB_COLUMN_AGE)); 
                HiLog.info(LABEL_LOG, "query: Id :" + id + " Name :" + name + " Gender :" + gender + " Age :" + age); 
            } while (resultSet.goToNextRow()); 
        } catch (DataAbilityRemoteException | IllegalStateException exception) { 
            HiLog.error(LABEL_LOG, "query: dataRemote exception | illegalStateException"); 
        } 
    } 
  
    private void insert(int id, String name, String gender, int age) { 
        ValuesBucket valuesBucket = new ValuesBucket(); 
        valuesBucket.putInteger(DB_COLUMN_PERSON_ID, id); 
        valuesBucket.putString(DB_COLUMN_NAME, name); 
        valuesBucket.putString(DB_COLUMN_GENDER, gender); 
        valuesBucket.putInteger(DB_COLUMN_AGE, age); 
        try { 
            if (databaseHelper.insert(Uri.parse(BASE_URI + DATA_PATH), valuesBucket) != -1) { 
                HiLog.info(LABEL_LOG, "insert successful"); 
            } 
        } catch (DataAbilityRemoteException | IllegalStateException exception) { 
            HiLog.error(LABEL_LOG, "insert: dataRemote exception|illegalStateException"); 
        } 
    } 
  
    private void update() { 
        DataAbilityPredicates predicates = new DataAbilityPredicates(); 
        predicates.equalTo(DB_COLUMN_PERSON_ID, 102); 
        ValuesBucket valuesBucket = new ValuesBucket(); 
        valuesBucket.putString(DB_COLUMN_NAME, "ZhangSanPlus"); 
        valuesBucket.putInteger(DB_COLUMN_AGE, 28); 
        try { 
            if (databaseHelper.update(Uri.parse(BASE_URI + DATA_PATH), valuesBucket, predicates) != -1) { 
                HiLog.info(LABEL_LOG, "update successful"); 
            } 
        } catch (DataAbilityRemoteException | IllegalStateException exception) { 
            HiLog.error(LABEL_LOG, "update: dataRemote exception | illegalStateException"); 
        } 
    } 
  
    private void delete() { 
        DataAbilityPredicates predicates = new DataAbilityPredicates() 
                .equalTo(DB_COLUMN_PERSON_ID, 100); 
        try { 
            if (databaseHelper.delete(Uri.parse(BASE_URI + DATA_PATH), predicates) != -1) { 
                HiLog.info(LABEL_LOG, "delete successful"); 
            } 
        } catch (DataAbilityRemoteException | IllegalStateException exception) { 
            HiLog.error(LABEL_LOG, "delete: dataRemote exception | illegalStateException"); 
        } 
    } 
}

说明:以上代码仅demo演示参考使用。

恭喜您


您已经成功的学习了如何通过Data Ability创建关系型数据库,并提供Data端数据库表格数据的增、删、改、查API接口,然后通过创建DataAbilityHelper实例作为客户端访问数据库的数据。





©本站发布的所有内容,包括但不限于文字、图片、音频、视频、图表、标志、标识、广告、商标、商号、域名、软件、程序等,除特别标明外,均来源于网络或用户投稿,版权归原作者或原出处所有。我们致力于保护原作者版权,若涉及版权问题,请及时联系我们进行处理。
分类
其它
地址:北京市朝阳区北三环东路三元桥曙光西里甲1号第三置业A座1508室 商务内容合作QQ:2291221 电话:13391790444或(010)62178877
版权所有:电脑商情信息服务集团 北京赢邦策略咨询有限责任公司
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,我司致力于保护作者版权,如有侵权,请与我司联系删除
京ICP备:2022009079号-2
京公网安备:11010502051901号
ICP证:京B2-20230255