Android 数据存储——SQLite

栏目: 数据库 · 发布时间: 7年前

内容简介:SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、 PHP 、 Java 等,还有ODBC接口,同样比起 Mysql 、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。( 摘自百度百科 )

鉴于 SQLite 具备如此优秀的业务处理能力,Android 平台自然使用它作为内置的数据库存储模块。存储在 SD 卡上,只是一个文件的形式,可以方便的备份、移植。有兴趣的小伙伴,可以至 SQLite 官网下载 C 源码研究研究。

另外 Android 平台提供了一整套的 SQLite 操作 API。可以方便快速的创建表、更新表、对表数据进行增删改查。

1 创建数据库

创建数据库方法,分四类。

  1. create/createInMemory 在内存中创建数据库,当数据库关闭时即销毁。
  2. openDatabase 打开数据库,可以指定打开方式。
  3. openOrCreateDatabase 创建并打开数据库。
  4. getWritableDatabase/getReadableDatabase 打开读写,或只读数据库。

上述四个方式,都可以指定 CursorFactory 、 Flags 、 DatabaseErrorHandler (API 11)、 OpenParams (API 27) 四个基本参数。最终都 执行到 openDatabase 方法。

SQLite 对数据库文件添加四种访问权限,做 flags 参数:

  1. OPEN_READWRITE :读写方式打开数据库文件,可以进行增删改查等操作。
  2. OPEN_READONLY :只读方式打开数据库文件,只能执行查询操作,插入数据会抛出异常 android.database.sqlite.SQLiteReadOnlyDatabaseException 。
  3. CREATE_IF_NECESSARY :打开数据库时,如没有数据库文件,便会自动创建,并允许增删改查。
  4. NO_LOCALIZED_COLLATORS : 使 setLocale 方法不生效。
// 执行 query 方法时,使用该对象,用于生成 cursor
SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
    @Override
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            return new SQLiteCursor(masterQuery, editTable, query);
        }else
            return new SQLiteCursor(db,masterQuery, editTable, query);
    }
};
// 数据库发生异常时,执行该对象。
DatabaseErrorHandler errorHandler = new DatabaseErrorHandler() {
    @Override
    public void onCorruption(SQLiteDatabase dbObj) {
        // 做关闭数据库的操作
        dbObj.close();
    }
};
// 打开数据库的参数,API 27 才支持。
SQLiteDatabase.OpenParams openParams = new SQLiteDatabase.OpenParams.Builder()
        .setCursorFactory(factory) // 指定 CursorFactory
        .setErrorHandler(errorHandler)// 指定 DatabaseErrorHandler
        .addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY) // 指定打开权限。
        .build();
复制代码

1.1 create

直接在内存中创建数据库,create/createInMemory 两个方法,后者在 API 27 才支持,建议使用 create。

/**
 * 在内存中创建 SQLite 数据库,当数据库关闭时,即销毁。
 * 适合临时保存数据。
 *
 * @param factory 可选参数,建议传入 NULL 或者构建 factory 实例。
 * @return 返回数据库对象
 * @throws 数据库创建失败时,抛出 SQLiteException
 */
@NonNull
public static SQLiteDatabase create(@Nullable CursorFactory factory) {
    // This is a magic string with special meaning for SQLite.
    return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH,
            factory, CREATE_IF_NECESSARY);
}
/**
 * 在内存中创建 SQLite 数据库,当数据库关闭时,即销毁。
 * 适合临时保存数据。
 *
 * @param openParams 配置打开数据库的参数
 * @return 返回数据库对象
 * @throws 数据库创建失败时,抛出 SQLiteException
 */
@NonNull
public static SQLiteDatabase createInMemory(@NonNull OpenParams openParams) {
    return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH,
            openParams.toBuilder().addOpenFlags(CREATE_IF_NECESSARY).build());
}
复制代码

都执行到 openDatabase 方法,Flags = CREATE_IF_NECESSARY。

1.2 openDatabase

/**
 * 根据 flags 打开数据库
 *
 * @param path 创建或打开数据库的文件路径。可以使用相对地址或绝对地址。
 *          相对地址存在应用缓存 database 目录,绝对地址可以存在 sd 卡目录下。
 * @param factory 可选参数,建议传入 NULL 或者构建 factory 实例。
 * @param flags 控制数据的方式方式,
 * @return 返回打开新打开的数据库
 * @throws 数据库创建失败时,抛出 SQLiteException
 */
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory,
        @DatabaseOpenFlags int flags) {
    return openDatabase(path, factory, flags, null);
}

