记录:Android SQLite的使用

摘要:计划看一下 contentprovide 的使用。查看一下,需要使用 SQLite。以往都没有使用过,最多得就是在网上摘抄一下demo,就好了。~~(╯﹏╰)。个人其实比较信奉,抄.抄 ..抄…,原文的链接排列在参考链接下面

Sql语句,增、删、改、查

在会用的时候都是使用 Android封装好的函数,直接调用就好。可是没有过多久就忘记其函数参数代表的意思。有需查询一下 ,可是总是不能记住。因为不知道这写参数的意思是什么。在抄袭demo中,作者使用的是 sql语句,抱着好奇加想要简略了解一下,查看了一下。对其常用的操作记录一下。通过 “SQLiteExpertPers“ 直接测试了一下。   1) .创建表   可以通过工具直接创建(注意其主键的设置)。用sql语句创建
创建表
create table if not exists person(_id integer primary key autoincrement, LastName varchar, FirstName varchar, Address varchar, City varchar, Year integer);
  2). 插入数据
insert into 语句用于向表格中插入新的行
语法:
insert into 表名 values (值1,值2,.....)
insert into 表名 (列1,列2,...) values (值1,值2,...)

insert into person values(null,"Adams","John","Oxtord Street","Lodon",1970);
insert into person values(null,"Bush","George","Fifth Ave nue","New York",1975);
insert into person(LastName,FirstName,Address,City,Year) values ("Carter","i ho mas","Changan Steet","Beijing",1980);
insert into person(_id,LastName,FirstName,Address,City,Year) values (null,"Gates","Bill","Xuan wumen 10","Beijing",1985);
insert into person(FirstName,Address,City,Year) values ("Hu YULin","四川","成都",1993);
  注意:使用直接插入值的方法,需要把主键 _id填写为 null,这样就会自增长。不写会把错误。通过给对应列插入对应数据的时候,不为其主键写null,不会报错,并且自增长。
记录:Android SQLite的使用_第1张图片



  3). 更新数据

update 语句用于修改表中的数据
语法:
//更新某一行中的某一列
update 表名 set 列名 = 新值 where 列名 = 某值 
//更新某一行中的若干列
update 表名 set 列名=新值,列名=新值 where 列名=某值

update person set FirstName = "张三" where LastName="Adams" ;
update person set FirstName="李四",Address="China",City="Nanjing" where LastName="Bush";
update person set FirstName="王二",Address="China",City="Diqiu" where LastName="Carter";
记录:Android SQLite的使用_第2张图片



  4). 查询数据

select 语句用于从表中选取数据
语法:
select 列名 from 表名
select * from 表名 (*表示查询所有的列)
//按条件查询 可以使用 and,or
//操作符       |   描述
// =        |   等于
// <>       |   不等于
// >        |   大于
// <        |   小于
// >=       |   大于等于
// <=       |   小于等于
//BETWEEN   |   在某个范围类
// LIKE     |   搜索某种模式

select * from 表名 where 列名 运算符 值
select * from 表名 where 列名 运算符 值 and 列名 运算符 值
select * from 表名 where 列名 运算符 值 or  列名 运算符 值

--select语句
--从 person表中获取 LastName和FirstName
select LastName,FirstName from person;
--从person表中,选取所有的列,* 表示快捷
-select * from person;
--where 语句
--取居住在城市"Beijing"中的人
select * from person where City="Beijing";
--取出Year >= 1980的人
select * from person where Year >= 1980;
--and 和 or 
select * from person where FirstName='王二' and LastName='Carter';
select * from person where FirstName='张三' or FirstName='李四';
--order by(默认为升序)
select LastName,FirstName,Year from person order by Year;



  5). 删除数据

delete 语句用于删除表中的行
语法:
//删除某一行
delete from 表名 where 列名=值
//删除所有行,其表的结构、属性、和索引都是完整的
delete * from 表名

delete from person where FirstName='Hu YULin';
delete from person;

SQLite 创建和打开的方式

