Android 数据存储(三)SQLite Databases

最近项目需要用到涉及数据库SQLite的知识,真正用的时候才发现自己一点都不熟悉。所以打算将其使用方法总结一下,方便自己以后复习。

SQLiteDatabase



SQLiteDatabase类用来管理SQLite数据库。它有方法可以创建,删除,执行SQL命令,并执行其它常见的数据库管理任务。


1 创建 打开数据库

SQLiteDatabase类中提供了5 个static方法用来打开一个文件对应的数据库。

//openDatabase方法打开path文件对应的数据库。  
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags)  
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,DatabaseErrorHandler errorHandler)  
//openOrCreateDatabase 如果不存在则先创建再打开数据库,如果存在则直接打开。  
public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory)  
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory)  
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,DatabaseErrorHandler errorHandler)

通过文件path 或者file 创建SQLiteDatabase对象。通过此对象就可以操作数据库了。

//创建打开数据库。  
SQLiteDatabase mTestDb = SQLiteDatabase.
  openOrCreateDatabase("/data/data/cn.vn.sqlitedatademo/databases/my.db", null);  

要保证文件路径是已存在,(例如没有databases文件夹,则会报错android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database)

Activity的父类(不是直接父类)ContextWrapper.Java实现了Context.java中的如下两个抽象方法:
SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory)
SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler)
所以可以在Activity中直接调用这两个方法来创建数据库,或者通过它的context来创建数据库。


2 创建表

创建一张表,表名user_info,列为 _id(主键并且自动增加)、name(用户名)、pwd(密码)、modifyTime(修改时间)。

mTestDb.execSQL("CREATE TABLE IF NOT EXISTS user_info(_id INTEGER PRIMARY KEY AUTOINCREMENT, 
  name TEXT,pwd TEXT,modifyTime INTEGER)")

3 添加数据

public static void InsertTest(){  
    UserBean user = new UserBean();  
    user.setName("xiaopihai");  
    user.setPwd("12345678");  
    user.setModifyTime(System.currentTimeMillis());  
    //增加一条数据,INSERT INTO user_info VALUES()因为有4列,所以需要写4项数据,否则会失败,  
    //第一个数据位null,这是因为它是自动增长的。.  
    mTestDb.execSQL("INSERT INTO user_info VALUES(null,'xiaopihai','12345678',"+user.getModifyTime()+")");  
    //不可以为主键设置数据,因为它是唯一的,不可以与别的相同。否则会报错  
    //SQLiteConstraintException: PRIMARY KEY must be unique (code 19)  
    //mTestDb.execSQL("INSERT INTO user_info VALUES(12,'zhangsan','mima1111',"+System.currentTimeMillis()+")");  
    //下面是增加一条数据(只设置某个或某几个内容),默认没添加的数据时空的。  
    mTestDb.execSQL("INSERT INTO user_info(name,pwd) VALUES('lisi','mimajjjj')");  
    mTestDb.execSQL("INSERT INTO user_info VALUES(null,?,?,?)",new Object[]{user.getName(),user.getPwd(),user.getModifyTime()});  
    mTestDb.execSQL("INSERT INTO user_info(name,pwd) VALUES(?,?)",new Object[]{"lisi","mimajjjj"});  
      
    ContentValues values = new ContentValues();  
    values.put(UserSQLiteOpenHelper.COL_NAME,"qwerdf");  
    //values.put(UserSQLiteOpenHelper.COL_PWD, "qwerdflol");  
    values.put(UserSQLiteOpenHelper.COL_TIME, user.getModifyTime());  
    mTestDb.insert("user_info", null,values);  
}

数据库结果

_id|name|pwd|modifyTime  
1|xiaopihai|12345678|1466403447668  
2|lisi|mimajjjj|  
3|xiaopihai|12345678|1466403447668  
4|lisi|mimajjjj|  
5|qwerdf||1466403447668

数据库增加有两种方法:

  1. 通过执行sql语句,调用execSQL(String sql) 或者 execSQL(String sql,Object[] bindArgs)方法。
    execSQL(String sql) 执行不带占位符的sql语句
    mTestDb.execSQL("INSERT INTO user_info(name,pwd) VALUES('hhohh', 'hidiiiihiihh')");
    execSQL(String sql, Object[] bindArgs)执行带占位符的SQL语句。
    mTestDb.execSQL("INSERT INTO user_info(name,pwd) VALUES(?,?)",new Object[]{"lisi","mimajjjj"});
  2. SQLiteDatabase的insert(String table,String nullColumnHack,ContentValues values)方法,
    参数1 表名称,
    参数2 空列的默认值
    参数3 ContentValues类型的一个封装了列名称和列值的Map;
    返回值:新插入的行的行号,或-1(如果发生错误)。
    values中可以存单个或多个数据。
