SQLite语法与Android数据库操作

本篇文章可以学到以下内容:

  • SQLite操作以及SQL基本语法
  • Android中的数据库操作(增删改查)
  • Android中ContentProvide内容提供者和ContentResolver内容解析者的使用

项目地址:

https://github.com/liaozhoubei/DatabaseContentProvider

学习android的小伙伴们在使用Android的SQLiteDatabase类进行数据库操作的时候总会有一些力不从心,特别是对于初涉数据库的小伙伴来说更是如此。
这是因为Android的SQLiteDatabase原本就不是依赖于Android而存在的,而是单独的作为一个个体而存在的,有着自己特有的体系和语言,而这就是SQL语法了。

关于SQLite数据库的理论知识网上一搜一大片,这里就不多说。SQLite是一个轻量型的数据库,它对于大型数据库来说功能少,因此只需要学习一些通用的SQL语法就能够轻松掌握,而这些SQL语法对于其他的数据库来说也是基本不变化的。

但SQLite有个缺点,那就是作为轻量级选手的它,如果要保存大量数据会力有不及,因此它在android中适合保存个人设置等没有大量数据的信息。

好了,下面就是正式学习SQLite了,只有掌握了SQLite,掌握SQL语法,才能对Android中的数据库操作运用自如。

SQLite的数据类型

与Java语言一样,SQLite也有其特有的数据类型,当然相比MySQL来说只有5种数据类型算是很少了

  NULL:空值相当于Java中的null
  INTEGER:带符号的整型,相当于Java中的int型
  REAL:浮点数字,相当于Java中float/double型
  TEXT/VARCHAR:字符串文本,相当于Java中String类
  BLOB:二进制对象,相当于Java中的byte数组,用于存放图片、声音等文件

Sqlite3中的约束

SQLite的约束是什么呢?约束就是限定数据库字段的条件,如果有个student数据表,它里面有一个age年龄的属性字段,我们要求数据库保存age这个字段的时候必须有值,不能为空,那么就可以设置为:"age INTEGER NOT NULL"。这句话的意思就是age字段是不能为空的整型

  NOT NULL :非空
  UNIQUE : 唯一
  PRIMARY KEY :主键
  FOREIGN KEY : 外键
  CHECK :条件检查
  DEFAULT : 默认

创建表,此时可对表里的结构和字段进行约束限定,将约束条件放在需要约束的字段之后

创建表

数据库是数据库,数据表是存放在数据库中存放信息的容器,这点大家要区分。

语法:
  create table tabname(col1 type1 [not null][primary key], col2 type2[not null], ··· )
注:
    tabname为表名
    col1、col2为字段名字
    type1、type2为字段类型
    在[]中的约束条件是可以选的

例:

  字段名       类型            长度           约束            说明
  id        INTEGER                       主键,自增长       编号
  name      VARCHAR           20            非空            姓名
  cid       INTEGER                                        所在班级
  age       INTEGER                      大于18且小于60     年龄
  gender    BIT                          默认为1,表示男     性别
  score     REAL                                           成绩

create table student(
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name VARCHAR(20) NOT NULL,
    cid INTEGER,
    age INTEGER CHECK(age>18 and age<60),
    gender BIT DEFAULT(1),
    score REAL);

注:SQL中不区分大小写

insert插入语句

数据库的操作无非四大类,增删改查这几种,因此必须要掌握这几种语句,insert增加也就是在数据表中添加信息

语法

    insert into table1(field1, field2) values(value1, value2);
    语法详解,在表名为table1的表的field1和field2字段分别插入value1和value2的值

例:

  给student表中的各个字段插入值
  insert into student (name, cid, age, gender, score) values ('tom', 1, 20, 1, 80.0);

注:其实可以在student后面不带有字段名,即name,cid···等,但如果不携带字段信息但看insert语句就无法直接明了的知道插入的value是给哪个字段赋值

update更新语句

语法:

update table1 set field1=value1 where 范围
语法详解:set后面是某个字段要更改的值,where表示限定field1字段value值在某个范围内才需要修改
set表示要修改哪个字段的值
where表示修改字段值的范围

例:

 update student set name='jack' where name='tom';
 将student表中name字段中value为tom的值修改为jack

select查询语句

select查询方法可以说是在数据库操作中使用最频繁的操作了,无论是想获取数据还是得知数据库中的信息都必须使用select语句,同时select语句也算得上在SQL语法中最复杂的语句了。

语法:

基础查询:
    select * from table1 where 范围
    语法详解:* 代表通配符,即所有的字段
    select col1, col2, col3 from table1 where 范围;
    查看col1, col2, col3字段的信息

    例:

        select * from student where name="jack"
        查询student表中名字叫jack的所有字段的信息

        select id, name, score from student;
        查询student表中所有学生的编号、名字、分数

限制查询:

LIMIT关键字
select * from table1 LIMIT 5;
查询table1中前5行中的数据,LIMIT关键字后面加数字可限定返回多少条数据
OFFSET关键字
 select * from table1 limit 5 offset 5;
 查询table1中从第5行起的5行数据,OFFSET前面必须有LIMIT限定

例子:
select * from student limit 5;
查询table1中前5行中的数据
select * from limit 5 offset 5;
查询table1中从第5行起的5行数据

排序查询:

 ORDER BY关键字
 select * from table1 order by col1;
 查询table1中所有数据,然后按照col1字段的升序排列(A-Z, 0-9)
  select * from table1 order by col1, col2;
  查询table1中所有数据,然后先按照col1字段的排列,然后按照col2字段排序

  例子:
      select * from student order by score;
      查询student中所有数据,然后按照score字段的升序排列
      select * from student order by name, score;
      查询student中所有数据,然后先按照name字段的排列,然后按照score字段排序

  DESC关键字
  select * from student order by name, score DESC;
  查询student中所有数据,然后先按照name字段的排列,然后按照name字段降序排序
  注:DESC为降序排序,即(Z-A,9-0),DESC是DESCENDING缩写。
      DESC必须跟着ORDER BY关键字后面;
      DESC关键字只应用在直接位于其前面的列名;
      与DESC相反的是ASC(即ASCENDING),在升序排序时可指定ASC,但这一关键字并没什么用处,因为升序是默认的

where过滤语句

where是过滤语句,数据会根据where自居的搜索条件进行过滤,一般情况下where跟在insert、delete、update、select后面。这是一个需要值得注意的语句,使用它能够极大的提高查询数据库的效率,而使用delete语句如果不带上过滤语句,则会把数据表中的所有信息删除!

注:当ORDER BY关键字和where一起使用的时候,ORDER BY应该位于where后面,否则会报错。

where子句后面跟着的是过滤条件,过滤条件可使用逻辑符号,即>、<、=、!=等等逻辑符号,与计算机通用的逻辑符合并没有什么不同

例子:
    select * from student where score>70.0 ORDER BY name;
    查询student表中成绩大于70分的数据,同时按照名字升序排列

高级过滤:
    AND关键字:必须满足前后两个条件的数据
    select * from student where score>60 and score< 100;
    查询student表中成绩大于60分并且小于100分的数据
    OR关键字:只要满足前后任意一个条件即可
    select * from student where score>90 or score<70;
    查询student表中成绩大于90分并且或者小于70分的数据

    AND和OR连用(and的优先级比or要高,两者连用的时候最后在各自的条件中添加圆括号()进行分组)
    select * from student where (score>90 or score<70) and (age>19);

    BETWEEN关键字:
     select * from student where score between 70 and 80;
     查询student表中分数在70至80分之间的数据

    IN关键字:用于指定范围,范围中的每个条件都进行匹配,IN由一组逗号分隔、括在圆括号中的合法值
    select * from student where name in('tom', 'Denesy');
    查询student表中名字为tom和Denesy的数据

    注:在指定要匹配值得清单的关键字中,IN和OR功能相当
    IN可与and和or等其他操作符使用
    IN操作符比一组OR操作符执行快
    可以包含其他的SELECT语句

    NOT关键字:否定其后条件的关键字
    select * from student where not name='tom';
    获取不包含名字为tom的信息
    select * from student where not score>80;
    获取分数不是大于80分的信息

    空值检查:
    select * from student where score is not null;
    查询student表中score字段不为空的信息

