【问题标题】:Should I create a new db or ship an existing db in android我应该创建一个新的数据库还是在 android 中发布一个现有的数据库
【发布时间】:2017-08-10 12:20:08
【问题描述】:

我遇到了一种情况,我必须创建一个包含大量表的数据库。当我编写创建相同代码的代码时,它变得复杂而令人困惑。我想知道,创建一个 sqlite 数据库并将其与 apk 一起发布是否是一种好方法。 您认为哪种方法更好?为什么?

PS:升级时,我将删除所有表并创建新表。没有数据被保留。

非常感谢

【问题讨论】:

  • 为什么要增加额外的复杂性,或者是您从其他地方创建/复制数据库?或许可以举一个你觉得复杂和令人困惑的结构和代码的例子。
  • 我不觉得运送一个空的但很大的数据库会增加额外的复杂性。我觉得它比编写代码来创建相同的代码要容易得多。我只是想知道,这些选项中哪个是更好的做法。

标签: android performance sqlite android-sqlite sqliteopenhelper


【解决方案1】:

如果您只为没有预定义数据的数据库创建表,您可以在需要时创建表。但是,如果您的数据库中有超过 1 MB 的数据(不是 BLOB),您应该在外部创建数据库,然后将其发送到您的 apk 中。因为插入 1 MB 数据需要时间,并且 Android 系统会抱怨“应用程序无响应”(ANR)对话框。用户不喜欢等待太久才能使用该应用程序。

您可以使用Android SQLiteAssetHelper 在您的 apk 中发送您的 SQLite 数据库。

【讨论】:

  • 如果我没有任何数据,创建数据库总是比发送数据库更好?
  • 是的,因为它使您免于维护数据库文件。至少,它会减少您的应用文件大小。
  • 您可以使用 ORM,而不是手动编写 SQLite 数据库模式。或者试试greenDAO,它会节省你很多时间。
【解决方案2】:

一些额外的/替代的方法。

我个人的方法是上述两种方法的替代方法,虽然是在应用程序中创建表的扩展,但它一直是创建类,a) 简化表创建,b) 满足有限的动态添加表和列.

它的优点是列、表和索引只有一个命名点(我认为这是在应用程序之外定义数据库的缺陷)。

它的优点是可以直接重复使用。尽管它的缺点是创建底层类需要花费时间(这对我来说是一个好处,因为我学到了很多对 Java 和 Android 都是新的东西)。

它不需要处理从资产文件夹复制/管理数据库的复杂性(你说没有,我的观察是 SO 上更频繁发生的 Android SQlite 问题之一是关于从资产文件夹复制数据库时出现问题)。

可能会被问到的一些问题,App 是如何处理删除 App 数据的?如果您保留数据库的副本以适应这种情况,则会占用额外的空间(我相信仅一个数据库加上每个表的 4k 可能约为 36k(这只是来自对单个数据库/表的观察,所以不是在任何深度进行调查))。我怀疑额外代码的空间是否会如此之多,但也许可以进行调查。

它还不能满足外键/约束或触发器 (从未使用过)。

有5个类DBColumn、DBTable、DBIndex、DBDatabase和SQLKWORDS。

SQLKWORDS 包含常用 SQL KEYWORDS 的定义,例如

public class SQLKWORD {

    public static final String SQLTABLE = " TABLE ";
    public static final String SQLCREATE = " CREATE ";
    public static final String SQLDROP = " DROP ";
    public static final String SQLINDEX = " INDEX ";
    public static final String SQLUNIQUE = " UNIQUE ";
    .....
}

DBColumn 用于定义列

这些用于逐步构建 DBDatabase,这基本上是所需的模式以及各种方法,例如actionBuildSQL 将添加任何不存在的表,免费的actionAlterSQL 用于添加新列。

例如定义一个表(4列前3构成主索引(第3个参数如果是PRIMARY INDEX的一部分,则为true,否则为false)):-

import mjt.dbcolumn.DBColumn;
import mjt.dbtable.DBTable;    
import static mjt.sqlwords.SQLKWORD.*;

public class DBCardPayeeLinkTableConstants {

    static final String CARDPAYEELINK_TABLE = "cardpayeelink";

    private static final String CARDREF_COL = "cardref";
    private static final String PAYEEREFCOL_COL = "payeeref";
    private static final String USERREF_COL = "userref";
    private static final String USAGE_COUNT_COL = "usagecount";

    // DBCOlumns
    static final DBColumn CARDREF = new DBColumn(
            CARDREF_COL,
            SQLINTEGER,
            true,
            ""
    );

    static final DBColumn PAYEEREF = new DBColumn(
            PAYEEREFCOL_COL,
            SQLINTEGER,
            true,
            ""
    );

    static final DBColumn USERREF = new DBColumn(
            USERREF_COL,
            SQLINTEGER,
            true,
            ""
    );

    static final DBColumn USAGECOUNT = new DBColumn(
            USAGE_COUNT_COL,
            SQLINTEGER,
            false,
            "0"
    );

    static final ArrayList<DBColumn> CARDPAYEELINKCOLUMNS =
            new ArrayList<>(
                    Arrays.asList(
                            CARDREF,
                            PAYEEREF,
                            USERREF,
                            USAGECOUNT
                    )
            );

    static final DBTable CARDPAYEELINKS = new DBTable(
            CARDPAYEELINK_TABLE,
            CARDPAYEELINKCOLUMNS
    );

