美文网首页
SQLite 入门简介

SQLite 入门简介

作者: YueJZ | 来源:发表于2018-04-17 16:33 被阅读0次

    SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置。

    就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 直接访问其存储文件。

    当我们有持久化保存数据的需求时,可以采用 SQLite。

    本文将介绍 SQLite 的简单使用,主要从以下几个方面介绍。

    • 创建表
    • insert 和 updata 操作
    • 查询操作
    • 在开发时查看数据库结构及数据

    创建表

    本应用中,数据库只存储 Crime 类的数据,较为简单,我们直接定义 schema 的 java 类。新建 database 包 database.CrimeDbSchema。创建 CrimeDbSchema 类。

    CrimeTable.java

    public class CrimeDbSchema {
        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";
            }
        }
    }
    

    CrimeTable 类唯一的作用就是描述数据库表名,Cols 类描述数据库的列名。有了这些数据表元素,就可以在 Java 代码中安全引用了。此外,这还为修改字段名或者新增表元素都带来了方便。

    有了数据库 schema,就可以创建数据库了。openOrCreateDatabase(...) 和 databaseList() 是Android 提供的 Context 底层方法,用来打开数据库文件并转化为 SQLiteDatabase 实例。不过在实践中,建议总是遵循以下步骤:

    1. 确认目标数据库是否存在
    2. 如果不存在,首先创建数据库,初始化数据表并初始化数据
    3. 如果存在,打开并确认 CrimeDbSchema 是否为最新
    4. 如果是旧版本,就先升级到最新版本

    以上工作可借助 Android 的 SQLiteOpenHelper 类处理。同样在数据库包中创建该类。

    BaseHelper.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);
        }
    
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
        
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    
        }
    }
    

    onCreate(...) 负责创建数据库,onUpgrade(...) 负责与升级相关的工作。接下来在 CrimeLab 中使用 CrimeBaseHelper 类。

    CrimeLab.java

    public class CrimeLab {
    
        private static CrimeLab sCrimeLab;
    
        private List<Crime> mCrimes;
        private Context mContext;
        private SQLiteDatabase mDatebase;
        
        private CrimeLab(Context context) {
            mContext = context.getApplicationContext();
            mDatebase = new CrimeBaseHelper(mContext)
                    .getWritableDatabase();
            mCrimes = new ArrayList<>();
        }
    }
    
    

    调用 getWritableDatabase() 时,CrimeHelper 会做上面提到的四步工作。这里我们还需要把 onCreate(...) 中的 SQL 补充完整。

    CrimeBaseHelper.java

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

    现在运行程序,只会看到空列表,因为我们只创建了数据库,里面并没有值,下面我们逐步来完善它。

    insert 和 updata 操作

    修改 CrimeLab 类。负责数据库写入更新操作的辅助类是 ContentValues。它类似与 HashMap 的键值存储类。但它只能处理 SQLite 数据。

    CrimeLab.java

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

    准备好了 ContentValues 类,就该向数据库写入数据了。

    CrimeLab.java

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

    insert(String,String,ContentValues) 的第一个参数是数据库表名,第三个参数是要写入的数据,第二个参数很少用到,当传入的 ContentValues 参数为空时,insert(...) 方法会调用失败,但如果以 uuid 做为第二个参数的值,那么就可以插入该值。我们现在用不到。

    现在来写更新的方法

    CrimeLab.java

    public void updateCrime(Crime crime){
        String uuidString = crime.getId().toString();
        ContentValues values = getContentValues(crime);
        mDatebase.update(CrimeDbSchema.CrimeTable.NAME,values, CrimeDbSchema.CrimeTable.Cols.UUID +
                " = ?",new String[]{uuidString});
    }
    

    update(String,ContentValues,String,String []) 方法类似于 insert(...) 方法。传入表名和 ContentValues,然后创建 where 字句(第三个参数),指定 where 字句中的参数(String [] 数组参数)。为什么不直接放在 where 语句中呢?因为这样可以防止 SQL 注入。

    修改数据之后需要刷新数据,可以覆盖 CrimeFrament.onPause() 方法。

    CrimeFragment.java

    @Override
    public void onPause() {
        super.onPause();
        CrimeLab.get(getActivity()).updateCrime(mCrime);
    }
    

    数据库的写入部分处理完了,如果都没有问题,那么运行程序会出现空列表。

    查询操作

    读取数据库中的数据需要调用 SQLiteDatabase.query(...),在 CrimeLab 中新增一个 quary 方法。

    CrimeLab.java

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

    whereClause 和 whereArgs 与 update(...) 方法中参数的作用类似。

    Cursor 类的功能是封装数据库中的原始字段值。因为每次从数据库中取出一条数据,都会用到相同的代码,如:getString(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.UUID))。考虑到代码的复用。我们创建可复用的专用 Cursor 子类。创建 Cursor 子类最简单的方式是使用 CursorWrapper。可以用 CursorWrapper 封装 Cursor 对象,然后再添加有用的扩展方法。

    在数据包中新建 CrimeCursorWrapper 类。

    CrimeCursorWrapper.java

    public class CrimeCursorWrapper extends CursorWrapper{
        public CrimeCursorWrapper(Cursor cursor){
            super(cursor);
        }
    
        public Crime getCrime(){
            String uuidString = getString(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.UUID));
            String title = getString(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.TITLE));
            long date = getLong(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.DATE));
            int isSloved = getInt(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.SOLVED));
    
            Crime crime = new Crime(UUID.fromString(uuidString));
            crime.setDate(new Date(date));
            crime.setTitle(title);
            crime.setSolved(isSloved != 0);
    
            return crime;
        }
    }
    

    在 Crime 类中新增一个传入 uuid 的构造方法。

    Crime.java

    public class Crime {
        public Crime() {
            this(UUID.randomUUID());
        }
    
        public Crime(UUID id){
            mId = id;
            mDate = new Date();
        }
    }
    

    更改 queryCrimes(...) 方法的返回值

    CrimeLab.java

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

    完善 getCrimes() 方法:遍历取出所有的 crime,返回 Crime 数组对象。

    CrimeLab.java

     public Crime getCrime(UUID id) {
         public List<Crime> getCrimes() {
            //return new ArrayList<>();
            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() 说没有数据了为止。 最后还需要 close() 方法关闭它,否则会出错。

    增加根据 uuid 查询单个 Crime 的方法。

    CrimeLab.java

    public Crime getCrime(UUID id) {
        CrimeCursorWrapper cursorWrapper = queryCrimes(CrimeDbSchema.CrimeTable.Cols.UUID + " = ?",
                new String[]{id.toString()});
        try{
            if (cursorWrapper.getCount() == 0){
                return null;
            }
            cursorWrapper.moveToFirst();
            return cursorWrapper.getCrime();
        }finally {
            cursorWrapper.close();
        }
    }
    

    现在可以看到新增的 Crime 了。

    还没有完,虽然 Crime 记录存入了数据库,但是数据库读取还没有完善。例如,当编辑完新的 crime,点击后退键,会发现 CrimeListActivity 并没有刷新记录。这是因为 CrimeLab 的工作方式已经变了。以前,只有一个 List<Crime>,而且每个 Crime 在 List<Crime> 中只存在一个对象。要获取哪一个只能去找 mCrimes。现在 mCrimes 已经废弃不用,所以,getCrimes() 方法返回的 List<Crime> 是 Crime 对象的快照,要刷新 CrimeListActivity 界面,首先要刷新这个快照。

    要刷新 crime 显示,首先添加一个 setCrimes(...) 方法给 CrimeAdapter。

    CrimeListFragment.java

    private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {
        public void setmCrimes(List<Crime> crimes){
            mCrimes = crimes;
        }
    }
    

    然后在 updateUI() 方法中调用 setCrimes(...) 方法。

    CrimeListFragment.java

    private void updateUI() {
        CrimeLab crimeLab = CrimeLab.get(getActivity());
        List<Crime> crimes = crimeLab.getCrimes();
        if (mAdapter==null){
            mAdapter = new CrimeAdapter(crimes);
            mCrimeRecyclerView.setAdapter(mAdapter);
        }else {
            mAdapter.setmCrimes(crimes);
            mAdapter.notifyItemChanged(mPosition);
        }
        updateSubtitle();
    }
    

    现在就能正常使用该应用了。

    在开发时查看数据库结构及数据

    这篇文章介绍了一种查看数据库结构及数据的工具,可点击查看

    调试工具

    附:GitHut地址

    相关文章

      网友评论

          本文标题:SQLite 入门简介

          本文链接:https://www.haomeiwen.com/subject/dzkpkftx.html