ContentValues values = new ContentValues();  
values.put(UserSQLiteOpenHelper.COL_NAME,"qwerdf");  
//values.put(UserSQLiteOpenHelper.COL_PWD, "qwerdflol");  
values.put(UserSQLiteOpenHelper.COL_TIME, user.getModifyTime());  
mTestDb.insert("user_info", null,values);}

4 修改数据

public static void updateTest(){  
    mTestDb.execSQL("UPDATE user_info SET name = 'update1' WHERE _id = 1;");  
    mTestDb.execSQL("UPDATE user_info SET name =? WHERE _id=?",new Object[]{"update1",1});  
    mTestDb.execSQL("UPDATE user_info SET name = 'update2',modifyTime=111 WHERE _id = 2;");  
    mTestDb.execSQL("UPDATE user_info SET name =? ,modifyTime =? WHERE _id=?",new Object[]{"update1",111,2});  
      
    ContentValues values = new ContentValues();  
    values.put("name","update3");  
    values.put("pwd", "2222222");  
    values.put("modifyTime", 898);  
    mTestDb.update("user_info", values, "_id=?", new String[]{String.valueOf(3)});  
}

数据库结果:

_id|name|pwd|modifyTime  
1|update1|12345678|1466404010484  
2|update1|mimajjjj|111  
3|update3|2222222|898  
4|lisi|mimajjjj|  
5|qwerdf||1466404010484

修改数据库两种方法:

  1. 通过execSQL执行sql语句。
  2. 通过方法

public int update (String table, ContentValues values, String whereClause, String[] whereArgs)
在数据库中更新行的便捷方法。
参数:更新的表名; map更新的内容,null是有效值将被转换为NULL。whereClause可选更新时WHERE子句适用。传递null将更新所有行。返回受影响的行数,如果没有返回0。


5 删除数据

public static void deleteTest(){  
    mTestDb.execSQL("delete from user_info where _id=1");  
    //mTestDb.execSQL("delete from user_info where _id=?",new Object[]{1});  
      
    mTestDb.delete("user_info", "_id=?", new String[]{String.valueOf(2)});  
}

数据库结果

_id|name|pwd|modifyTime  
3|update3|2222222|898  
4|lisi|mimajjjj|  
5|qwerdf||1466404010484

删除数据库的两种方法:
修改数据库两种方法:

  1. 通过execSQL执行sql语句。
  2. 通过下面方法
    public int delete (String table, String whereClause, String[] whereArgs)
    参数
    删除的表
    whereClause选择时删除WHERE子句适用。传递null将删除所有行。
    返回
    受影响的行数,返回删除的行数,如果没有返回0。

6 查询数据

查询所有

public List<UserBean> findAll() {  
        List<UserBean> userList = new ArrayList<UserBean>();  
        //查询表中的所有数据。  
        Cursor cursor = mDB.query(UserSQLiteOpenHelper.DATABASE_TABLE_USER,  
                null, null, null, null, null, UserSQLiteOpenHelper.COL_TIME  
                        + " desc");    //order by modifytime 降序  
  
        if (null != cursor) {  
            while (cursor.moveToNext()) {  
                UserBean user = new UserBean();  
                user.set_id(cursor.getLong(cursor  
                        .getColumnIndex(UserSQLiteOpenHelper.COL_ID)));  
                user.setName(cursor.getString(cursor  
                        .getColumnIndex(UserSQLiteOpenHelper.COL_NAME)));  
                user.setPwd(cursor.getString(cursor  
                        .getColumnIndex(UserSQLiteOpenHelper.COL_PWD)));  
                user.setModifyTime(cursor.getLong(cursor  
                        .getColumnIndex(UserSQLiteOpenHelper.COL_TIME)));  
                userList.add(user);  
            }  
            cursor.close();  
        }  
        return userList;  
    }

查询某个

