SQLite的简单使用

引入
savedInstanceState无法满足应用持久化保存数据
Android为此提供了长期储存地——闪存上的本地文件系统
Android设备上的应用都有一个沙盒目录,路径通常是:
/data/data/packed name
文件保存再沙盒中,可阻止其他应用和未获得root权限的设备用户

SQLite是类似于MySQL和Postgresql的开源关系型数据库
与其他数据库不同的是,SQLite使用单个文件存储数据


代码部分
建立整个表的框架,将表名字段名定义在类中方便之后的使用

CrimeDbSchema.java

public class CrimeDbSchema {

    //CrimeTable内部类唯一用途是定义描述数据表元素的String常量
    public static final class CrimeTable{

        //数据库的表名
        public static final String NAME="crimes";

        //定义数据表字段
        public static final class Cols {
            public static final String UUID = "uuid";
            public static final String TITLE = "title";
            public static final String DATE = "date";
            public static final String SOLVED = "solved";
        }
    }
}

创建数据库
openOrCreateDatabase(),databaseList()是android提供的Context底层方法,用于打开数据库文件并将其转换为SQLite实例
要打开一个数据库,首先确认目标数据库是否存在,不存再则先创建,若存在则判断是否是最新版本,是就打开不是就升级
这一过程可以用SQLiteOpenHelper类来处理
要覆盖两个方法onCreate,onUpgrade


onCreate中的建表语句

@Override
    public void onCreate(SQLiteDatabase db){
        db.execSQL("create table "+ CrimeTable.NAME+"("+
        "_id integer primary key autoincrement, "+
        CrimeTable.Cols.UUID+", "+
        CrimeTable.Cols.TITLE+", "+
        CrimeTable.Cols.DATE+", "+
        CrimeTable.Cols.SOLVED+
                ")"
        );
    }

书写建表语句时要注意table与逗号后面的空格


onUpgrade
有时需要调整数据库表结构,比如新增字段
常规做法是在SQLiteOpenHelper记录版本号,然后在onUpgrade方法中升级数据表
但这样的常规方法很复杂,不如直接删除数据库文件然后再重新创建它
删除数据库文件可直接在设备上删除应用

CrimeBaseHelper.java
public class CrimeBaseHelper extends SQLiteOpenHelper {
    private static final int VERSION=1;
    private static final String DATABASE_NAME="crimeBase.db";

    public CrimeBaseHelper(Context context){
        super(context,DATABASE_NAME,null,VERSION);
        //参数一:context,参数二:数据库名
        //参数三:允许在查询数据时返回自定义cursor,一般传入null
        //参数四:当前数据库的版本号,可用于对数据库进行升级操作
    }

    @Override
    public void onCreate(SQLiteDatabase db){
        db.execSQL("create table "+ CrimeTable.NAME+"("+
        "_id integer primary key autoincrement, "+
        CrimeTable.Cols.UUID+", "+
        CrimeTable.Cols.TITLE+", "+
        CrimeTable.Cols.DATE+", "+
        CrimeTable.Cols.SOLVED+
                ")"
        );
    }

    @Override
    public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){

    }
}

打开数据库
(再CrimeLab的构造函数中)

private CrimeLab(Context context){
        mContext=context.getApplicationContext();
        mSQLiteDatabase=new CrimeBaseHelper(mContext)
                .getWritableDatabase();
    }

调用getWritableDatabase时CrimeBaseHelper的工作
1.打开沙盒目录下的crimeBase.db数据库(名字是在CrimeBaseHelper里定义的);若不存在就先创建
2.若是首次创建则调用onCreate方法,并保存最新版本号
3.若是已创建过的数据库,首先检查它的版本号,若CrimeOpenHelper中的更高(?),则调用onUpgrade方法

新建的数据库文件存放在/data/data/<package name>/databases/

还有一个getReadableDatabase()方法
两个方法都可以创建或打开一个现有的数据库,并返回一个可对数据库进行读写操作的对象
当数据库不可写入时(磁盘空间已满)时,getReadableDatabase()方法返回的对象将以只读的方式打开数据库,getWritableDatabase()方法出现异常

普通的建表语句

create table table_name(
      id integer primary key autoincrement,//primary key声明为主键
                                           //autoincrement表示id列是自增的
     字段名 字段类型,
     ……)

SQLite建表是不是必需要指定字段类型


ContentValues键值存储类,类似Bundle,但只用于SQLite数据,负责处理数据库写入与更新操作。
如何把一个Crime数据转换为ContentValues

private static ContentValues getContentValues(Crime crime){
        ContentValues values=new ContentValues();
        values.put(CrimeTable.Cols.UUID,crime.getId().toString());
        values.put(CrimeTable.Cols.TITLE,crime.getTitle());
        values.put(CrimeTable.Cols.DATE,crime.getDate().getTime());
        values.put(CrimeTable.Cols.SOLVED,crime.getSolved()?1:0);
        return values;
    }

写入数据库
(CrimeLab的addCrime方法)
mDatabase.insert(String TableName,String nullColumnHack,ContentValues values)

public void addCrime(Crime c){
        ContentValues values=getContentValues(c);
        mSQLiteDatabase.insert(CrimeTable.NAME,null,values);
    }