在 android 中使用 SQLite数据库的时候,创建和打开数据库的时候不止继承 SQLiteOpenHelper这一种方式。目前发现了三种方式 1. 自定义一个类继承 SQLiteOpenHelper 2. 使用 Context.openOrCreateDatabase() 3. SQLiteDatabase.openOrCreateDatabase() 那么,这三种方式到底有什么不同勒 。?

  1). 第一种方式:继承 SQLiteOpenHelper 打开或创建数据库

特点:可以在升级数据库版本的时候在回到函数里面做相应的操作

  2). 第二种方式:Context.openOrCreateDatabase 打开或创建数据库

特点:可以指定数据文件的操作模式
    /**
     * 第二种方式:Context.openOrCreateDatabase打开或创建数据库
     * 特点:可以指定数据库文件的操作模式
     *
     * @param v
     */
    public void context(View v) {
        /**指定数据库的名称为info2.db,并指定数据文件的操作模式为MODE_PRIVATE**/
        SQLiteDatabase sqLiteDatabase = this.openOrCreateDatabase(Contacts.DATABASE_NAME2, MODE_PRIVATE, null);
        /**查看改对象所创建的数据库**/
        showDataBase(sqLiteDatabase);
    }


       /**
        * 查看手机中由SQLiteDatabase创建的的数据库文件
        */
       public void showDataBase(SQLiteDatabase sqLiteDatabase) {
           List> ll = sqLiteDatabase.getAttachedDbs();
           for (int i = 0; i < ll.size(); i++) {
               Pair p = ll.get(i);
               Log.d(Contacts.TAG, p.first + "=" + p.second);
           }
       }

  3). 第三种方式:Context.openOrCreateDatabase 打开或创建数据库

