持久化技术——SQLite数据库

SQLite数据库

概述

在Android中使用了一个开源的、与操作系统无关的SQL数据库——SQLite数据库。

SQLite是一款轻量级数据库,它的设计目的是嵌入式,因为它占用的资源非常少,在嵌入式设备中可能只需要几百KB,所以它是应用最广泛的嵌入式数据库。满足嵌入式的需求,这也是Android系统采用SQLite数据库的原因之一。

目前的版本是3.27.2

官网: https://www.sqlite.org/index.html

?是网上copy的一段资料,可以简单浏览一下。

出处:http://www.runoob.com/sqlite/sqlite-intro.html

什么是 SQLite?

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

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

为什么要用 SQLite?

  • 不需要一个单独的服务器进程或操作的系统(无服务器的)。

  • SQLite 不需要配置,这意味着不需要安装或管理。

  • 一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。

  • SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。

  • SQLite 是自给自足的,这意味着不需要任何外部的依赖。

  • SQLite 事务是完全兼容 ACID 的,允许从多个进程或线程安全访问。

  • SQLite 支持 SQL92(SQL2)标准的大多数查询语言的功能。

  • SQLite 使用 ANSI-C 编写的,并提供了简单和易于使用的 API。

  • SQLite 可在 UNIX(Linux, Mac OS-X, Android, iOS)和 Windows(Win32, WinCE, WinRT)中运行。

历史

  1. 2000 -- D. Richard Hipp 设计 SQLite 是为了不需要管理即可操作程序。

  2. 2000 -- 在八月,SQLite1.0 发布 GNU 数据库管理器(GNU Database Manager)。

  3. 2011 -- Hipp 宣布,向 SQLite DB 添加 UNQl 接口,开发 UNQLite(面向文档的数据库)。

 

SQLite3数据类型

SQLite3它支持以下五种类型:

  1. NULL
  2. INTEGER
  3. REAL(浮点数字)
  4. TEXT(字符串文本)
  5. BLOB(二进制对象)数据类型。

实际上SQLite3也接受varchar(n)、char(n)、 decimal(p,s) 等数据类型,但在运算或保存时会转成对应的五种数据类型。

SQLite最大的特点是你可以保存任何类型的数据到任何字段中,无论这列声明的数据类型是什么:

  • 可以在Integer类型的字段中存放字符串或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。

但有一种情况例外:

  • 定义为INTEGER PRIMARY KEY的字段只能存储64位整数,当向这种字段中保存除整数以外的数据时,将会产生错误。

 SQLite命令行操作

在命令行下创建数据库:

  1. 启动模拟器后,打开命令行,执行adb shell
  2. 进入所在工程目录
  3. 执行sqlite3创建数据库文件

这时可以使用:

  1. .help可以获取帮助界面   
  2. .quit退出/离开

SQL指令都是以分号()结尾的,如果遇到两个减号(--)则代表注解,sqlite3会略过去。

创建表:

create table film(_id integer primary key autoincrement,

title text, length int, year int, starring text);

语法上基本上和SQL差别不大,就是好多地方会以“."打头

可以参考下:http://www.runoob.com/sqlite/sqlite-syntax.html


注意:请确保你已经拥有root权限。否则在使用SQLite命令行会遇到一定的问题,如遇到Permission denied的提示、访问不了data/data目录等问题

 

一般来说,ADV创建的安卓模拟器是具有root权限的,但是需要注意,如果创建的模拟器版本较高,需要确保后面有(Google APIs),否则你就得考虑一下怎么给你的模拟器root一下的问题了,那就是另一个麻烦事情了。


也可以通过SQLite可视化管理工具快速进行管理,下面提供官方的链接

https://sqlitestudio.pl

 

当然如果已经装了navicat之类的...其实也是支持SQLite的 ← ←


定义数据库的元数据

元数据

元数据(Metadata)是描述其它数据的数据(data about other data),或者说是用于提供某种资源的有关信息的结构数据(structured data)

包android.database.sqlite包含了使用SQLite数据库的所有API
SQL数据库主要概念之一就是Schema——一个关于如何组织数据库的定义 

单表定义表名和列名

