Android SQL数据库程序开发实践

1 Android数据库基本介绍

Android自带的数据库就是 SQLlite,由于 SQLite 是 C 和 C++ 实现的,因此 Android 在Framework 层封装了一层Java接口,使得开发人员可以更方便的操作数据库,相关类库:

  • SQLiteOpenHelper
  • SQLiteDatabase
  • Cursor
  • ContentObserver
  • SQLiteStatement

2 数据库基本类型与接口

在 Android 中使用数据库,我们通常都会使用SQLiteOpenHelper类,该类封装了 SQLite 数据库的创建与升级工作,代码大致如下:

public class DBHelper extends SQLiteOpenHelper {
    
        private static final String TAG = DBHelper.class.getSimpleName();
        private static final String DB_NAME = "study.db";
        private static final int DB_VERSION = 1;
    
        public DBHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
        }
    
        public DBHelper(Context context, DatabaseErrorHandler errorHandler) {
            super(context, DB_NAME, null, DB_VERSION, errorHandler);
        }
    
        @Override
        public void onOpen(SQLiteDatabase db) {
            super.onOpen(db);
    
            db.execSQL(SqlHolder.OPEN_FOREIGN_KEY);
        }
    
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
            sqLiteDatabase.execSQL(SqlHolder.CREATE_CLASSES);
            sqLiteDatabase.execSQL(SqlHolder.CREATE_STUDENTS);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
            switch (oldVersion) {
                case 1:{
                    //do sth
                    break;
                }
                case 2:{
                    // do sth
                    break;
                }
            }
        }
    }

3 插入数据

通过 SQLiteDAtebase 插入数据的接口为 insertWithOnConflict,它的声明如下:

public long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm)
  • table 表示要操作的表名
  • 参数2当参数3为空时将表中的字段置为null
  • 参数3 表示表中各字段的键值对
  • 参数4 插入数据发生冲突时如何解决

ContentValues的使用方式:

     ContentValues values = new ContentValues();
                values.put( "name", name);
                values.put( "phone", phone);

conflictAlgorithm 的可选值有六种,这些策略声明在 SQLiteDatabase 中:

  • CONFLICT_ABORT 当执行sql语句违反限制条件时,会停止执行当前sql语句,并将数据恢复到操作之前的状态。不过当前事务下先执行sql语句造成的数据变动并不会受到影响
  • CONFLICT_FAIL 当执行sql语句违反限制条件时,会停止执行当前sql语句,不过先前执行sql语句造成的数据变动并不会收到影响,而后面的sql语句不会执行
  • CONFLICT_IGNORE 当插入或者修改内容是违反了限制条件时,那么这次数据将不会生效,但是后面的sql语句会被继续执行
  • CONFLICT_NONE 当执行sql语句违反限制条件时,忽略该错误,继续执行后续的sql语句
  • CONFLICT_REPLACE 当执行sql语句违反了唯一性的限制原则,新的数据将会替换旧的数据
  • CONFLICT_ROLLBACK 当执行sql语句违反限制条件时,会停止执行当前sql语句,并将数据恢复到操作之前的状态。

除了使用 insertWithOnConflict 外,我们也可以在创建表的使用指定发送冲突时的行为,具体参考:


4 删除数据

原生的sql语句删除数据语法为:

    delete form table_name where column1= value1 , column2 = value2;

而使用Android封装的的delete函数为:

int delete(String table, String whereClause, String[] whereArgs);

实例:
    db.delete("students","name = ? and cls_id = ?", new String[]{"jake","2"});

5 修改数据

Android封装的修改数据的函数为:

    update(String table, ContentValues values, String whereClause, String[] whereArgs)
    updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, int conflictAlgorithm) 

  • conflictAlgorithm 与 insertWithOnConflict 中的 conflictAlgorithm意义一致。

6 查询数据

下面是 Android 中参数最为复杂的一个 query 函数:

    Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

参数分别代表如下:

  • table 操作的表明
  • columns 要获取的字段
  • selection 条件语句,where部分
  • selectionArgs where语句中的字段值
  • groupBy 分组字段
  • having groupBy 中的having部分
  • OrderBy 排序
  • limit 限制返回的数据数量

使用示例:

    Cursor c =  db.query( "person", new String[]{ "phone"}, "name=?", new String[]{name}, null, null, null);

7 Cursor

从上面的查询函数可以看到,query 函数返回了一个 Cursor,它提供了随机读写访问数据库查询结果集的接口,Cursor并不是线程安全的,因此,当多线程中访问Cursor对象时要手动实现同步,避免出现线程安全问题。