特点:可以指定数据库的路径
    /**把assets目录下的address.db拷贝到/data/data/com.itheima.mobilesafe/files/address.db*/
    private void copyDB(String dbname) {
        File file=new File(getFilesDir(),dbname);
        if(file.exists() && file.length()>0){
            //数据库已经存在
        }else{

            try {
                InputStream is=getAssets().open(dbname);

                FileOutputStream fos=new FileOutputStream(file);
                int len=0;
                byte[] buffer=new byte[1024];
                while((len=is.read(buffer)) !=-1){
                    fos.write(buffer, 0, len);
                }
                is.close();
                fos.close(); 
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }
//根据路径读取出来
SQLiteDatabase db=SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);

  getWritableDatabase()与getReadableDatabase()
  我们获取数据库实例时可以使用getWritableDatabase()也可以使用getReadableDatabase( )而我们一般会选着使用getWritableDatabase( )获取数据库的实例。下面的 分析源码为 api 15

查看一下SQLiteOpenHelper中的getReadableDatabase()方法:

public synchronized SQLiteDatabase getReadableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling mDatabase.close()
                mDatabase = null;
            } else {
                //如果发现 mDatabase 不为空并且已经打开则直接返回
                return mDatabase;  // The database is already open for business
            }
        }

        if (mIsInitializing) {
            //如果正在初始化则抛出异常
            throw new IllegalStateException("getReadableDatabase called recursively");
        }

        //开始实例化数据库 mDatabase
        try {
            //注意:这里是调用了 getWritableDatabase()方法
            return getWritableDatabase();
        } catch (SQLiteException e) {
            if (mName == null) throw e;  // Can't open a temp database read-only!
            Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
        }

        //如果无法以可读写模式打开数据库,则以只读方式打开
        SQLiteDatabase db = null;
        try {
            mIsInitializing = true;
            String path = mContext.getDatabasePath(mName).getPath();
            //以只读的方式打开数据库
            db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY,
                    mErrorHandler);
            if (db.getVersion() != mNewVersion) {
                throw new SQLiteException("Can't upgrade read-only database from version " +
                        db.getVersion() + " to " + mNewVersion + ": " + path);
            }

            onOpen(db);
            Log.w(TAG, "Opened " + mName + " in read-only mode");
            //为 mDatabase指定新打开的数据库
            mDatabase = db;
            //返回打开的数据库
            return mDatabase;
        } finally {
            mIsInitializing = false;
            if (db != null && db != mDatabase) db.close();
        }
    }

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

    public synchronized SQLiteDatabase getWritableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling mDatabase.close()
                mDatabase = null;
            } else if (!mDatabase.isReadOnly()) {
                  // 如果mDatabase不为空已打开并且不是只读模式 则返回该实例  
                return mDatabase;  // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException("getWritableDatabase called recursively");
        }

        // If we have a read-only database open, someone could be using it
        // (though they shouldn't), which would cause a lock to be held on
        // the file, and our attempts to open the database read-write would
        // fail waiting for the file lock.  To prevent that, we acquire the
        // lock on the read-only database, which shuts out other users.

        boolean success = false;
        SQLiteDatabase db = null;
        //如果mDatabase不为空则加锁,阻止其他操作
        if (mDatabase != null) mDatabase.lock();
        try {
            mIsInitializing = true;
            if (mName == null) {
                db = SQLiteDatabase.create(null);
            } else {
                //打开或创建数据库
                db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
            }

            //获取数据库版本(如果刚创建的数据库,版本为 0)
            int version = db.getVersion();
            //比较版本(我们代码中的版本) mNewVersion为 1
            if (version != mNewVersion) {
                //开启事务
                db.beginTransaction();
                try {
                    if (version == 0) {
                        //执行 onCreate()方法
                        onCreate(db);
                    } else {
                        //如果我们升级了 mNewVersion为2,而原版本为 1 则执行 onUpgrade 方法
                        if (version > mNewVersion) {
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            onUpgrade(db, version, mNewVersion);
                        }
                    }
                    // 设置最新版本  
                    db.setVersion(mNewVersion);
                    // 设置事务成功  
                    db.setTransactionSuccessful();
                } finally {
                    // 结束事务  
                    db.endTransaction();
                }
            }

            onOpen(db);
            success = true;
            // 返回可读写模式的数据库实例  
            return db;
        } finally {
            mIsInitializing = false;
            if (success) {
                // 打开成功  
                if (mDatabase != null) {
                    // 如果mDatabase有值则先关闭 
                    try { mDatabase.close(); } catch (Exception e) { }
                    // 解锁
                    mDatabase.unlock();
                }

                // 赋值给mDatabase  
                mDatabase = db;
            } else {
                // 打开失败的情况:解锁、关闭  
                if (mDatabase != null) mDatabase.unlock();
                if (db != null) db.close();
            }
        }
    }

  几个关键的步骤是,首先判断 mDatabase如果不为空已经打开并不是只读模式则直接返回。否则,如果 mDatabase不为空则加锁,然后开会打开或创建数据库。比较版本,根据版本号来调用相应的方法,为数据库设置新版本号。最后释放旧的部位空的 mDatabase并解锁,把先开的数据库实例赋值予mDatabase,并返回最新实例。

  从上面可以看出,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和 getWritableDatabase()一样的数据库。所以,我们一般使用 getWritableDAtabase()获取数据实例,如果遇到异常,再试图用 getReadableDatabase()获取实例,当这个时候你获取的实例只能读不能写了。

Cursor

  Cursor 是每行的集合,使用 moveToFirst( )定位第一行。你必须知道每一行列的名称,必须知道每一列的数据类型。Cursor 是一个随机的数据源,所有的数据都是通过下标取得。

Cursor 的几个常用方法
isFirst() 是否指向第一条
isLast() 是否指向最后一条
isBeforeFirst() 是否指向第一条之前
isAfterLast() 是否指向最后一条之后
isNull(int columnIndex) 指定列是否为空(列基数为 0 )
isClosed(); 游标是否关闭
close() 关闭游标,释放资源
move(int offset) 以当前位置为参考,移动到指定行
moveToFirst() 移动光标到第一行
moveToLast() 移动光标到最后一行
moveToNext() 移动光标到下一行
moveToPosition(int position) 移动光标到一个绝对的位置
moveToPrevious() 移动光标到上一行
getPosition() 返回当前游标所指向的行数
getColumnCount() 返回所有列的总数
getString(int columnIndex) 返回当前行指定列的值
getColumnIndex(String columnName) 返回指定列的名称,如果不存在返回-1
getColumnIndexOrThrow(String columnName) 从零开始返回指定列名称,如果不存在将抛出IllegalArgumentException 异常。
getCount() 返回Cursor 中的行数
getColumnNames() 返回一个字符串数组的列名
getColumnName(int columnIndex) 从给定的索引返回列名
copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) 在缓冲区中检索请求的列的文本,将将其存储
if(cur.moveToFirst() == false){
    //为空的 Cursor
    return ;
}