//以下是一个为单表定义表名和列名的代码片段
public final class FeedReaderContract {
    public FeedReaderContract(){}
    /* Inner class that defines the table contents */
    public static abstract class FeedEntry implements BaseColumns{
        public static final String TABLE NAME = "entry";
        public static final String COLUMN NAME ENTRY_ID = "entryid";
        public static final String COLUMN NAME TITLE ="title";
        public static final String COLUMN NAME SUBTITLE = "subtitle";
        ...
    }
}

使用SQLiteOpenHelper创建数据库

  • android系统里,数据库存储在private空间里,其他app无法访问,比较安全。
  • SQLiteOpenHelper类里有很多有用的API,当你使用这个类获取数据库的引用的时候,只有当你需要的时候,系统才有可能行耗时较长的操作,比如创建和更新数据库。你只要调用getWritableDatabase()和getReadableDatabase()方法就可以。
  • 因为有些操作可能会long running,所以务必在后台进程调用getWritableDatabase()和getReadableDatabase()方法。
  • 要使用SQLiteOpenHelper类(抽象类),就需要创建一个子类,并且覆写onCreate(),onUpgrade()和onOpen0这几个回调方法,同时,也最好实现onDowngrade()方法。
//创建表和删除表的典型的语句
private static final String SQL_CREATE ENTRIES = 
    "CREATE TABLE"+ FeedEntry.TABLE NAME+ "("+FeedEntry. ID +"INTEGER PRIMARY KEY
    AUTOINCREMENT,"+FeedEntry.COLUMN NAME ENTRY ID + "text,"+
    FeedEntry.COLUMN NAME TITLE + "text)";

private static final String SQL DELETE ENTRIES = 
    "DROP TABLE IF EXISTS "+ FeedEntry.TABLE NAME;
//示例
public class DatabaseHelper extends SQLiteOpenHelper {
    public static final int DATABASE_VERSION = 1;    
    public static final String DATABASE_NAME ="FeedReader.db";
    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE NAME, null, DATABASE_VERSION);
    } 
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL CREATE ENTRIES);) 
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execsQL(SQL DELETE ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {                 
        onUpgrade(db, oldVersion, newVersion);
    }


//使用以下语句创建数据库助手类对象
    FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

插入数据

SQLiteDatabase db =mDbHelper.getWritableDatabase();
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN NAME ENTRYID, id);
values.put(FeedEntry.COLUMN NAME TITLE, title);
values.put(FeedEntry.COLUMN NAME CONTENT, content);
// Insert the new row, returning the primary key value of the new row 
long newRowld;
newRould = db.insert(
    FeedEntry.TABLE NAME,
    FeedEntry.COLUMN NAME NULLABLE,
    values);

增删改查小案例

PetMetaData.java

package com.example.a4_11sqlite;

import android.provider.BaseColumns;

/*元数据的定义*/

//不希望让别的类继承
public final class PetMetaData {
    //不能进行实例化
    private PetMetaData(){}
    //Dog表的定义
    public static abstract class DogTable implements BaseColumns {
        //表名
        public static final String TABLE_NAME="dog";
        //字段
        public static final String NAME="name";
        public static final String AGE="age";
    }
}

DatabaseHelper.java

package com.example.a4_11sqlite;

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

public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DB_NAME="pet.db";
    private static final int VERSION=1;
    private static final String CREATE_TABLE_DOG="create table dog(_id integer primary key autoincrement,"+
            "name text,age integer)";
    private static final String DROP_TABLE_DOG="DROP TABLE IF EXISTS dog";
    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    //数据库表不存在时调用
    @Override
    public void onCreate(SQLiteDatabase db) {
        //用于操作数据库的工具类——SQLiteDatabase
        db.execSQL(CREATE_TABLE_DOG);
    }

    //升级
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL(DROP_TABLE_DOG);
        db.execSQL(CREATE_TABLE_DOG);
    }
}

DatabaseAdapter.java

package com.example.a4_11sqlite;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import java.util.ArrayList;

public class DatabaseAdapter {
    //工具类
    private DatabaseHelper dbHelper;

    //构造方法
    public DatabaseAdapter(Context context) {
        dbHelper = new DatabaseHelper(context);
    }