/**
 * 根据 flags 打开数据库
 *
 * @param path 打开或创建数据库的文件
 * @param factory 可选参数,建议传入 NULL 或者构建 factory 实例。
 * @param flags 控制数据库文件的访问方式
 * @param errorHandler 当 SQLite 报出数据库出错时,使用 DatabaseErrorHandler 处理错误。如关闭数据库。
 * @return 返回打开新打开的数据库
 * @throws 数据库创建失败时,抛出 SQLiteException
 */
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory,
        @DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler) {
    SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1, null,
            null);
    db.open();
    return db;
}

/**
 * 根据指定的参数,打开数据库
 *
 * @param path 创建或打开数据库的文件路径。
 *          使用绝对路径,或者通过context#getDatabasePath(String)
 * @param openParams 配置打开数据库的参数
 * @return 返回打开新打开的数据库
 * @throws 数据库创建失败时,抛出 SQLiteException
 */
public static SQLiteDatabase openDatabase(@NonNull File path,
        @NonNull OpenParams openParams) {
    return openDatabase(path.getPath(), openParams);
}

private static SQLiteDatabase openDatabase(@NonNull String path,
        @NonNull OpenParams openParams) {
    Preconditions.checkArgument(openParams != null, "OpenParams cannot be null");
    SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags,
            openParams.mCursorFactory, openParams.mErrorHandler,
            openParams.mLookasideSlotSize, openParams.mLookasideSlotCount,
            openParams.mIdleConnectionTimeout, openParams.mJournalMode, openParams.mSyncMode);
    db.open();
    return db;
}

复制代码

四个方法中,可以分为两类。

  1. 前两个分别指定 Flags、CursorFactory 和 DatabaseErrorHandler(API 11)支持。
  2. 后两个统一由 OpenParams 指定打开数据库的参数。包含但不限于上面三个属性。

1.3 openOrCreateDatabase

/**
 * 相当于 openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
 */
public static SQLiteDatabase openOrCreateDatabase(@NonNull File file,
        @Nullable CursorFactory factory) {
    return openOrCreateDatabase(file.getPath(), factory);
}

/**
 * 相当于 openDatabase(path, factory, CREATE_IF_NECESSARY).
 */
public static SQLiteDatabase openOrCreateDatabase(@NonNull String path,
        @Nullable CursorFactory factory) {
    return openDatabase(path, factory, CREATE_IF_NECESSARY, null);
}

/**
 * 相当于 openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler).
 */
public static SQLiteDatabase openOrCreateDatabase(@NonNull String path,
        @Nullable CursorFactory factory, @Nullable DatabaseErrorHandler errorHandler) {
    return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
}
复制代码

创建并打开数据库,三个方法最后都会执行到 SQLiteDatabase openDatabase(String ,CursorFactory,int,DatabaseErrorHandler) 方法。

1.4 SQLiteOpenHelper

SQLiteOpenHelper 是 Android 封装的最人性化的工具。方便开发者自行管理数据库表目录和结构。

/**
 * 管理数据库创建和版本
 */
public class DBOpenHelper extends SQLiteOpenHelper {

    // 相当于 SQLiteDatabase openDatabase(String, CursorFactory)
    public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    // 相当于 SQLiteDatabase openDatabase(String, CursorFactory, DatabaseErrorHandler)
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) {
        super(context, name, factory, version, errorHandler);
    }

    // 相当于 SQLiteDatabase openDatabase(String , OpenParams);
    @TargetApi(Build.VERSION_CODES.P)
    public DBOpenHelper(Context context, String name, int version, SQLiteDatabase.OpenParams openParams) {
        super(context, name, version, openParams);
    }

    // 创建数据文件时调用,此时适合创建新表
    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    // 更新数据库版本时调用,适合更新表结构或创建新表
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

// 生成 helper 对象,可以打开数据库文件。文件名可以是相对路径或绝对路径
DBOpenHelper dbHelper = new DBOpenHelper(this, "test.db", null, 1);
// 用读写的方式打开数据库文件
SQLiteDatabase database = dbHelper.getWritableDatabase();
复制代码

1.5 使用场景

openDatabase 适合打开已经存在的数据库文件。并且表目录和结构固定,一般只有对数据库表的常规操作。如,已有一个全国城市信息数据库文件,需要在项目中打开。

SQLiteOpenHelper 需要自己重新创建数据库文件,根据后期的需求可能需要对数据库的表目录及结构做修改。升级数据版本,在 onUpgrade 方法中处理即可。openDatabase 也可以实现这个需求,只是稍微麻烦些。

2 插入数据

插入操作方法总结为两类,insert 和 replace ,最后都执行 insertWithOnConflict 方法。