//访问 Cursor 的下标获取的其中的数据
int index = cur.getColumnIndex(列名);
String name = cur.getString(index);

//看看如何循环 Cursor 取出我们需要的数据
while(cur.moveToNext()){
    //光标移动成功
    //把数据取出来
}

//当 cur.moveToNext()为假时,将跳出循环,即 cursor数据循环完毕。

//如果喜欢使用 for() 循环,而不想用 while() 可以使用 Google 提供的如下方法:
isBeforeFirst()
//返回游标是否指向之前第一行的位置
isAfterLast()
//返回游标是否指向第最后一行的位置
isClosed()
//如果返回 true 即表示该游戏标己关闭有了以上的方法,可以如此取出数据
for(cur.moveToFirst();!cur.isAfterLast();cur.moveToNext())
{
    int nameColumn = cur.getColumnIndex(People.NAME);
    int phoneColumn = cur.getColumnIndex(People.NUMBER);
    String name = cur.getString(nameColumn);
    String phoneNumber = cur.getString(phoneColumn);
}
//Tip:在Android查询数据是通过 Cursor类来实现的。当我们使用 SQLiteDatabase.query()方法时,就会得到 Cursor 对象,Cursor所指向的就会每一条数据。

Android 封装好的增删改查

  insert操作

/**
*table:要插入数据表的名称
*nullColumnHack:当values 参数为空或者里面没有内容的时候,
*我们 insert是会失败的(底层数据库不允许插入一个空行),为了
*防止这种情况,我们要在这里指定一个列名,到时候如果发现
*将要插入行为空行时,就会将你指定的这个列名设为null,
*然后再向数据中插入
*values:一个 ContentValues对象,雷士一个map通过键值的形式存储
*/
public long insert(String table, String nullColumnHack, ContentValues values) {
        try {
            return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
        } catch (SQLException e) {
            Log.e(TAG, "Error inserting " + values, e);
            return -1;
        }
    }

  这里比较疑惑,nullColumnHack到底是感什么用的,为什么出现在这里。当我们不设定一列的时候,不都是数据库给设为默认值吗? 很多字段设置默认值也是 null,这里显示设置也会null,有什么区别吗? 怎么会显示设置了之后就允许插入了。

  其实 insert 方法最后会去调用 public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) 方法,当我们在 ContentValues 为 null,或者 size <=0时,就会在 sql 语句中添加 nullColumnHack 的设置,我们可以想象一下,如果我们不添加 nullColumnHack 的话,那么我们的 sql语句最终的结果将会雷士 insert into table () values (),这显然是不允许的。而我们如果添加上 nullColumnHack呢? sql 将会变成,insert into table (null) values (null),这样显然格式可以的。


  update操作

    /**
    * table: 要操作的表名
    * values:更新字段为key 为最新的 values值
    * whereClause:where 表达式
    * whereArgs:是前面占位符的实际参数值
    */
    public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
        return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
    }



  query操作
  查询操作相对于其它的操作要复杂些,因为我们经常要面对这各种各样的查询条件,所以系统也考虑这种的复杂性。为我们提供了较为丰富的查询方式

    /**
    *sql:sql语句组织到一个sql语句中
    *selectionArgs:代替实际参数的占位符
    */
    public Cursor rawQuery(String sql, String[] selectionArgs) {

    }   

    /**
    * distinct:指定是否去除重复记录
    * table:执行查询数据的表名
    * columns:要查询出来的列名,相当于 select语句 select关键字后面部分
    * selection:查询条件子句,相当于select语句 where 关键字后面的部分,在条件子句中允许使用占位符
    * groupBy:用于控制分组,相当于 select语句 group by关键字后面的部分
    * having:用于对分组进行过滤,相当于select语句having关键字后面部分
    * orderBy:用于对记录进行排序,相当于select语句order by 关键字后面部分,如:personid desc, age asc
    * limit:用于进行分页,相当于 select语句limit关键字后面的部分
    */
    public Cursor query(boolean distinct, String table, String[] columns,
            String selection, String[] selectionArgs, String groupBy,
            String having, String orderBy, String limit) {

    }



  delete操作