Cursor常用的函数如下:

函数名作用
getString(int index)通过字段的索引获取一个String类型的字段,通过Index获取其他类型的字段与getString类似,只是函数名不一样而已,如getInt(int index)
getColumnIndex(String columnName)根据字段名获取字段的索引值,通过索引获取字段值的效率较高。
getCount()该Cursor中有多少条数据
moveToFirst()将光标移动到第一个数据的位置
moveToLast()将光标移动到最后一个数据的位置
moveToNext()将光标移动到下一个数据的位置
isClosed()判断游标是否关闭
isLast()判断光标是否在一个一个数据的位置上,对应还有isFirst判断是否在第一个数据上
close()关闭游标,使用完Cursor之后一定要记得调用此函数

一般的游标使用示例如下:

        Cursor dataCursor = resolver.query(datauri, new String[] {
                            "data1", "mimetype" }, "raw_contact_id=?",
                            new String[] { id }, null);
    
                    while (dataCursor.moveToNext()) {
                        String data1 = dataCursor.getString(0);
                        String mimetype = dataCursor.getString(1);
                        //doSth
                    }
                    dataCursor.close();

少用 cursor.getColumnIndex

在使用 cursor 时,我们往往会如下编码:

public static final String NAME_COLUMN = "name"

...

cursor.getString(cursor.getColumnIndex(NAME_COLUMN));

实际上,这包含了两个操作,getColumnIndex 和 getString,而如果我们能直接记住列的位置,就可以减少一次操作:

public static final String NAME_COLUMN_INDEX = 3;

...

cursor.getString(NAME_COLUMN_INDEX);

8 Android 中数据库事务

使用事务的两大好处是院子提交和性能更加优越,院子提交意味着同一事物内的所有修改要么都完成要么都不完成,如果某一个修改失败了,会自动回滚到修改之前的状态,SQLite会为每一个插入、更新操作创建一个事务,并且每次更新和插入后立即提交,如果连续的操作1000次,那么这样的过程就会重复1000次,如果使用事务的话就可以很大程度上优化性能,开启事务后的操作是这样的,创建事务-->提交1000次-->提交,这样创建事务和提交就只有一次。

Android中使用事务非常简单:

            database.beginTransaction();
            try {
                //这个执行数据库事务的代码,比如插入数据
                database.setTransactionSuccessful();
            } finally{
                database.endTransaction();
            }

9 SQLite 操作与异步

Android 数据的使用就是上面介绍的几个类了,需要注意的是使用 SQLiteDatabase 和 Curosr 一定要记得关闭,特使是 Cursor 时,关于数据的操作和表的升级前面都已经讲过了,现在主要考虑一下 Android 数据操作与多线程。

SQLite 是一个内嵌的数据库,数据库服务器就寄宿在应用程序中,无须网络配置和管理,数据服务器和客户端运行在同一个进程中,减少了网络访问消耗,简化了数据库管理,保证了执行效率,但是SQLite在多线程并发和数据大小方面存在局限性,并且它的锁机制的粒度为表级锁,所以不能,也没有必要通过多线程操作数据,如果通过多线程去操作数据库,往往会发生意想不到的异常,为了保证UI的流畅性,且又不会因为数据所引发异常,可以将数据的操作放在一个独立的线程中执行,最后通过将结果通过Handler返回给UI,当然也可以使用独立的轮询线程封装一个RxJava的Scheduler来配合RxJava操作数据库。另外,Android 也提供了 AsyncQueryHandler 类来帮我们实现类似功能。

例如使用 Handler 简单的封装一个 DbCommand:

public abstract class DbCOmmand<T> {

        private static ExecutorService sExecutorService =  Executors.newSingleThreadExecutor();
    
        private final static android.os.Handler sUiHandler = new android.os.Handler(Looper.getMainLooper());
    
        public final void execute() {
            sExecutorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                postResult(doBackground());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        private void postResult(final T t) {
            sUiHandler.post(new Runnable() {
                @Override
                public void run() {
                    opPostExecute(t);
                }
            });
        }
    
        protected  void opPostExecute(T t){}
        
        protected abstract T doBackground();
    
    
    }

10 使用ORM

ORM即对象关系映射,即对sql的封装,面向对象的操作数据库,使用上有很多orm框架,如果对sql使用熟练,并且能够熟悉框架的实现原理,使用orm框架也是不错的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

村里小码农

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

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

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

打赏作者

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

抵扣说明:

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

余额充值