delete删除语句

语法:

delete from table1 where 范围
语法详解:从table中删除某一范围内的数据(只能一条一条的删除,不能删除某个字段中的值)
注:不要漏掉where,否则会清空整个表中的信息

例:

  delete from student where name='tom';
  从student表中删除名字为tom的信息

  delete from student where score< 90;
  从student表中删除分数小于90分的信息

Android中的数据库操作

Android中的数据库操作其实只要掌握了上面的SQLite的基本知识,那么就很简单了。
首先我们要创建一个类继承自SQLiteOpenHelper,这个类的作用在于创建数据库和数据表。在Android中已经帮我们封装好了创建数据库的方法,因此只要写好创建数据表的语句就好了,而创建数据表的方法是使用原生的SQL语法,仅仅如此就让你不得不学习SQL语法了。

MyDatabaseHelper代码如下:

    public class MyDatabaseHelper extends SQLiteOpenHelper {
        // 创建Book数据表语法
        public static final String CREATE_BOOK = "create table Book ("
                + "id integer primary key autoincrement, " 
                + "author text, "
                + "price real, " 
                + "pages integer, " 
                + "bookname text)";
        
        // 通过构造方法创建数据库,其中name为数据库名称
        public MyDatabaseHelper(Context context, String name) {
            super(context, name, null, 1);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            // 执行创建数据表的语法
            db.execSQL(CREATE_BOOK);
        }
    ···
    
    }

数据库操作类

为了方便大家阅读,这里将数据库操作都封装到一个类中,同时,将原本需要从外部传入的数据也直接在类中写入。
代码如下:

public class BookDao {
    private MyDatabaseHelper helper;

    public BookDao(Context context) {
        helper = new MyDatabaseHelper(context, "BookStore.db");
    }
    /**
     * 添加数据到Book表中        
     * @return    返回新插入的行号,如果插入失败返回-1
     */
    public long addData() {
        SQLiteDatabase database = helper.getReadableDatabase();
        // 使用anddroid封装的SQL语法
        ContentValues values = new ContentValues();
        values.put("bookname", "The Da Vinci Code");
        values.put("author", "Dan Brown");
        values.put("pages", 454);
        values.put("price", 16.96);
        long insert = database.insert("Book", null, values);
        values.clear();
        values.put("bookname", "The Lost Symbol");
        values.put("author", "Dan Brown");
        values.put("pages", 510);
        values.put("price", 19.95);
        long insert1 = database.insert("Book", null, values);

        return insert;
    }

    /**
     * 更新Book表中的数据
     * @return    返回受影响的行
     */
    public int updateData() {
        SQLiteDatabase database = helper.getReadableDatabase();
        // 使用anddroid封装的SQL语法
        ContentValues values = new ContentValues();
        values.put("price", 10.99);
        int update = database.update("Book", values,  "bookname = ?", new String[] { "The Da Vinci Code" });

        return update;
    }
    /**
     * 删除Book中的数据     
     * @return    返回受影响的行
     */
    public int delete() {
        SQLiteDatabase database = helper.getReadableDatabase();
        // 使用anddroid封装的SQL语法
        int delete = database.delete("Book", "pages > ?", new String[] { "500" });

        return delete;    
    }
    /**
     * 查询Book表中的数据
     */
    public void query() {
        SQLiteDatabase database = helper.getReadableDatabase();
        // 使用原生SQL语法
        Cursor cursor = database.query("Book", null, "price=?", new String[]{"10.99"}, "bookname", "author='Baby lin'", "author");

        if (cursor.moveToFirst()) {
            do {
                String bookname = cursor.getString(cursor.getColumnIndex("bookname"));
                String author = cursor.getString(cursor.getColumnIndex("author"));
                int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                double price = cursor.getDouble(cursor.getColumnIndex("price"));
                System.out.println("书名:" + bookname + "  作者:" + author + "  页数" + pages + "   价格" + price);
            } while (cursor.moveToNext());
        }
        cursor.close();    
    }    
}