/**
* table:代表想删除数据的表名
* whereClause:删除的条件
* whereArgs:上面条件的占位符
*/
 public int delete(String table, String whereClause, String[] whereArgs) {
 }


demo

  DBHelper继承了 SQLiteOpenHelper,作为维护和管理数据库的基类。DBManager是建立在DBHelper之上,封装了常用的业务方法,Person是我们person表对应的 JavaBean,Activity为显示界面


  先看一下 DBHelper:

public class DBHelper extends SQLiteOpenHelper{
    //数据库名称
    private static final String DATABASE_NAME = "test.db";
    //数据库的版本号
    private static final int DATABASE_VERSION = 1;
    //表名
    private final String TABLE_NAME = "person" ;
    private final String COL_ID = "_id";
    private final String COL_NAME = "name" ;
    private final String COL_AGE = "age" ;
    private final String COL_INFO = "info" ;


    public DBHelper(Context context) {
        //构造函数,直接调用父类的方法,CursorFactory设置为 null,使用默认的 游标工厂
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    //数据库一次创建的时候 会调用onCreate( )
    @Override
    public void onCreate(SQLiteDatabase db) {

        String sql = "create table if not exists ["+TABLE_NAME+"] "+"("+COL_ID+" integer primary key autoincrement, "
                +COL_NAME+" varchar, "
                +COL_AGE+" integer, "
                +COL_INFO+" text)" ;

        Log.i("info","create table = "+sql) ;
        db.execSQL(sql);
    }

    //如果 DATABASE_VERSION值被修改成(只能比前一次大),系统发现
    //现有数据库版本不同,即会调用 onUpgrade
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //版本升级 http://www.cnblogs.com/liqw/p/4264925.html
        db.execSQL("ALTER TABLE person ADD COLUMN other STRING");
    }
}

  正如上面所述,数据库第一创建时 onCreate方法会被调用,我们可以执行创建表的语句,当系统发现版本变换之后,会调用 onUpgrade方法,我们可以执行修改表结构等语句。

  为了方便我们面向对象的使用数据,我们建立了一个Person类,对应 person表中的字段。如下:

public class Person {
    public int _id;
    public  String name;
    public int age;
    public  String  info;

    public Person() {
    }

    public Person(String name, int age,String info) {
        this.name = name;
        this.age = age;
        this.info = info;
    }
}

  然后,我们需要一个 DBManager,来封装我们所有的业务方法,代码如下:

public class DBManager {

    private DBHelper helper;
    private SQLiteDatabase db ;

    public DBManager(Context context){
        helper = new DBHelper(context);
        //因为getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0, mFactory);
        //所以要确保context已初始化,我们可以把实例化DBManager的步骤放在Activity的onCreate里
        db = helper.getWritableDatabase() ;
    }

    public void add(List persons) {
        db.beginTransaction();  //开始事务
        try {
            for (Person person : persons) {
                db.execSQL("INSERT INTO person VALUES(null, ?, ?, ?)", new Object[]{person.name, person.age, person.info});
            }
            db.setTransactionSuccessful();  //设置事务成功完成
        } finally {
            db.endTransaction();    //结束事务
        }
    }


    //更新 person 的 age
    public  void updateAge(Person person){
        ContentValues cv = new ContentValues() ;
        cv.put("age",person.age);
        db.update("person",cv,"name = ?",
                new String[]{person.name});
    }

    public void deleteOldPerson(Person person){
        db.delete("person","age >= ?",
                new String[]{String.valueOf(person.age)});
    }