    //添加
    public void add(Dog dog){
        //获取操作数据库的工具类
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put(PetMetaData.DogTable.NAME,dog.getName());
        values.put(PetMetaData.DogTable.AGE,dog.getAge());

        //插入,参数(表名,可以为NULL的列名,ContentValues)
        //中间的参数是为了ContentValues为空的时候保证Sqlite语句的合法性,如果ContentValues确保不为空,中间参数可以为NULL
        db.insert(PetMetaData.DogTable.TABLE_NAME,null,values);
        db.close();
    }
    //删除
    public void delete(int id){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        //删除的条件——
        String whereClause = PetMetaData.DogTable._ID+"=?";
        String[] whereArgs= {String.valueOf(id)};
        //删除,参数1表名,参数2条件,参数3条件的值
        db.delete(PetMetaData.DogTable.TABLE_NAME,whereClause,whereArgs);
        db.close();
    }
    //更新
    public void update(Dog dog){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put(PetMetaData.DogTable.NAME,dog.getName());
        values.put(PetMetaData.DogTable.AGE,dog.getAge());
        String whereClause=PetMetaData.DogTable._ID+"=?";
        String[] whereArgs= {String.valueOf(dog.getId())};
        //更新,参数1表名,参数2ContentValues,参数3条件,参数4条件的值
        db.update(PetMetaData.DogTable.TABLE_NAME,values,whereClause,whereArgs);

    }
    //根据ID查询
    public Dog findById(int id){
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        String[] colums = {PetMetaData.DogTable._ID,PetMetaData.DogTable.NAME,PetMetaData.DogTable.AGE};
        //参数从左到右分别为:
        // 是否去重,表名,要查询的列,查询条件,查询条件值,分组条件,分组条件值,排序,分页条件)
        Cursor c = db.query(true,PetMetaData.DogTable.TABLE_NAME,colums,PetMetaData.DogTable._ID+"=?",new String[]{String.valueOf(id)},null,null,null,null);
        //返回一个游标
        Dog dog=null;
        //游标移动到下一条
        if (c.moveToNext()){
            dog=new Dog();
            dog.setId(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable._ID)));
            dog.setName(c.getString(c.getColumnIndexOrThrow(PetMetaData.DogTable.NAME)));
            dog.setAge(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable.AGE)));
        }
        c.close();
        db.close();
        return dog;
    }
    //查询所有数据
    public ArrayList<Dog> findAll(){
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        String[] colums={PetMetaData.DogTable._ID,PetMetaData.DogTable.NAME,PetMetaData.DogTable.AGE};
        //参数从左到右分别为:
        // 是否去重,表名,要查询的列,查询条件,查询条件值,分组条件,分组条件值,排序,分页条件)
        Cursor c=db.query(true,PetMetaData.DogTable.TABLE_NAME,colums,null,null,null,null,null,null);
        ArrayList<Dog> dogs=new ArrayList<>();
        Dog dog=null;
        //游标移动到下一条
        while (c.moveToNext()){
            dog=new Dog();
            dog.setId(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable._ID)));
            dog.setName(c.getString(c.getColumnIndexOrThrow(PetMetaData.DogTable.NAME)));
            dog.setAge(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable.AGE)));
            dogs.add(dog);
        }
        c.close();
        db.close();
        return dogs;
    }
}

实体类Dog.java

package com.example.a4_11sqlite;

public class Dog {
    private int id;
    private String name;
    private int age;

    //空
    public Dog() {
    }