更新数据库
(CrimeLab的upDateCrime)
mDatabase.update(String TableName,ContentValues,String where,String[] whereArgs)
三四句用于确认需要更新哪些数据
参数三创建where语句,参数四指定where子句的参数
不再where语句中直接放入参数的原因是,String本身很可能包含SQL代码,若直接放入语句中,可能会改变语义十分危险(SQL脚本注入)

 public void updateCrime(Crime crime){
        String uuidString=crime.getId().toString();
        ContentValues values=getContentValues(crime);

        mSQLiteDatabase.update(CrimeTable.NAME,values,
                CrimeTable.Cols.UUID+"=?",
                new String[] {uuidString});
    }

之前使用List<>来存crime时,在CrimeFragment中有改动发生时,使用了crime.setTitle之类的方法,crimes中数据也会跟着改变,但现在使用的是数据库,数据库返回的数据的一个快照而不是数据本身,想要修改数据还是要调用数据库的update方法
CrimeFragment.java

@Override
    public void onPause()
    {
        super.onPause();

        CrimeLab.get(getActivity()).updateCrime(mCrime);
    }

读取数据库
query方法有好几个重载版本
其中之一

public Cursor query(
         String table,
         String[] columns,
         String where,
         String whereArgs,
         String groupBy,
         String having,
         String orderBy,
         String limit)

Cursor是表处理工具,封装数据表中的原始字段
CursorWrapper可以封装一个个Cursor对象,并在其上添加新的方法,可用来快速方便的创建Cursor的子类
(那直接使用继承Cursor然后添加新方法不就好了,为什么要用CursorWrapper呢)

query方法得到的是cursor
在CrimeCursorWrapper中添加从cursor转换的crime的方法

public class CrimeCursorWrapper extends CursorWrapper {
   public CrimeCursorWrapper(Cursor cursor){
        super(cursor);
    }

    public Crime getCrime(){
        String uuidString=getString(getColumnIndex(CrimeTable.Cols.UUID));
        String title=getString(getColumnIndex(CrimeTable.Cols.TITLE));
        long date=getLong(getColumnIndex(CrimeTable.Cols.DATE));
        int isSolved=getInt(getColumnIndex(CrimeTable.Cols.SOLVED));

        Crime crime=new Crime(UUID.fromString(uuidString));
        crime.setTitle(title);
        //这一步要用long类型数据造一个Date型数据来
        crime.setDate(new Date(date));
        crime.setSolved(isSolved!=0);
        return crime;
    }
}

查询数据并获得CrimeCursorWrapper
(CrimeLab中)

private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs){
        Cursor cursor=mSQLiteDatabase.query(
                CrimeTable.NAME,
                null,
                whereClause,
                whereArgs,
                null,
                null,
                null
        );
        return new CrimeCursorWrapper(cursor);
    }

重写CrimeLab.getCrimes

public List<Crime> getCrimes(){
        List<Crime> crimes=new ArrayList<>();

        CrimeCursorWrapper cursor=queryCrimes(null,null);
        try{
            cursor.moveToFirst();
            while(!cursor.isAfterLast()){
                crimes.add(cursor.getCrime());
                cursor.moveToNext();
            }
        }finally {
            cursor.close();
        }
        return crimes;
    }

cursor中可能有很多条数据,要从中取出数据,首先要调用moveToFirst()方法使其指向第一行记录,然后在调用moveToNext指向下一行记录,直到isAfterLast说明没有数据了
一定要关闭cursor,不然可能会导致应用崩溃


重写CrimeLab.getCrime

public Crime getCrime(UUID id){
       CrimeCursorWrapper cursor=queryCrimes(
                CrimeTable.Cols.UUID+"=?",
                new String[]{id.toString()}
        );

        try{
            if (cursor.getCount()==0){
                return null;
            }

            cursor.moveToFirst();
            return cursor.getCrime();
        }finally {
            cursor.close();
        }
    }

删除数据
mSQLiteDatabase.delete(String table_name,String where,String[] whereArgs)


给数据库新增一张表
数据库已存在后,就不会在执行DatabaseHelper里的onCreate方法了,若想新增一张表,只在onCreate里加db.execSQL是没用的

public static final String CREATE_CATEGORY="create table Category("
        +"id integer primary key autoincrement,"
        +"category_name text,"
        +"category_code integer)";
public void onCreate(SQLiteDatabase db){
    db.execSQL(CREATE_BOOK);
    db.execSQL(CREATE_CATEGORY);
    Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){
    db.execSQL("drop table if exists Book");
//如果这张表存在就将它删掉,因为在创建表的时候若发现此表已存在会报错
    db.execSQL("drop table if exists Category");
    onCreate(db);
}

在构造SQLiteOpenHelper对象是,传入的第四个参数版本号,只要比之前大,就会执行onUpgrade()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 对于Sqlite的简单使用,我们可以用两种方式,先说第一种:这种是大家常用的方式,我们先创建一个类继承SQLite...
    sakurajiang阅读 3,846评论 0 2
  • 2017年5月17日 Kylin_Wu 标注(★☆)为考纲明确给出考点(必考) 常见手机系统(★☆) And...
    Azur_wxj阅读 5,829评论 0 10
  • LZ-Says:给大家推荐一个网站,有兴趣可以查阅,想为大家贡献一点自己的力量也可以投稿,老大审核通过会发表,更好...
    静心Study阅读 4,599评论 0 3
  • SQLite简介 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库...
    BrumeLoong阅读 3,602评论 0 0
  • 1. 一大早,老爸喊我吃早饭,我拖着疲惫的身躯,睁着朦胧的睡眼,一桌子的豆浆油条,豆沙包。老爸总是那一身黑色毛衣正...
    连叶北阅读 4,492评论 2 5

友情链接更多精彩内容