上面的代码虽然有些长,但其功能却很简单,也就是实现了数据库的增删查改这是个方法。

首先我们看到每个方法都有这么一行代码:

SQLiteDatabase database = helper.getReadableDatabase();

这一行代码是通过MyDatabaseHelper使用父类SQLiteOpenHelper 中有两个非常重要的实例方法,getReadableDatabase() 和getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则将出现异常。

详解addData()方法

其次,在addData()插入数据中有ContentValue这个类,这个类是用来存储要插入数据表中的数据的。
举个比较熟悉的例子,如ArrayList数据列表,我们可以在它里面存储String或者HashMap<key, value>,而ContentValue就是类似ArrayList这样的容器。
实际上ContentValue里面存储的也是HashMap<key, value>。
通过ContentValue存储数据,然后再使用Android封装的insert方法,能够解析其中的数据,然后保存到数据表中。
其实深入Android中的insert方法,可以看到Android只是将insert方法中的参数取出还原为SQL原生语句,然后保存到数据表中。那么怎么在Android使用原生的SQL方法呢?android在SQLiteDatabase对象中封装了execSQL()方法,可直接使用,当然,execSQL()方法又有完全使用SQL语句,一半使用SQL语句一半使用Android参数的方法。下面是使用原生的SQL语句的代码:

public long addData() {
    SQLiteDatabase database = helper.getReadableDatabase();
    
    // 使用原生SQL语法
    database.execSQL("insert into Book(bookname, author, pages, price) values('The Da Vinci Code', 'Dan Brown', 454, 16.96)");
    
    // 使用原生与android封装方法,在values(?,?,?,?)有4个问号,
    // 代表的是占位符,分别对应后面的String数组中的四个值。
    database.execSQL("insert into Book(bookname, author, pages, price) values(?, ?, ?, ?)", 
            new String[]{"The Lost Symbol", "Dan Brown", "510", "19.95"});
    return 0;
}

我们看到如果使用原生的SQL语句来保存数据库,代码量减少了很多。但是android为什么推荐我们使用它封装的方法呢?这是因为使用原生SQL语法不能获取返回值,因此一但出现问题,会造成App崩溃;同时使用SQL语句的时候不能拼错单词或出现其他问题,否则也会崩溃。

同时我们看到使用SQL语句中保持的数字都是String,与数据表中的限定的值不同,这是因为在保存过程中只要数据类型正确,会自动将"510"还原为整数型。

详解query()方法

除此之外,需要特别注意的便是查询数据库的query()方法

query(String table, String[] columns, String selection,
        String[] selectionArgs, String groupBy, String having,
        String orderBy) ;

这几个参数的意思是:

    table:指定查询的表名,对应:from table_name
    columns:指定查询的列名,对应select column1, column2
    selection:指定where 的约束条件,对应:where column = value
    selectionArgs:为where 中的占位符提供具体的值
    groupBy:指定需要group by 的列,对应:group by column
    having:value 对group by 后的结果进一步约束,对应:having column = value
    orderBy:指定查询结果的排序方式,对应:order by column1, column2

这些参数中在SQL语法中都是有的,其后面对应的是相应的SQL语句。
group By这个关键字是归组的意思,用于与 SELECT 语句一起使用,来对相同的数据进行分组。在 SELECT 语句中,GROUP BY 子句放在 WHERE 子句之后,放在 ORDER BY 子句之前。
也就是说如果使用GROUP BY子句,那么会见所有相同的字段的值合并为一条信息返回。
HAVING 子句允许指定条件来过滤将出现在最终结果中的分组结果。WHERE 子句在所选列上设置条件,而 HAVING 子句则在由 GROUP BY 子句创建的分组上设置条件。