/**
 * 向数据库插入一行数据
 *
 * @param table 指定表名,插入一行数据
 * @param nullColumnHack 可选参数,建议是 null
 *            如果设置 null,将不允许向表中插入空数据,即 values = null 时无法正确执行插入操作。
 *            如果不设置 null,那么需要设置表中可以为空的属性列的名称。
 *            当 values = null 时,可以向表中插入空数据。
 *            而实际上是插入一行数据,只有属性列名 nullColumnHack 的值是 null。 
 * @param values map 集合,包含需要插入的一行数据。至少需要包含一个属性的 key 和 value。
 *            key 是属性列名称,value 是属性值。
 * @return 返回新插入的行序号, 发生错误时,返回 -1
 */
public long insert(String table, String nullColumnHack, ContentValues values) {
    try {
        return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
    } catch (SQLException e) {
        Log.e(TAG, "Error inserting " + values, e);
        return -1;
    }
}

/**
 * 向数据库插入一行数据。只是在插入操作异常时,需要手动捕获异常。
 * 参数都同上
 * @param table 
 * @param nullColumnHack 
 * @param values 
 * @throws SQLException
 * @return 
 */
public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
        throws SQLException {
    return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
}
复制代码
/**
 * 根据主键替换表中的一行数据,如果替换的主键在表中不存在,则插入一行数据。
 * 参数都同上
 * @param table
 * @param nullColumnHack
 * @param initialValues
 * @return 返回新插入的行序号, 发生错误时,返回 -1
 */
public long replace(String table, String nullColumnHack, ContentValues initialValues) {
    try {
        return insertWithOnConflict(table, nullColumnHack, initialValues,
                CONFLICT_REPLACE);
    } catch (SQLException e) {
        Log.e(TAG, "Error inserting " + initialValues, e);
        return -1;
    }
}

/**
 * 根据主键替换表中的一行数据,如果替换的主键在表中不存在,则插入一行数据。
 * 参数都同上
 * @param table
 * @param nullColumnHack
 * @param initialValues
 * @throws SQLException
 * @return 
 */
public long replaceOrThrow(String table, String nullColumnHack,
        ContentValues initialValues) throws SQLException {
    return insertWithOnConflict(table, nullColumnHack, initialValues,
            CONFLICT_REPLACE);
}
复制代码
/**
 * 向数据库中插入一行数据的通用方法。
 * 参数同上。
 * @param table
 * @param nullColumnHack
 * @param initialValues
 * @param conflictAlgorithm 解决冲突的处理算法
 * @return 返回插入数据的行序号。当 conflictAlgorithm 错误或者发生插入异常时,返回-1。
 */
public long insertWithOnConflict(String table, String nullColumnHack,
        ContentValues initialValues, int conflictAlgorithm) {
    acquireReference();
    try {
        StringBuilder sql = new StringBuilder();
        sql.append("INSERT");
        // 冲突处理算法,插入时只用第一个和最后一个。
        // {"", "OR ROLLBACK", "OR ABORT", "OR FAIL", "OR IGNORE", "OR REPLACE"};
        sql.append(CONFLICT_VALUES[conflictAlgorithm]);
        sql.append(" INTO ");
        sql.append(table);
        sql.append('(');

        Object[] bindArgs = null;
        int size = (initialValues != null && !initialValues.isEmpty())
                ? initialValues.size() : 0;
        // 拼接  sql  语句
        if (size > 0) { 
            bindArgs = new Object[size];
            int i = 0;
            for (String colName : initialValues.keySet()) {
                sql.append((i > 0) ? "," : "");
                // 拼接列名称
                sql.append(colName);
                // 保存属性值参数
                bindArgs[i++] = initialValues.get(colName);
            }
            sql.append(')');
            sql.append(" VALUES (");
            // 将所有的值用 ? 做占位符
            for (i = 0; i < size; i++) {
                sql.append((i > 0) ? ",?" : "?");
            }
        } else {
                // 插入的数据集合是空时,至少需要有一列数据,列名称是 nullColumnHack,值是 NULL
            sql.append(nullColumnHack + ") VALUES (NULL");
        }
        sql.append(')');
        // 执行插入操作
        SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
        try {
            return statement.executeInsert();
        } finally {
            statement.close();
        }
    } finally {
        releaseReference();
    }
}
复制代码

演示代码,不掩饰抛出异常的方法:

ContentValues values = new ContentValues();
    values.put("id", 1);
    values.put("name", "flueky");
    values.put("age", 27);
    // 插入完整的数据,id name age
    database.insert("user", null, values);
    // 插入空数据,将 name 设置为 null
    database.insert("user", "name", null);
    // 替换 id = 1 的数据
    values.put("id", 1);
    values.put("name", "xiaofei");
    values.put("age", 27);
    database.replace("user", null, values);
