Android 数据库存储

Android 数据持久化学习笔记 —— 数据库存储

安卓系统中内置了一个轻量级的关系型数据库 SQLite,对应的数据库管理类为 SQLiteDatabase。

SQLiteDatabase 数据库管理类

Context 和 SQLiteDatabase 都提供了创建和打开数据库的方法,但两个类中的同名方法所需参数不完全相同,以 openOrCreateDatabase() 为例。

// Context 提供的 openOrCreateDatabase(String name, int mode, CursorFactory factory)
SQLiteDatabase dbContext = openOrCreateDatabase(getFilesDir() + "/test.db", MODE_PRIVATE, null);

// SQLiteDatabase 提供的 openOrCreateDatabase(String path, CursorFactory factory)
SQLiteDatabase dbSQLiteDatabase = SQLiteDatabase.openOrCreateDatabase(getFilesDir() + "/test.db", null);

第一个参数都是数据库的完整路径(包括数据库名,以 .db 为文件后缀),这里使用的 getFilesDir() 方法会得到路径为 /data/data/<package>/files/ 的 File 对象,使用字符串拼接组成完整数据库路径。Context 提供的方法多了一个参数为操作模式,与 SharedPreferences 一样默认使用 MODE_PRIVATE,其他模式均已弃用。最后一个参数都是 CursorFactory 对象,用于在查询数据时返回一个自定义的 Cursor,这里传入 null 即可。

SQLiteDatabase 中的方法主要有三类,以部分方法简单介绍。

  1. 管理类
    • openOrCreateDatabase():打开或创建数据库。
    • isOpen():判断数据库是否已打开。
    • close():关闭数据库。
    • setVersion():设置数据库的版本号。
  2. 事务类
    • beginTransaction():开启事务。
    • setTransactionSuccessful():设置事务成功标志。
    • endTransaction():结束事务。
  3. 数据处理类
    • execSQL():执行拼接好的 SQL 控制语句。可以进行增删改操作, 但不能进行查询操作。
    • insert():插入一条记录。
    • delete():删除符合条件的记录。
    • update():更新符合条件的记录。
    • query():执行查询操作,返回结果集的游标。
    • rawQuery():执行拼接好的 SQL 查询语句,返回结果集的游标。

SQLiteOpenHelper 数据库帮助类

实际开发中并不是直接使用 openOrCreateDatabase() 方法获取到一个 SQLiteDatabase 实例,然后再去进行各种操作的。而是通过 Android 提供的数据库辅助工具类 SQLiteOpenHelper,对 SQLite 数据库进行合理使用。创建 SQLiteOpenHelper 对象的时候,不会真正创建数据库或者连接数据库,调用 getReadableDatabase()getWriteableDatabase() 才会触发该操作,同时数据库文件会保存到 /data/data/<package>/databases/ 中。