    public List query(){
        ArrayList persons = new ArrayList<>();
        Cursor c = queryTheCursor();
        if(null != c && c.getCount() > 0){
            while(c.moveToNext()){
                Person person = new Person() ;
                person._id = c.getInt(c.getColumnIndex("_id"));
                person.name = c.getString(c.getColumnIndex("name"));
                person.age = c.getInt(c.getColumnIndex("age"));
                person.info = c.getString(c.getColumnIndex("info"));
                persons.add(person);
            }
        }
        c.close();
        return persons ;
    }
    public Cursor queryTheCursor(){
        Cursor c = db.rawQuery("select * from person",null) ;

        return c;
    }

    public void closeDB(){
        db.close();
    }
}

  我们在DBManager构造方法中实例化DBHelper并获取一个 SQLiteDatabase对象,作为整个应用的数据库实例。在添加多个 Personxinxi时,我们采用了事务处理,确保数据完整性;最后我们提供了一个 closeDB( )方法,释放数据库资源,这一步骤在我们整个应用关闭时执行,这个环节同意被忘记,需要注意。

  最后看一下显示界面的布局与代码:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="add"
        android:onClick="add"/>
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="update"
        android:onClick="update"/>
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="delete"
        android:onClick="delete"/>
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="query"
        android:onClick="query"/>
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="queryTheCursor"
        android:onClick="queryTheCursor"/>
    <ListView
        android:id="@+id/listView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
LinearLayout>
public class SqliteActivity extends AppCompatActivity {

    private DBManager mDBManager ;
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);

//        openSqlite();
        listView = (ListView) findViewById(R.id.listView);
        mDBManager = new DBManager(this);

    }

    @Override
    protected void onDestroy() {
        mDBManager.closeDB();
        super.onDestroy();
    }

    public void add(View view){
        ArrayList persons = new ArrayList();

        Person person1 = new Person("Ella", 22, "lively girl");
        Person person2 = new Person("Jenny", 22, "beautiful girl");
        Person person3 = new Person("Jessica", 23, "sexy girl");
        Person person4 = new Person("Kelly", 23, "hot baby");
        Person person5 = new Person("Jane", 25, "a pretty woman");

        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person4);
        persons.add(person5);

        mDBManager.add(persons);
    }

    public void update(View view){
        Person person = new Person() ;
        person.name = "Jane";
        person.age = 30 ;
        mDBManager.updateAge(person);
    }

    public void delete(View view) {
        Person person = new Person();
        person.age = 30;
        mDBManager.deleteOldPerson(person);
    }

    public void query(View view){
        List persons = mDBManager.query() ;
        ArrayList> list =
                new ArrayList<>() ;

        for (Person person : persons) {
            HashMap map = new HashMap<>() ;
            map.put("name",person.name);
            map.put("info", person.age + " years old, " + person.info);

            list.add(map);
        }

        SimpleAdapter adapter = new SimpleAdapter(this, list, android.R.layout.simple_list_item_2,
                new String[]{"name", "info"}, new int[]{android.R.id.text1, android.R.id.text2});
        listView.setAdapter(adapter);
    }

    public void queryTheCursor(View view) {
        Cursor c = mDBManager.queryTheCursor() ;
        //托付给activity根据自己的生命周期去管理Cursor的生命周期
        startManagingCursor(c);
        CursorWrapper cursorWrapper = new CursorWrapper(c) {
            @Override
            public String getString(int columnIndex) {
                //将简介前加上年龄
                if (getColumnName(columnIndex).equals("info")) {
                    int age = getInt(getColumnIndex("age"));
                    return age + " years old, " + super.getString(columnIndex);
                }
                return super.getString(columnIndex);
            }
        };
        //确保查询结果中有"_id"列
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2,
                cursorWrapper, new String[]{"name", "info"}, new int[]{android.R.id.text1, android.R.id.text2});
        ListView listView = (ListView) findViewById(R.id.listView);
        listView.setAdapter(adapter);
    }

        private void openSqlite(){

        //打开或创建test.db数据库
        SQLiteDatabase db = openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null);
        db.execSQL("DROP TABLE IF EXISTS person");
        //创建person表
        db.execSQL("CREATE TABLE person (_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR, age SMALLINT)");
        Person person = new Person();
        person.name = "john";
        person.age = 30;
        //插入数据
        db.execSQL("INSERT INTO person VALUES (NULL, ?, ?)", new Object[]{person.name, person.age});

        person.name = "david";
        person.age = 33;
        //ContentValues以键值对的形式存放数据
        ContentValues cv = new ContentValues();
        cv.put("name", person.name);
        cv.put("age", person.age);
        //插入ContentValues中的数据
        db.insert("person", null, cv);



        cv = new ContentValues();
        cv.put("age", 35);
        //更新数据
        db.update("person", cv, "name = ?", new String[]{"john"});

        Cursor c = db.rawQuery("SELECT * FROM person WHERE age >= ?", new String[]{"33"});
        while (c.moveToNext()) {
            int _id = c.getInt(c.getColumnIndex("_id"));
            String name = c.getString(c.getColumnIndex("name"));
            int age = c.getInt(c.getColumnIndex("age"));
            Log.i("db", "_id=>" + _id + ", name=>" + name + ", age=>" + age);
        }
        c.close();

        //删除数据
        db.delete("person", "age < ?", new String[]{"35"});

        //关闭当前数据库
        db.close();

        //删除test.db数据库