    //全
    public Dog(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    //无id
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/add"
        android:onClick="add"
        android:text="添加"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/delete"
        android:onClick="delete"
        android:text="删除"
        app:layout_constraintTop_toBottomOf="@id/add"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/delete"
        android:onClick="delete"
        android:text="删除"
        app:layout_constraintTop_toBottomOf="@id/add"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/update"
        android:onClick="update"
        android:text="修改"
        app:layout_constraintTop_toBottomOf="@id/delete"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/query_id"
        android:onClick="query_id"
        android:text="通过id查询"
        app:layout_constraintTop_toBottomOf="@id/update"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/query_all"
        android:onClick="query_all"
        android:text="全部查询"
        app:layout_constraintTop_toBottomOf="@id/query_id"/>


</android.support.constraint.ConstraintLayout>

 MainActivity.java

package com.example.a4_11sqlite;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private DatabaseAdapter dbAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbAdapter = new DatabaseAdapter(this);
    }
    public void add(View view){
        Dog dog = new Dog("被加入的狗",1);
        dbAdapter.add(dog);
        System.out.println("加入了一只狗");
    }
    public void delete(View view){
        dbAdapter.delete(1);
        System.out.println("删除了一只id为1的狗");
    }
    public void update(View view){
        Dog dog = new Dog(1,"被修改的狗",2);
        dbAdapter.update(dog);
        System.out.println("修改了一只狗");
    }
    public void query_id(View view){
        Dog dog = dbAdapter.findById(1);
        System.out.println("查询id为1的结果是:"+dog);
    }
    public void query_all(View view){
        ArrayList<Dog> dogs = dbAdapter.findAll();
        int size = dogs.size();
        for(int i = 0;i<size;i++){
            System.out.println("查询全部的结果为:"+dogs.get(i));
        }
    }
}

测试效果 

添加

id查询 

 

修改 

 

点击全部查询修改情况

 

删除

通过ID查询,id为1的狗已经不存在了

然后点击两次添加,再次点击查询全部


 

使用原生SQL语句

之前的案例利用dbAdapter调用封装好的增删改查对应方法实现了对SQLite数据的增删查改操作。

但是很多场景下,这些封装好的方法太过死板,只能实现基本操作,不能满足复杂需求。

能不能使用原生SQL语句来进行数据库操作?显然是可以的。


类似JAVA JDBC中的executeQuery和executeUpdate方法,安卓中也有两个方法可以输入参数SQL语句。

  1. rawQuery()方法:用于执行查询语句。
  2. execSQL()方法可以执行插入、更新、删除,或建立表单之类有更改行为的SQL语句。

在使用它们之前还需要完成一个操作:

  • 通过databaseHelper.getWritableDatabase()或databaseHelper.getReadableDatabase()获取SQLiteDatabase对象

DatabaseHelper工具类仍为?部分的代码,下面提供四种操作的参考代码,效果和之前一样,就不演示了。

读者可以自行建四个按钮,然后在点击事件里分部调用下面几个方法并传入对应参数进行测试。

实际上下面的代码也可以考虑把sql作为一个参数,这样更加灵活一些,可以提高代码复用性。

插入

    public void rawAdd(Dog dog){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        String sql="insert into dog(name,age) values(?,?)";
        Object[] args={dog.getName(),dog.getAge()};
        db.execSQL(sql,args);
        db.close();
    }

删除

    public void rawDelete(int id){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        String sql="delete from dog where _id=?";
        Object[] args={id};
        db.execSQL(sql,args);
        db.close();
    }

修改(更新)

    public void rawUpdate(Dog dog){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        String sql="update dog set name=?,age=? where _id=?";
        Object[] args={dog.getName(),dog.getAge(),dog.getId()};
        db.execSQL(sql,args);
        db.close();
    }

查询

    public ArrayList<Dog> rawFindAll(){
        SQLiteDatabase db= dbHelper.getReadableDatabase();
        String sql="select * from dog";
        Cursor c=db.rawQuery(sql,null);
        ArrayList<Dog> dogs=new ArrayList<>();
        Dog dog=null;
        while (c.moveToNext()){
            dog=new Dog();
            dog.setId(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable._ID)));
            dog.setName(c.getString(c.getColumnIndexOrThrow(PetMetaData.DogTable.NAME)));
            dog.setAge(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable.AGE)));
            dogs.add(dog);
        }
        c.close();
        db.close();
        return dogs;
    }

使用事务

尽管SQLite是轻量级的数据库,但是它的功能依旧强大,和别的标准数据库一样,它同样提供了事务支持,遵循ACID原则

    //事务处理案例
    public void transaction(){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        db.beginTransaction();//开始事务
        try{
            db.execSQL("insert into dog(name,age) values('事务处理狗1','1')");
            db.execSQL("insert into dog(name,age) values('事务处理狗2','2')");
            db.setTransactionSuccessful();//设置事务成功的标志
        }finally {
            db.endTransaction();//(结束)提交事务,检查标记,如果为flase,回滚。
        }
        db.close();
    }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云无心鸟知还

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值