为了获取查询结构,首先插入这些数据:

    database.execSQL(
            "insert into Book(bookname, author, pages, price) values('The Da Vinci Code', 'Dan Brown', 454, 16.96)");
    database.execSQL(
            "insert into Book(bookname, author, pages, price) values('The Lost Symbol', 'Dan Brown', 510, 19.95)");
    database.execSQL(
            "insert into Book(bookname, author, pages, price) values('piao liu chuan shuo', 'Baby lin', 189, 12.99)");
    database.execSQL(
            "insert into Book(bookname, author, pages, price) values('lv bing xun chuan qi', 'Baby lin', 470, 10.99)");
    database.execSQL(
            "insert into Book(bookname, author, pages, price) values('bing yu huo zhi ge', 'Dan Brown', 624, 10.99)");
    database.execSQL(
            "insert into Book(bookname, author, pages, price) values('bing yu huo zhi ge', 'Dan Brown', 624, 10.99)");
    database.execSQL(
            "insert into Book(bookname, author, pages, price) values('wo yao du shu', 'Dan Brown', 510, 10.99)");

他们只有些许的区别,现在我们想查询价格是10.99元,然后按照bookname进行归组,找出组里作者是Baby lin的那条信息,最后按照author排序(实质只有一条信息,没必要排序)。
这条信息由两种写法,一种是上面代码演示的,一种用原生SQL,如下:

Cursor cursor = database.rawQuery(
            "select * from Book where price=10.99 group by bookname having author='Baby lin' order by author", null);

这两种写法都可以的到Cursor这个对象,然后通过Cursor获得数据库中的数据。
Cursor的用法相对固定,通过while遍历,每次使用cursor.moveToNext()将游标移到下一行数据,如果能移动下一行数据则返回True,否则为false。
cursor.getString(int index)或者cursor.getInt(int index)传入相应字段在数据表中的排序便能获得在当前行的字段的值。

修改和删除

修改和删除就比前面两种方法简单多了。
修改update数据表的三种方法:

    // 使用anddroid封装的SQL语法
    ContentValues values = new ContentValues();
    values.put("price", 10.99);
    int update = database.update("Book", values, "bookname = ?", new String[] { "The Da Vinci Code" });
    // 使用原生SQL语法
    // database.execSQL("update Book SET price=10.99 where bookname='The Da
    // Vinci Code' ");
    // 使用原生与android封装方法
    // database.execSQL("update Book SET price=? where bookname=? ", new String[] { "10.99", "The Da Vinci Code" });

删除delete数据表的三种方法:

    int delete = database.delete("Book", "pages > ?", new String[] { "500" });
    // 使用原生SQL语法
    // database.execSQL("delete from Book where pages > 500");
    // 使用原生与android封装方法
    // database.execSQL("delete from Book where pages > ?", new String[] { "500" });

以上就是android中对于数据库操作的全部内容了!

内容提供者

研究完数据库,那么与数据库密切相关的内容提供者就不得不说说的,因为内容提供者也是依赖于数据库的实现的。
内容提供者也就是将当前应用的数据提供出去给其他的app使用,这是一个很好的功能,可惜一般情况下没有人会使用你的应用的信息,因为不知道是不是有陷阱。当然啦,支付宝、微博这些肯定会有人用的。
言归正传,我们来说说如何构建一个内容提供者,在这里以上一个项目的数据库作为内容提供者。

创建一个类继承自ContentProvider

ContentProvider有六个方法必须实现,他们是onCreate()和getType()以及数据库的增删查改四个方法,其中他们每个方法都会使用的Uri参数,这个参数是调用ContentResolver内容解析者的增删改查方法时传递过来的。

我们首先创建MyContentProvider继承ContentProvider。
然后在AndroidManifest.xml的application中添加内容提供者的注册清单:

    <provider
        android:name="com.example.databasecontentprovider.MyContentProvider"
        android:authorities="com.example.databasecontentprovider.provider"
        android:exported="true" >
    </provider>