/某个或某些查询  
//模糊查询            
Cursor cursor = mDB.query(UserSQLiteOpenHelper.DATABASE_TABLE_USER,  
    null, UserSQLiteOpenHelper.COL_NAME + " like?"+" and "+UserSQLiteOpenHelper.COL_ID+" >?",  
    new String[] {"%"+name+"%",2+""}, null, null, UserSQLiteOpenHelper.COL_ID  
    + " desc");  
          
//      Cursor cursor = mDB.query(UserSQLiteOpenHelper.DATABASE_TABLE_USER,  
//          null, UserSQLiteOpenHelper.COL_NAME + " =?",  
//          new String[] {name}, null, null, UserSQLiteOpenHelper.COL_ID  
//          + " desc");  
          
        //多个条件查询  
//      Cursor cursor = mDB.query(UserSQLiteOpenHelper.DATABASE_TABLE_USER,  
//              null, UserSQLiteOpenHelper.COL_NAME + " like?"+" and "+UserSQLiteOpenHelper.COL_ID+" >?",  
//              new String[] {"%"+name+"%",2+""}, null, null, UserSQLiteOpenHelper.COL_ID  
//              + " desc");
SqliteDatabase query

这是SqliteDatabase提供的几种方法,可以适当选用,进行查询。

SQLiteOpenHelper类(抽象类),一个辅助类来管理数据库的建立和版本管理。
可以创建一个它的子类,实现onCreate(SQLiteDatabase)
, onUpgrade(SQLiteDatabase, int, int) 以及可选的方法onOpen(SQLiteDatabase),****它主要是打开数据库(如果已存在),如果没存在则创建并打开。根据需要进行升级。
对其子类进行初始化时UserSQLiteOpenHelper.getInstance(context),这时并没有建立数据库。

public static UserSQLiteOpenHelper getInstance(Context context) {  
        if (null == mInstance) {  
            mInstance = new UserSQLiteOpenHelper(context);  
            mContext = context;  
        }  
        return mInstance;  
    }  
  
    private UserSQLiteOpenHelper(Context context) {  
        //这时并没有建立数据库。  
        super(context, REMOTE_LIVE_DATABASE_NAME, null, version);  
}

看父类SQLiteOpenHelper类源码

public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {  
        this(context, name, factory, version, null);  
}  
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,  
            DatabaseErrorHandler errorHandler) {  
        if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);  
  
  
        mContext = context;  
        mName = name;  
        mFactory = factory;  
        mNewVersion = version;  
        mErrorHandler = errorHandler;  
}

先是调方法SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version),然后掉到方法SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version),该方法中要求我们传递的version必须大于等于1,否则会报错IllegalArgumentException异常。这个方法中并没有为我们创建并打开数据库,只是存了5个变量的值而已。
那什么时候才会创建数据库呢?

SQliteOpenHelper

public SQLiteDatabase getWritableDatabase() {  
        synchronized (this) {  
            return getDatabaseLocked(true);  
        }  
    }  
  
    public SQLiteDatabase getReadableDatabase() {  
        synchronized (this) {  
            return getDatabaseLocked(false);  
        }  
    }  
  
    private SQLiteDatabase getDatabaseLocked(boolean writable) {  
        if (mDatabase != null) {  
            if (!mDatabase.isOpen()) {  
                // Darn!  The user closed the database by calling mDatabase.close().  
                mDatabase = null;  
            } else if (!writable || !mDatabase.isReadOnly()) {  
                // The database is already open for business.  
                return mDatabase;  
            }  
        }  
  
        if (mIsInitializing) {  
            throw new IllegalStateException("getDatabase called recursively");  
        }  
  
        SQLiteDatabase db = mDatabase;  
        try {  
            mIsInitializing = true;  
  
            if (db != null) {  
                if (writable && db.isReadOnly()) {  
                    db.reopenReadWrite();  
                }  
            } else if (mName == null) {  
                db = SQLiteDatabase.create(null);  
            } else {  
                try {  
                    if (DEBUG_STRICT_READONLY && !writable) {  
                        final String path = mContext.getDatabasePath(mName).getPath();  
                        db = SQLiteDatabase.openDatabase(path, mFactory,  
                                SQLiteDatabase.OPEN_READONLY, mErrorHandler);  
                    } else {  
                        db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?  
                                Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,  
                                mFactory, mErrorHandler);  
                    }  
                } catch (SQLiteException ex) {  
                    if (writable) {  
                        throw ex;  
                    }  
                    Log.e(TAG, "Couldn't open " + mName  
                            + " for writing (will try read-only):", ex);  
                    final String path = mContext.getDatabasePath(mName).getPath();  
                    db = SQLiteDatabase.openDatabase(path, mFactory,  
                            SQLiteDatabase.OPEN_READONLY, mErrorHandler);  
                }  
            }  
  
            onConfigure(db);  
  
            final int version = db.getVersion();  
            if (version != mNewVersion) {  
                if (db.isReadOnly()) {  
                    throw new SQLiteException("Can't upgrade read-only database from version " +  
                            db.getVersion() + " to " + mNewVersion + ": " + mName);  
                }  
  
                db.beginTransaction();  
                try {  
                    if (version == 0) {  
                        onCreate(db);  
                    } else {  
                        if (version > mNewVersion) {  
                            onDowngrade(db, version, mNewVersion);  
                        } else {  
                            onUpgrade(db, version, mNewVersion);  
                        }  
                    }  
                    db.setVersion(mNewVersion);  
                    db.setTransactionSuccessful();  
                } finally {  
                    db.endTransaction();  
                }  
            }  
  
            onOpen(db);  
  
            if (db.isReadOnly()) {  
                Log.w(TAG, "Opened " + mName + " in read-only mode");  
            }  
  
            mDatabase = db;  
            return db;  
        } finally {  
            mIsInitializing = false;  
            if (db != null && db != mDatabase) {  
                db.close();  
            }  
        }  
    }