复制代码

replace 方法类似于 update 操作。

3 删除数据

删除数据库表数据,只有一个方法。支持指定删除条件

/**
 * 删除数据库中一行的方法
 *
 * @param table 需要删除的表名
 * @param whereClause 传入 null 时表示删除表中全部数据。
 *            或者指定删除条件,只会删除满足条件的行。
 * @param whereArgs 指定删除条件的值,按照顺序替换在删除条件中的 ? 。
 * @return 删除满足条件的行时,返回删除的行数。找不到满足条件删除的时候,返回 0 。
 */
public int delete(String table, String whereClause, String[] whereArgs) {
    acquireReference();
    try {
        SQLiteStatement statement =  new SQLiteStatement(this, "DELETE FROM " + table +
                (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
        try {
            return statement.executeUpdateDelete();
        } finally {
            statement.close();
        }
    } finally {
        releaseReference();
    }
}
复制代码

4 更新数据

更新表数据方法有两个,建议只用第一个, conflictAlgorithm 参数默认 CONFLICT_NONE 。

/**
 * 更新数据库表中的一行数据
 *
 * @param table 需要更新的表名
 * @param values 包含属性名和新属性值的 map 集合。
 * @param whereClause 可选的 WHERE 条件决定需要更新的行。
 *            如果是空,则更新所有的行。
 * @param whereArgs 替换在 where 条件中包含的 ? 。
 * @return 返回更新的行数
 */
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
    return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
}

/**
 * 更新数据库表中的一行数据
 * 参数同上
 *
 * @param table 
 * @param values 
 * @param whereClause 
 * @param whereArgs 
 * @param conflictAlgorithm 决定更新冲突的算法。
 * @return 
 */
public int updateWithOnConflict(String table, ContentValues values,
        String whereClause, String[] whereArgs, int conflictAlgorithm) {
    // 官方源码中说,null is valid,此处却抛出异常,有点费解        
    if (values == null || values.isEmpty()) {
        throw new IllegalArgumentException("Empty values");
    }

    acquireReference();
    try {
        // 拼接更新的 sql 语句
        StringBuilder sql = new StringBuilder(120);
        sql.append("UPDATE ");
        sql.append(CONFLICT_VALUES[conflictAlgorithm]);
        sql.append(table);
        sql.append(" SET ");

        // 将 values 和 whereArgs 值拼接在一起
        int setValuesSize = values.size();
        int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
        Object[] bindArgs = new Object[bindArgsSize];
        int i = 0;
        // 拼接需要更新的列属性
        for (String colName : values.keySet()) {
            sql.append((i > 0) ? "," : "");
            sql.append(colName);
            // 保存属性值
            bindArgs[i++] = values.get(colName);
            sql.append("=?");
        }
        // 添加 where 条件的值
        if (whereArgs != null) {
            for (i = setValuesSize; i < bindArgsSize; i++) {
                bindArgs[i] = whereArgs[i - setValuesSize];
            }
        }
        if (!TextUtils.isEmpty(whereClause)) {
            sql.append(" WHERE ");
            sql.append(whereClause);
        }

        SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
        try {
            // 执行 sql 语句
            return statement.executeUpdateDelete();
        } finally {
            statement.close();
        }
    } finally {
        releaseReference();
    }
}
复制代码

5 查询数据

Android 给数据库的表查询操作提供了丰富的 API,一共10个方法,去除重载方法,共四个。下表中列出,四个方法和使用的参数。

  1. ● 表示必传参数
  2. ○ 表示可传参数
  3. - 表示不用的参数
参数 query queryWithFactory rawQuery rawQueryWithFactory
String table ● ● - -
String[] columns ● ● - -
String selection ● ● - -
String[] selectionArgs ● ● ● ●
String groupBy ● ● - -
String having ● ● - -
String orderBy ● ● - -
String limit ○ ● - -
boolean distinct ○ ● - -
CancellationSignal cancellationSignal ○ ○ ○ ○
CursorFactory cursorFactory - ● - ●
String sql - - ● ●
String editTable - - - ●

参数说明:

  1. table 需要查询的表名。
  2. columns 列出需要查询的列。传递 null 将返回所有的列。不建议这样做,防止从不用的存储中读取数据。
  3. selection 声明需要返回表数据的过滤器,同 where 条件子句格式一样(不包含 where 自身)。传入 null 将返回表中所有的数据。
  4. selectionArgs 替换在 selection 中使用的 ? 或 sql 中使用的 ? 。
  5. groupBy 申明返回的表数据的分组规则,同 GROUP BY 子句一样(不包含 GROUP BY )传入 null 将不分组。
  6. having 决定哪些分组会包含在 cursor 中。如果使用了分组条件将按照 HAVING 子句(不包含 HAVING )去格式化分组。传入 null 会将所有的分组都包含在 cursor 中。且不使用分组时,必须用 null 。
  7. orderBy 对表数据进行排序。同 ORDER BY 的条件语句一样(不包含 ORDER BY )。传入 null 将不排序。
  8. limit 限定查询数据的条数。同 LIMIT 子句。传入 null 不限定查询条数。
  9. distinct 如果想每行数据不一样,用 true ,否则用 false 。
  10. cancellationSignal 取消查询进程中的操作信号。如果不需要手动取消,使用 null 。如果操作被取消会抛出 OperationCanceledException 。
  11. cursorFactory 生成 Cursor 对象,如果是 null ,则使用打开数据库时使用的 cursorFactory 。如果打开数据库时也使用 null ,那么自动生成 Cursor 。
  12. sql 所有的 query 方法,最后都会合并出 sql 执行 rawquery 方法。
  13. editTable 指定需要编辑的表名。
/**
 * 最简单也最常用的 query 方法。最后拼接好 sql 语句,去执行 rawQuery 方法。
 */
public Cursor query(String table, String[] columns, String selection,
        String[] selectionArgs, String groupBy, String having,
        String orderBy) {

    return query(false, table, columns, selection, selectionArgs, groupBy,
            having, orderBy, null /* limit */);
}

/**
 * 参数最全的 query 方法,所有的参数可以手动指定,或使用 null。除 table。
 */
public Cursor queryWithFactory(CursorFactory cursorFactory,
        boolean distinct, String table, String[] columns,
        String selection, String[] selectionArgs, String groupBy,
        String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
    acquireReference();
    try {
        String sql = SQLiteQueryBuilder.buildQueryString(
                distinct, table, columns, selection, groupBy, having, orderBy, limit);

        return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
                findEditTable(table), cancellationSignal);
    } finally {
        releaseReference();
    }
}