    // define full column names i.e. table.columnname
    public static final String CARDREF_FCOL =
            CARDREF.getFullyQualifiedDBColumnName();
    public static final String PAYEEREF_FCOL =
            PAYEEREF.getFullyQualifiedDBColumnName();
    public static final String USEREF_FCOL =
            USERREF.getFullyQualifiedDBColumnName();
    public static final String USAGECOUNT_FCOL =
            USAGECOUNT.getFullyQualifiedDBColumnName();
}

有许多 DBCOlumn 构造函数,例如DBColumn(true) 将创建常用的 _id 列。从上面可以看出,方法也是多种多样的。

DBTable 获取一个 DBColumns 列表,而 DBDatabase 获取一个 DBTables 列表(如果需要,DBIndexes 本身就是一个 DBTable 和一个 DBColumns 列表)。 DBDatabase 的方法如actionAlterSQL 将所需的模式与实际的当前模式进行比较。

然后我会有一个如下的课程:-

class DBConstants {

    /*
        Define the database name
     */
    static final String DATABASENAME = "cardoniser";
    static final int DATABASEVERSION = 1;

    /*
        Define the DBDatabase instance (base schema),
        it consists of the DBTable instances which are
        comproised of DBColumns (and optionally DBindex instances)
     */
    static final DBDatabase cardonsier = new DBDatabase(DATABASENAME,
            new ArrayList<>(Arrays.asList(
                    DBCardsTableConstants.CARDS,
                    DBUsertableConstants.USERS,
                    DBPreftableConstants.PREFS,
                    DBPayeestableContants.PAYEES,
                    DBCategoriestableConstants.CATEGORIES,
                    DBCardCategoryLinkTableConstants.CARDCATEGORYLINKS,
                    DBCardPayeeLinkTableConstants.CARDPAYEELINKS,
                    DBTransactionsTableConstants.TRANSACTIONS
            ))
    );
}

在我的 DBHelper 中我有(onUpgradeonCreate 中没有做任何事情,只是调用 onExpand):-

@Override
    public void onCreate(SQLiteDatabase db) {
        usable = this.onExpand(db,false);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) {
    }

    public boolean onExpand(SQLiteDatabase db, boolean buildandexpand) {

        boolean rv = true;
        if (db == null) {
            db = instance.getWritableDatabase();
        }
        // Is the Database definition valid to use?
        if (DBConstants.cardonsier.isDBDatabaseUsable()) {
            ArrayList<String> buildsql = DBConstants.cardonsier.generateDBBuildSQL(db);
            // Anything to build
            //      will not be if database is unchanged rather than
            //      if no definitions/valid definitions as this would
            //      be detected by isDBDatabaseUsable
            if (!buildsql.isEmpty()) {
                // YES so build the Database
                DBConstants.cardonsier.actionDBBuildSQL(db);
            }
            if (buildandexpand) {
                ArrayList<String> altersql = DBConstants.cardonsier.generateDBAlterSQL(db);
                if (!altersql.isEmpty()) {
                    DBConstants.cardonsier.generateExportSchemaSQL();
                }

            }
        }
        else {
            rv = false;
        }
        return rv;
    }
    public boolean isDBUsable() {
        return usable;
    }

在我最初的活动中,它将应用任何表或行添加以及索引添加、删除或更改(删除并重新创建索引):-

    DBDAO dbdao = new DBDAO(this);
    DBHelper.getHelper(this).onExpand(dbdao.getDB(),true);

除此之外,我对每个表使用一个类(实际上是两个,因为我还有一个单独的类用于特定于表的方法)。

总而言之,几乎没有复杂性问题,对我来说,在完成基础工作后,这三个选项都更好。


另一个可以考虑的选项。

有很多 SQLite 数据库工具,例如SQLite Manager(浏览器扩展),允许您创建数据库并导出 SQL。 例如上述数据库(非常接近它)是从 SQL 管理器中检索到的:-

CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE cardcategorylink (cardref INTEGER , categoryref INTEGER , usagecount INTEGER  DEFAULT 0 , PRIMARY KEY (cardref, categoryref) );
CREATE TABLE cardpayeelink (cardref INTEGER , payeeref INTEGER , usagecount INTEGER  DEFAULT 0 , PRIMARY KEY (cardref, payeeref) );
CREATE TABLE cards (_id INTEGER  PRIMARY KEY, cardname TEXT , cardtyperef INTEGER  DEFAULT 0 , cardnumber TEXT );
CREATE TABLE categories (_id INTEGER  PRIMARY KEY, categoryname TEXT );
CREATE TABLE payees (_id INTEGER  PRIMARY KEY, payeename TEXT );
CREATE TABLE prefs (_id INTEGER  PRIMARY KEY, prefname TEXT , preftype TEXT , prefvalue TEXT , prefshortdesc TEXT , preflongdesc TEXT );
CREATE TABLE transactions (_id INTEGER  PRIMARY KEY, timestamp INTEGER  DEFAULT 0 , cardref INTEGER  DEFAULT 0 , payeeref INTEGER  DEFAULT 0 , categoryref INTEGER  DEFAULT 0 , amount REAL  DEFAULT 0.00 , flags INTEGER  DEFAULT 0 );
CREATE TABLE users (_id INTEGER  PRIMARY KEY, username TEXT , userhash TEXT , usersalt TEXT );

请注意,上面的数据库实际上是通过Android设备管理器复制到SQLite管理器中的,所以它确实是使用我采用的方法生成的)。

如果您的来源是 MySQL 或其他来源,那么我相信 PHPMyAdmin 具有类似的功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-25
    • 2015-11-26
    • 1970-01-01
    • 2010-09-15
    • 2017-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多