查看SQLiteOpenHelper源码中,发现只有getDatabaseLocked()中才会创建数据库,也就是第一次调用SQLiteOpenHelper.getWritableDatabase()或SQLiteOpenHelper.getReadableDatabase()时才回真正的创建数据库。此方法创建的数据库地址为:/data/data/包名/databases/
onConfigure(db)
创建数据库后,调用onConfigure(db),这个方法里面可以设置数据库连接的一些参数,如setLocale() 、setMaximumSize()、setForeignKeyConstraintsEnabled()。
onCreate(db)
再向下会走到onCreate(db)方法中,onCreate只在数据库第一次创建的时候会调用,调用之前version = 0,之后就会设置新的version,所以此方法不会再走了。在onCreate()方法中,主要用来创建表。
之后使用时可以通过设置SQLiteOpenHelper 中的mNewVersion,然后调用getReadableDatabase()或getWritableDatabase(),当mNewVersion大于数据库的version,则会调用onUpgrade来升级。而当mNewVersion小于数据库的version,则调用onDwongrade来降级。然后再将此版本号设置为数据库的版本号。

db.setVersion(mNewVersion)

如何设置mNewVersion呢?
查看SQLiteOpenHelper中只有构造方法中设置了mNewVersion,所有只能通过这两个方法才可以。(子类调用父类的这两个方法。。)

public SQLiteOpenHelper (Context context, String name, 
      SQLiteDatabase.CursorFactory factory, int version)  
public SQLiteOpenHelper (Context context, String name, 
      SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler)

onOpen()每次打开数据库会调用,选用,不是必须的。

demo下载链接http://download.csdn.net/detail/vnanyesheshou/9581418

效果图

欢迎大家关注、评论、点赞
你们的支持是我坚持的动力。

欢迎关注微信公众号

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 一. SQLiteOpenHelper类介绍 1. SQLiteOpenHelper是什么? 用来创建、打开、更新...
    NickelFox阅读 1,151评论 0 4
  • 完美的一个日期,适合用来写日记。 没有钱的日子是那么艰难。参加几次聚会就渐渐觉得自己要吃不起饭。 断了外快来源的我...
    张意志SMG阅读 264评论 0 0
  • 一 “我要开一家属于自己的烘焙店!” “为什么突然说要想开一家烘焙店呢?” “因为你喜欢吃面包蛋糕啊!” “诶~那...
    默童阅读 271评论 0 3
  • 前言 JS需要异步处理的地方实在是比较多,比如定时器/ajax/io操作等等,在当今前端技术日新月异的情况下,异步...
    云峰yf阅读 6,227评论 10 9
  • 距离是个很矛盾的事情,距离产生美,又有人说距离太远关系会变淡,那我们又是怎样呢? 6月的夏天特别神奇,它像...
    D怡阅读 153评论 0 0