Android SQLite使用详解

在项目开发中,我们基本上会用到数据库去存储数据。虽然现在有着不少的数据库框架,像GreenDao 、LiteOrm、xUtils3等,基本可以满足我们数据库方面的使用需求。之所以写SQLite的使用,主要是用来了解数据库的基本使用,也好在面试中应付面试。
使用SQLite, 首先创建一个继承在SQLiteOpenHelper的类,并重写onCreate()和onUpgrade()方法。

package com.my.learn.code.sqlite;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class MySqliteHelper extends SQLiteOpenHelper {

    private static final int DB_VERSION = 1;//数据库版本
    private static final String DB_NAME = "myTest.db";//数据库名称
    public static final String TABLE_NAME = "Orders";//表的名称

    public MySqliteHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    /**
     * 这个方法
     * 1、在第一次打开数据库的时候才会走
     * 2、在清除数据之后再次运行-->打开数据库,这个方法会走
     * 3、没有清除数据,不会走这个方法
     * 4、数据库升级的时候这个方法不会走
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        //初始化数据表,可以再这里面对多个表进行处理
        String sql = "create table if not exists " + TABLE_NAME + " (studentid text primary key, name text, sex text, age integer)";
        db.execSQL(sql);
    }

    /**
     * 数据库升级
     * 1、第一次创建数据库的时候,这个方法不会走
     * 2、清除数据后再次运行(相当于第一次创建)这个方法不会走
     * 3、数据库已经存在,而且版本升高的时候,这个方法才会调用
     *
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String sql = "DROP TABLE IF EXISTS " + TABLE_NAME;
        db.execSQL(sql);
        onCreate(db);
    }

    /**
     * 执行数据库的降级操作
     * 1、只有新版本比旧版本低的时候才会执行
     * 2、如果不执行降级操作,会抛出异常
     */
    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        super.onDowngrade(db, oldVersion, newVersion);
    }

}

这个类主要是用来创建数据库、数据库表以及维护数据库升级、数据表字段更新。
然后创建一个DAO类来操作数据表的CRUD。

public class StudentDao {
    private static StudentDao INSTANCE  = null;
    private MySqliteHelper mHelper = null;
    private SQLiteDatabase mDB = null;
    private StudentDao(Context context){
        mHelper = new MySqliteHelper(context);
        mDB = mHelper.getWritableDatabase();
    }

    public static StudentDao getInstance(Context context){
        if(INSTANCE == null){
            return new StudentDao(context);
        }
        return INSTANCE;
    }
}

在这里面SQLiteDatabase mDB使用了操作数据库的实例,这里我能用getWritableDatabase();来获取,在SQLiteOpenHelper中提供了getWritableDatabase()跟getReadableDatabase()两种方式来获取SQLiteDatabase,那么这两个方法的区别是什么呢?
我们看下彼此的代码就基本能清楚。
getReadableDatabase源码:

    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.createInMemory(mOpenParamsBuilder.build());
            } else {
                final File filePath = mContext.getDatabasePath(mName);
                SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
                try {
                    db = SQLiteDatabase.openDatabase(filePath, params);
                    // Keep pre-O-MR1 behavior by resetting file permissions to 660
                    setFilePermissionsForDb(filePath.getPath());
                } catch (SQLException ex) {
                    if (writable) {
                        throw ex;
                    }
                    Log.e(TAG, "Couldn't open " + mName
                            + " for writing (will try read-only):", ex);
                    params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
                    db = SQLiteDatabase.openDatabase(filePath, params);
                }
            }
            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);
                }

                if (version > 0 && version < mMinimumSupportedVersion) {
                    File databaseFile = new File(db.getPath());
                    onBeforeDelete(db);
                    db.close();
                    if (SQLiteDatabase.deleteDatabase(databaseFile)) {
                        mIsInitializing = false;
                        return getDatabaseLocked(writable);
                    } else {
                        throw new IllegalStateException("Unable to delete obsolete database "
                                + mName + " with version " + version);
                    }
                } else {
                    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();
            }
        }
    }

在getReadableDatabase()方法中,调用getDatabaseLocked()来获取SQLiteDatabase,getDatabaseLocked中在首先判断是否已存在数据库实例并且是打开状态,如果是,则直接返回该实例,否则试图获取一个可读写模式的数据库实例,如果遇到磁盘空间已满等情况获取失败的话,再以只读模式打开数据库,获取数据库实例并返回,然后为mDatabase赋值为最新打开的数据库实例。
getWritableDatabase代码:

    public SQLiteDatabase getWritableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(true);
        }
    }