其中android:name是ContentProvider的全类名,android:authorities 属性中指定了该内容提供器的权限,android:exported表示该应用是否可供外部访问。

然后回到MyContentProvider中,我们写下以下代码:

public class MyContentProvider extends ContentProvider{
    //定一个一个uri路径匹配器
    private static final UriMatcher sUrimatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int QUERYSUCESS = 0;  //ctrl+shift+X  变大写   小写加y
    private static final int INSERTSUCESS = 1;
    private static final int UPDATESUCESS = 2;
    private static final int DELETESUCESS = 3;
    private MyDatabaseHelper databaseHelper;
    
    static {
        sUrimatcher.addURI("com.example.databasecontentprovider.provider", "query", QUERYSUCESS);
        sUrimatcher.addURI("com.example.databasecontentprovider.provider", "insert", INSERTSUCESS);
        sUrimatcher.addURI("com.example.databasecontentprovider.provider", "update", UPDATESUCESS);
        sUrimatcher.addURI("com.example.databasecontentprovider.provider", "delete", DELETESUCESS);
    }

    @Override
    public boolean onCreate() {
        databaseHelper = new MyDatabaseHelper(getContext(), "BookStore.db");
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        int code = sUrimatcher.match(uri);
        if (code == QUERYSUCESS) {
            SQLiteDatabase readableDatabase = databaseHelper.getReadableDatabase();
            Cursor cursor = readableDatabase.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
            return cursor;
        } else {
            
            return null;
        }
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int code = sUrimatcher.match(uri);
        if (code == INSERTSUCESS){
            SQLiteDatabase readableDatabase = databaseHelper.getReadableDatabase();
            long insert = readableDatabase.insert("Book", null, values);
            Uri myUri = Uri.parse("com.youCanDoIt/" + insert);
            return myUri;
        } else {
            return null;
        }
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int code = sUrimatcher.match(uri);
        if (code == DELETESUCESS) {
            SQLiteDatabase readableDatabase = databaseHelper.getReadableDatabase();
            int delete = readableDatabase.delete("Book", selection, selectionArgs);
            return delete;
        } else {
            return 0;
        }
        
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        int code = sUrimatcher.match(uri);
        if (code == UPDATESUCESS){
            SQLiteDatabase readableDatabase = databaseHelper.getReadableDatabase();
            int update = readableDatabase.update("Book", values, selection, selectionArgs);
            return update;
        } else {
            return 0;
        }
    }

}

其中UriMatcher这个实用类,我们可以当作是路径匹配器,它添加访问需要的路径名。
在UriMatcher构造方法中有UriMatcher.NO_MATCH这个参数,表示的是Uri不匹配时返回的code
其中

sUrimatcher.addURI("com.example.databasecontentprovider.provider", "query", QUERYSUCESS);

这个方法,第一个参数这是在AndroidManifest.xml中内容提供者注册清单中的authorities,第二个参数需要匹配的路径名,第三个参数这是路径匹配时所返回的值。
那么query这个路径该怎么访问呢?
这时使用

Uri url = Uri.parse("content://com.example.databasecontentprovider.provider/query");

就能够解析这个路径,其中content://是协议,跟http://差不多。

设置好匹配器,也设置了访问路径,接下来就是提供给外部的权限方法了,现在我们先让其他的app有对数据库增删查改的方法,那么就要重新MyContentProvider中增删查改是个方法。
首先判断sUrimatcher.match(uri)是否能够匹配传过来的uri,如果返回的值刚好就与定义好的值相等,那么就返回这里增删查改方法中要返回的数据类型。
如public Cursor query(···)中要返回一个Cursor游标,那么就返回一个Cursor对象。
而public Uri insert( ···)最特殊,它要返回一个Uri,事实上这个Uri可以自定义,只需要符合相应规则就行,即com.xxxx即可,它最大的功能是让外部应用得到是否成功执行插入数据的操作。

这样一来,一个内容提供者就完成了。
总结一下,有以下几个步骤:

1、定义内容提供者 定义一个类继承contentProvider  
2、在清单文件里面配置一下 内容提供者
3、定义一个Urimatcher 
4、写一个静态代码块 ,添加匹配规则 
5、按照我们添加的匹配规则,暴露想暴露的方法 

内容访问者

内容提供者设置好之后,便要有访问者,内容访问者很简单,只要得到Uri路径,然后使用ContentResolver内容访问者就好了。
我们新建一个MyContentResolver应用,添加增删查改四个按钮,然后在MainActivity中添加以下代码:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button addData = (Button) findViewById(R.id.add_data);
        Button queryData = (Button) findViewById(R.id.query_data);
        Button updateData = (Button) findViewById(R.id.update_data);
        Button deleteData = (Button) findViewById(R.id.delete_data);
        
        addData.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                Uri url = Uri.parse("content://com.example.databasecontentprovider.provider/insert");
                ContentValues values = new ContentValues();
                values.put("bookname", "A Clash of Kings");
                values.put("author", "George Martin");
                values.put("pages", 1040);
                values.put("price", 55.55);
                ContentResolver contentResolver = getContentResolver();
                Uri insert = contentResolver.insert(url, values);
                System.out.println(insert);
            }
        });
        
        queryData.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                Uri url = Uri.parse("content://com.example.databasecontentprovider.provider/query");    
                ContentResolver contentResolver = getContentResolver();
                Cursor query = contentResolver.query(url, null, null, null, null);
                if (query != null) {
                    while (query.moveToNext()){
                        String autor = query.getString(1);
                        String prices = query.getString(2);
                        String pages = query.getString(3);
                        String bookname = query.getString(4);
                        System.out.println(autor + "   " + prices + "   " + pages +"   " + bookname);
                    }
                }
            }
        });
        
        updateData.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                Uri url = Uri.parse("content://com.example.databasecontentprovider.provider/update");
                ContentValues values = new ContentValues();
                values.put("bookname", "A Storm of Swords");
                values.put("pages", 1216);
                values.put("price", 24.05);    
                ContentResolver contentResolver = getContentResolver();
                int update = contentResolver.update(url, values, null, null);
                System.out.println("更新了" + update + "条数据");
            }
        });
        
        deleteData.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                Uri url = Uri.parse("content://com.example.databasecontentprovider.provider/delete");
                ContentResolver contentResolver = getContentResolver();
                int delete = contentResolver.delete(url, "bookname = ?", new String[]{"A Storm of Swords"});
                System.out.println("删除了" + delete + "条数据");
            }
        });
    }

}

在这里,我们通过Activity中的getContentResolver()得到ContentResolver(),然后通过ContentResolver对象传出想要修改或查询的数据。当然在这里就不能够写SQL原生的语句了,只能够按照Android的封装的方法来走。
内容解析者的操作简略为以下几步:
1、通过Uri地址获取Uri对象
2、通过getContentResolver()得到ContentResolver对象
3、通过ContentResolver对象进行增删改查等动作,方法与数据库操作基本相同。

好了,这篇文章就到此结束,写的还停长的,但实质上就只有这三方面的内容:
SQLite操作以及SQL基本语法;
Android中的数据库操作;
Android中的内容提供者和内容解析者;

补充小知识

使用adb进入模拟器:当有多个模拟器开启式使用以下方式
    adb -s emulator-5554 shell
其中emulator-5554是你的设备代号,在dos窗口下使用adb devices列出当前所有的设备

-:普通文件
d:目录
r:可读
w:可写
x:可读可写可执行

直接更改数据库权限为可读可写可执行(在dos窗口中更改数据库全新):chmod 777 xxx.db

创建或打开D盘中的数据库,在dos窗口:sqlite3.exe d:\test.db

然后创建表

此时如果在D盘中原本没有test.db这个数据库,那么在你创建表的时候会直接创建数据库和表

.table  展示数据库中已经拥有的表名

.schema 展示表的创建语句

项目地址:

https://github.com/liaozhoubei/DatabaseContentProvider

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

推荐阅读更多精彩内容