//      deleteDatabase("test.db");
    }
}

  这里需要注意是 SimpleCurSorAdapter的应用,当我们使用这个适配器的时,我们必须先得到一个 Cursor对象,这里面有几个问题:如何管理Curor的生命周期。如果包装Cursor,Cursor结果集都需要注意什么。

  如果手动去管理Cursor的话会非常麻烦,还有一定风险,处理不当的话运行期间会出现异常。幸好activity为我们提供了 startManagingCursor(Cursor cursor)方法,它会根据Activity的生命周期去管理当前的Cursor对象。startManagingCursor方法会根据Activity的生命周期去管理当前的Cursor对象的生命周期,就是说当Activity停止时他会自动调用Cursor的deactivate方法,禁用游标,当Activity重新回到屏幕时它会调用Cursor的requery方法再次查询,当Activity摧毁时,被管理的Cursor都会自动关闭释放。

  如何包装Cursor:我们会使用到CursorWrapper对象去包装我们的Cursor对象,实现我们需要的数据转换工作,这个CursorWrapper实际上是实现了Cursor接口。我们查询获取到的Cursor其实是Cursor的引用,而系统实际返回给我们的必然是Cursor接口的一个实现类的对象实例,我们用CursorWrapper包装这个实例,然后再使用SimpleCursorAdapter将结果显示到列表上。

  Cursor结果集需要注意些什么:一个最需要注意的是,在我们的结果集中必须要包含一个“_id”的列,否则SimpleCursorAdapter就会翻脸不认人,为什么一定要这样呢?因为这源于SQLite的规范,主键以“_id”为标准。解决办法有三:第一,建表时根据规范去做;第二,查询时用别名,例如:SELECT id AS _id FROM person;第三,在CursorWrapper里做文章:

    CursorWrapper cursorWrapper = new CursorWrapper(c) {  
        @Override  
        public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {  
            if (columnName.equals("_id")) {  
                return super.getColumnIndex("id");  
            }  
            return super.getColumnIndexOrThrow(columnName);  
        }  
    };  

  如果视图从CursorWrapper里获取“_id”对应的列索引,我们就返回查询结果里“id”对应的列索引即可。

  相关图片,在点击 ADD 添加 5 条数据,UPDATE 把最后一条数据 Jane 的 age 从 25改变到 30。DELETE删除最后一条数据,Jane
记录:Android SQLite的使用_第3张图片

参考链接




  1. SQL语句学习入门进阶
  2. Android sqlite 主键自增长
  3. Android中SQLite应用详解
  4. SQLite创建和打开数据库的三种方式
  5. android之SQLite数据库insert操作

你可能感兴趣的:(Android)