可以看到getWritableDatabase同样是调用了getDatabaseLocked(),只不过在getReadableDatabase中传的是false,getWritableDatabase中传的是true。在几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,比较版本,根据版本号来调用相应的方法,为数据库设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库实例赋予mDatabase,并返回最新实例。
通过代码可以看出,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库实例,所以我们在StudentDao构造方法中使用getWritableDatabase()获取整个应用所使用的数据库实例。下面看数据的操作部分。
1.增加数据
在Android中提供了两种增加数据操作:
初始化数据
直接采用execSQL方法:

mDB.execSQL("insert into " + MySqliteHelper.TABLE_NAME + " (studentid, name, sex, age) values (1, '张三', '男', 10)");

添加数据:
可以使用insert(String table,String nullColumnHack,ContentValues values)方法来插入,ContentValues内部实现就是HashMap,但是两者还是有差别的,ContenValues Key只能是String类型,Value只能存储基本类型的数据,像string,int之类的,不能存储对象。

        public void addstudent(Student student){
        mDB.beginTransaction();
        ContentValues contentValues=new ContentValues();
        contentValues.put("studentid",student.getId());
        contentValues.put("name",student.getName());
        contentValues.put("sex",student.getSex());
        contentValues.put("age",student.getAge());
        mDB.insertOrThrow( MySqliteHelper.TABLE_NAME,null,contentValues);
        mDB.setTransactionSuccessful();
        mDB.endTransaction();
    }

删除数据
删除数据的方法除了execSQL还有delete(String table,String whereClause,String[] whereArgs),whereClause是删除条件,whereArgs是删除条件值数组。

   /**
     * 删除数据
     */
    public void deletestudent(String id){
        mDB.beginTransaction();
        mDB.delete(MySqliteHelper.TABLE_NAME, "studentid = ?", new String[]{id});
        mDB.setTransactionSuccessful();
    }

修改数据
修改数据和插入数据很相似,调用的方法除了execSQL还可以是update(String table,ContentValues values,String whereClause, String[] whereArgs):

   /**
     * 修改数据
     */
    public void updatestudent(ContentValues contentValues,String id){
        mDB.beginTransaction();
        mDB.update(MySqliteHelper.TABLE_NAME,
                contentValues,
                "Id = ?",
                new String[]{id});
        mDB.setTransactionSuccessful();
    }

查找数据
查找数据有两个方法,一是public Cursor query(String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy,String limit);,另外一个是public Cursor rawQuery(String sql, String[] selectionArgs)。rawQuery的写法类似上面的execSQL。

    /**
     * 查询数据
     */
    public List getstudentlist() {
// select * from Orders where CustomName = 'Bor'
         Cursor cursor = mDB.query(MySqliteHelper.TABLE_NAME,
                new String[]{"name","sex","age"},
                "CustomName = ?",
                new String[]{"Bor"},
                null, null, null);

        if (cursor.getCount() > 0) {
            List studentList = new ArrayList(cursor.getCount());
            while (cursor.moveToNext()) {
                Student student = parseStudent(cursor);
                studentList.add(student);
            }
            cursor.close();
            return studentList;
        }
        return null;
    }

友情提示:记得 cursor.close();记得 cursor.close();记得 cursor.close();
至于为什么,自己想好吧。

数据库版本更新
数据库版本更新的情况有:新增/删除表、表字段的增加删除
1.首先把数据库的版本号+1;
2.在onCreate中写当前数据库最新版本的建表数据;
3.由于数据库更新对于新安装的用户来说其实是不存在更新一说,因为是第一次安装,所以会直接走onCreate而不会走onUpgrade,所以对于之前安装的用户,数据库更新只走onUpgrade,所以要在onUpgrade对不同版本进行判断,然后执行对应的操作。
添加字段
添加字段我们需要用到 alter [tabname] add column [字段名] [字段类型]

 switch (oldVersion){
            case 1:
                db.execSQL("alter table Student add column studentgrade text");

        }

删除字段
由于SQLite不支持直接在原表上删除字段,如果要用到删除字段的情景,可以新建一个表,字段跟原表一致,但是要去掉要删除的字段,可以使用一下语句完成:

//创建新表
    create table [新表] as select [字段名] from [原表] where 1=2;  
    //复制原表内容
    INSERT INTO [新表] SELECT 字段名] FROM  [原表] ;

然后删除原来的表,修改新表的名称即可

你可能感兴趣的:(Android学习笔记)