/**
 * 最简单也最常用的 rawQuery 方法。可以自定义好查询的 sql 语句后,自主查询。适用于复杂的关系表关联查询。
 */
public Cursor rawQuery(String sql, String[] selectionArgs) {
    return rawQueryWithFactory(null, sql, selectionArgs, null, null);
}

/**
 * 参数最全的 rawQuery 方法,使用场景不多。
 */
public Cursor rawQueryWithFactory(
        CursorFactory cursorFactory, String sql, String[] selectionArgs,
        String editTable, CancellationSignal cancellationSignal) {
    acquireReference();
    try {
        SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
                cancellationSignal);
        return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
                selectionArgs);
    } finally {
        releaseReference();
    }
}
复制代码

6 事务

事务( Transaction )是一个对数据库执行工作单元。事务( Transaction )是以逻辑顺序完成的工作单位或序列,可以是由用户手动操作完成,也可以是由某种数据库程序自动完成。

Android 中, SQLite 事务主要提供了三个 API。在结束事务前,如想将事务提交到数据库,需要设置事务完成标记。否则,在事务开启时候做的数据库操作将不会保留。

database.beginTransaction();
/* 此处执行数据库操作 */
database.setTransactionSuccessful();
database.endTransaction();
复制代码

7 结束语

本文到此,已经介绍了 SQLite 常用的操作。如想自定义 sql 语句实现更丰富的数据库操作,使用 database.execSQL 方法。使用自定义 sql 查询数据库请使用 database.rawQuery 方法。

示例代码

为避免篇幅过长, 暂时介绍到这里。关于 SQLite 更多的操作语句和用法,请见后续文章。

觉得有用?那打赏一个呗。[去打赏]({{ site.url }}/donate/)

此处是广告 : Flueky的技术小站


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

疯狂Java讲义

疯狂Java讲义

李刚 / 电子工业出版社 / 2012-1-1 / 109.00元

《疯狂Java讲义(附光盘第2版)》是《疯狂Java讲义》的第2版,第2版保持了第1版系统、全面、讲解浅显、细致的特性,全面介绍了新增的Java 7的新特性。 《疯狂Java讲义(附光盘第2版)》深入介绍了Java编程的相关方面,全书内容覆盖了Java的基本语法结构、Java的面向对象特征、Java集合框架体系、Java泛型、异常处理、Java GUI编程、JDBC数据库编程、Java注释、......一起来看看 《疯狂Java讲义》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

MD5 加密
MD5 加密

MD5 加密工具