使用步骤:

  1. SQLiteOpenHelper 是一个抽象类,需要先创建一个继承自它的自定义类 DatabaseHelper,重写 onCreate()onUpgrade() 方法,并添加构造函数。

    public class DatabaseHelper extends SQLiteOpenHelper {
    
        public DatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
    
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    }
    
  2. 上面是创建好自定义帮助类最开始的状态,接下来就要对构造函数进行修改。

    public class DatabaseHelper extends SQLiteOpenHelper {
    
        private static final String DB_NAME = "amie.db";
        private static final int DB_VERSION = 1;
        private static volatile DatabaseHelper instance = null;
    
        private DatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }
        
        /**
         * 使用单例模式获取数据库帮助类唯一实例
         * 一般来说,一个应用有一个数据库就够了,所以这里固定了数据库名,也可根据需求自定义参数
         * 实际客户端并发量不像服务端那么大,可以不使用双重校验锁
         */
        public static DatabaseHelper getInstance(Context context) {
            if (instance == null) {
                synchronized (DatabaseHelper.class) {
                    if (instance == null) {
                        instance = new DatabaseHelper(context, DB_NAME, null, DB_VERSION);
                    }
                }
            }
            return instance;
        }
        // ...
    }
    
  3. 添加保证数据库安全的必要方法。

    想要在数据库中进行增删改查需要获取 SQLiteDatabase 对象,但实际上增删改查基于两种类型的锁(俗称读锁和写锁)。操作时应该使用具有对应锁的 SQLiteDatabase 对象,通过调用 DatabaseHelper 实例对象的 getReadableDatabase()getWritableDatabase() 方法可获取。

    public class DatabaseHelper extends SQLiteOpenHelper {
    
        private SQLiteDatabase readDB = null;
        private SQLiteDatabase writeDB = null;
    
        // ...
    
        /** 打开数据库的读连接 */
        public SQLiteDatabase openReadLink() {
            if (readDB == null || ! readDB.isOpen()) {
                readDB = instance.getReadableDatabase();
            }
            return readDB;
        }
        /** 打开数据库的写连接 */
        public SQLiteDatabase openWriteLink() {
            if (writeDB == null || !writeDB.isOpen()) {
                writeDB = instance.getWritableDatabase();
            }
            return writeDB;
        }
        /** 关闭数据库连接 */
        public void closeLink() {
            if (readDB != null && readDB.isOpen()) {
                readDB.close();
                readDB = null;
            }
            if (writeDB != null && writeDB.isOpen()) {
                writeDB.close();
                writeDB = null;
            }
        }
    }
    
  4. 完成数据库初始化等逻辑(如:建表)。

    public class DatabaseHelper extends SQLiteOpenHelper {
        // ...
        @Override
        public void onCreate(SQLiteDatabase db) {
            // 编写 SQL 建表语句
            // SQLite 数据类型是一个用来指定任何对象的数据类型的属性。
            String sql = "CREATE TABLE IF NOT EXISTS tb_user (" +
                    " _id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                    " name TEXT NOT NULL," +
                    " age INTEGER," +
                    " gander INTEGER)";
            // 执行语句完成建表
            db.execSQL(sql);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // SQLite 的局限性,只允许 重命名表 和 添加表字段,不支持 修改、删除表字段 和 添加约束。另外每次升级都只能做一个操作,如添加一个表字段,不可以添加多个。
            // 升级数据库,一定要考虑以前的所有版本,对每个版本都进行适配,因为有可能跳版本升级。降级的时候,也要考虑所有的高版本,对每个高本版都进行适配。
            // 假设新版本号为 3
            if (oldVersion == 1) { // 既要添加 phone 字段还要添加 email 字段
            String sql = "ALTER TABLE " + TABLE_USER + " ADD COLUMN phone TEXT";
            db.execSQL(sql);
            sql = "ALTER TABLE " + TABLE_USER + " ADD COLUMN email TEXT";
            db.execSQL(sql);
        } else if (oldVersion == 2) { // 只需要添加 email 字段
            String sql = "ALTER TABLE " + TABLE_USER + " ADD COLUMN email TEXT";
            db.execSQL(sql);
        }
        }
    }
    
  5. 提供对表记录进行增删改查等操作方法。

    /** 与表对应的数据类,这里为了方便将属性设置为 public */
    public class User {
        public Integer _id;
        public String name;
        public Integer age;
        public Integer gander;
    
        public User() {}
    
        public User(Integer _id, String name, Integer age, Integer gander) {
            this._id = _id;
            this.name = name;
            this.age = age;
            this.gander = gander;
        }
    }
    
    public class DatabaseHelper extends SQLiteOpenHelper {
    
        // ...
        
        public long insertUser(User user) {
            ContentValues values = new ContentValues();
            values.put("name", user.name);
            values.put("age", user.age);
            values.put("gander", user.gander);
            return writeDB.insert("tb_user", null, values);
        }
    
        public long deleteUserByName(String username) {
            // 第二个参数中的 ? 为占位符,对应第三个参数中对应位置下的值
            return writeDB.delete("tb_user", "name = ?", new String[]{username});
    
            // 要想删除所有行,将第二个参数置为 "1" 即可
            // return writeDB.delete("tb_user", "1", null);
        }
    
        public long updateUserById(User user) {
            ContentValues values = new ContentValues();
            values.put("name", user.name);
            values.put("age", user.age);
            values.put("gander", user.gander);
            return writeDB.update("tb_user", values, "_id = ?", new String[]{String.valueOf(user._id)});
        }
    
        public List<User> queryAll() {
            List<User> list = new ArrayList<>();
            // 按条件查询只需修改对应参数即可
            Cursor cursor = readDB.query("tb_user", null, null, null, null, null, null);
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    User user = new User();
                    user._id = cursor.getColumnIndex("_id");
                    user.name = cursor.getColumnIndex("name");
                    user.age = cursor.getColumnIndex("age");
                    user.gander = cursor.getColumnIndex("gander");
                    list.add(user);
                }
            }
            cursor.close();
            return list;
        }
    }
    
  6. 在需要进行数据库操作的地方获取 DatabaseHelper 实例对象,开启读写连接,调用对应方法即可,别忘了关闭连接。

    public class MainActivity extends AppCompatActivity {
    
        private DatabaseHelper databaseHelper;
    
        private void amie() {
            // 某个方法体中
            User user = new User(1, "Vastness", 22, 1);
            long rowID = databaseHelper.insertUser(user);
            databaseHelper.updateUserById(user);
            databaseHelper.deleteUserByName("Amie");
            databaseHelper.queryAll();
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            databaseHelper = DatabaseHelper.getInstance(this);
            databaseHelper.openWriteLink();
            databaseHelper.openReadLink();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            databaseHelper.closeLink();
        }
    
    }
    

事务管理

以连续插入多条数据为例,要求都成功时才插入,只有有一条插入失败,就全都不插入。

try {
    writeDB.beginTransaction();
    writeDB.insert(TABLE_USER, null, values);
    writeDB.insert(TABLE_USER, null, values);
    writeDB.insert(TABLE_USER, null, values);
    writeDB.setTransactionSuccessful();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    writeDB.endTransaction();
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿咩AmieVastness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值