21. Android开发笔记:持久化(三):数据库存储

1.SQLited数据库

Android数据库内置了SQLite数据库
在刚开始接触Android的时候,我甚至都不敢相信,Android系统竟然是内置了数据库的!好吧,是我太孤陋寡闻了。SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手SQLite。而SQLite又比一般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。Android正是把这个功能极为强大的数据库嵌入到了系统当中,使得本地持久化的功能有了一次质的飞跃。

2.SQLiteOpenHelper

Android为了让我们能够更加方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建和升级。

  • SQLiteOpenHelper是一个抽象类,需要创建一个自己的帮助类去继承它, 并且必须在自己的帮助类里面重写这两个方法。

    • onCreate() : 实现创建数据库的逻辑
    • onUpgrade() : 升级数据库的逻辑
  • 构造函数
    SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少一点的那个构造方法即可。这个构造方法中接收4个参数,

    • 第一个参数是Context,这个没什么好说的,必须要有它才能对数据库进行操作。
    • 第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。
    • 第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null。
    • 第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。
      构建出SQLiteOpenHelper的实例之后,再调用它的getReadableDatabase()或者getWritableDatabase()方法就能够创建数据库了,
  • SQLiteOpenHelper中还有两个非常重要的实例方法:

    • getReadableDatabase()
    • getWritableDatabase()
      这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。
      不同的是,当数据库不可写入的时候(如磁盘空间已满),

getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,
getWritableDatabase()方法则将出现异常。

  • 创建数据库
    调用这两个方法就能够创建数据库了,数据库文件会存放在/data/data//databases/目录下。
    此时,重写的onCreate()方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。

3.SQLite数据库操作

3.1 创建数据库

创建项目新【DatabaseDemo】,

  • 创建MyDatabaseHelper类,继承自 SQLiteOpenHelper:

public class MyDatabaseHelper extends SQLiteOpenHelper {

    public static final String Creat_Table_Book = "create table Book ( " +
            " id integer primary key autoincrement, " +
            " author text, " +
            " price real, " +
            " pages integer, " +
            " name text, " +
            " category int);";


    private Context mContext;

    public MyDatabaseHelper(@Nullable Context context,
                            @Nullable String name,
                            @Nullable SQLiteDatabase.CursorFactory factory,
                            int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

        db.execSQL(Creat_Table_Book);

        Log.d("db", "onCreate: ");
        Toast.makeText(mContext, "创建数据库成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

  • 创建按钮,点击创建数据库

    布局文件activity_main.xml:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button android:id="@+id/btn_createDatabase"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="创建数据库"/>

</LinearLayout>


public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHepler;
    private static final  String DataBaseName = "BookStore.db"; //数据库名
    private static final  int DataBaseVersion = 1; //数据版本

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

        //获取 SQLiteOpenHelper 实例
        dbHepler = new MyDatabaseHelper(this, DataBaseName, null, DataBaseVersion);

        Button btn_createDatabase = findViewById(R.id.btn_createDatabase);
        btn_createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHepler.getWritableDatabase(); //若无数据库,创建数据库
            }
        });
    }
}

点击按钮,创建一个数据名为BookStore.db的数据库,

  • 检验数据库
    Android SDK 提供工具(位于 Android SDK 安装目录 F:AndroidSdkplatform-tools) 查看数据库, 可以在真机和虚拟机中调试;
    - 运行工程
    - 运行cmd,输入
    • adb shell
    • cd /data/data/com.example.databasedemo/databases
    • ls
    • qlite3 BookStroe.db
    • .table
    • .schema

F:AndroidSdkplatform-tools> adb shell
root@shamu:/ # cd /data/data/com.example.databasedemo/databases
root@shamu:/data/data/com.example.databasedemo/databases # ls
BookStore.db
BookStore.db-journal
root@shamu:/data/data/com.example.databasedemo/databases # qlite3 BookStroe.db                                               
SQLite version 3.8.6.1 2015-05-21 17:24:32
Enter ".help" for usage hints.

sqlite> .table
Book              android_metadata
sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE Book (  id integer primary key autoincrement, author text, price real, pages integer, name text);
sqlite>

3.2 升级数据库

要特别注意:MyDatabaseHelper的两个方法被调用的场景:
- onCreate():数据库不存在时被调用,数据库被创建成功后就不会在被调用
- onUpgrade(): MyDatabaseHelper dbHepler = new MyDatabaseHelper(this, DataBaseName, null, DataBaseVersion);
数据库版本号DataBaseVersion高于之前的版本时才调用
基于以上2个情况:
更新数据库时,onUpgrade()方法中要把之前的版本更新代码注释掉;

3.2.1 给表book新增一个字段pulisher

  • (1) 在MyDatabaseHelperonUpgrade()中添加如下代码:

  @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //版本2:
        db.execSQL("alter table book add column pulisher nvarchar(100)");
     
        Log.d("db", "onUpgrade: " + "数据库升级成功,版本:" + newVersion);
        Toast.makeText(mContext, "数据库升级成功,版本:" + newVersion, Toast.LENGTH_SHORT).show();
    }

  • (2) MainActivity类中,将 DataBaseVersion 由1改为2:

       private static final  int DataBaseVersion = 2;
    

    点击【创建数据库】按钮

  • (3) 检验是否成

sqlite> .schema

CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE Book (  id integer primary key autoincrement,  author text,  price real,  pages integer,  name text,  category int , pulisher nvarchar(100));

增加一个按钮用于手动升级数据库


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button android:id="@+id/btn_createDatabase"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="创建数据库"/>

    <Button android:id="@+id/btn_updateDatabase"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="升级数据库"/>

...


public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHepler;
    private static final  String DataBaseName = "BookStore.db";
    private static int DataBaseVersion = 1;

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

        //获取 SQLiteOpenHelper 实例
        dbHepler = new MyDatabaseHelper(this, DataBaseName, null, DataBaseVersion);

        Button btn_createDatabase = findViewById(R.id.btn_createDatabase);
        btn_createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHepler.getWritableDatabase(); //若无数据库,创建数据库
            }
        });

        Button btn_updateDatabase = findViewById(R.id.btn_updateDatabase);
        btn_updateDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //将数据库版本设置为:2
                DataBaseVersion  = 2;
                //获取 SQLiteOpenHelper 实例
                if (dbHepler != null){
                    dbHepler.close();  //先关闭,再重新赋值,否则还是使用旧版本,抛出异常
                    dbHepler = null;

                    dbHepler = new MyDatabaseHelper( MainActivity.this, DataBaseName, null, DataBaseVersion);
                    dbHepler.getWritableDatabase(); //若无数据库,创建数据库
                }
            }
        });
.....

3.2.1 新增表Category

  • (1) 在MyDatabaseHelperonUpgrade()中:
    注释掉
    db.execSQL("alter table book add column pulisher nvarchar(100)");

    执行 添加代码


  ......
      public  static  final String Create_Table_Category = "create table category(" +
            "id integer primary key autoincrement, " +
            "category_name nvarchar(100)," +
            "category_code integer);";
   ......      
   
  @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //版本2:
        //db.execSQL("alter table book add column pulisher nvarchar(100)");
     
        //版本3:
        db.execSQL(Create_Table_Category);
  
        Log.d("db", "onUpgrade: " + "数据库升级成功,版本:" + newVersion);
        Toast.makeText(mContext, "数据库升级成功,版本:" + newVersion, Toast.LENGTH_SHORT).show();
    }
 ......  
 
  • (2) MainActivity类中,将 DataBaseVersion 由2改为3:

       private static final  int DataBaseVersion = 3;
    

    点击【创建数据库】按钮

  • (3) 检验是否成

sqlite> .table
Book              android_metadata   category

sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE Book (  id integer primary key autoincrement,  author text,  price real,  pages integer,  name text,  category int , pulisher nvarchar(100));
CREATE TABLE category(id integer primary key autoincrement, category_name nvarchar(100),category_code integer);

sqlite>

3.3 插入数据


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button android:id="@+id/btn_createDatabase"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="创建数据库"/>

    <Button android:id="@+id/btn_insert"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="添加数据"/>

    <Button android:id="@+id/btn_query"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="查询数据"/>

    <Button android:id="@+id/btn_update"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="更新数据"/>

    <Button android:id="@+id/btn_delete"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="删除数据"/>

</LinearLayout>


       //添加数据
        Button btn_insert = findViewById(R.id.btn_insert);
        btn_insert.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db =  dbHepler.getWritableDatabase();
                ContentValues values = new ContentValues();

                // 第一条
                values.put("name", "西瓜");
                values.put("author", "Tom Bsa");
                values.put("pages", 230);
                values.put("price", 63.40);
                //第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可。
                db.insert("Book",null, values);

                // 第二条
                values.clear();
                values.put("name", "ABC SONG");
                values.put("author", "Jack DD");
                values.put("pages", 60);
                values.put("price", 50.40);
                values.put("pulisher", "李Lee");
                db.insert("Book",null, values);

                // 第三条
                values.clear();
                values.put("category_name", "Abc");
                values.put("category_code", 1);
                db.insert("Category",null, values);

                // 第四条
                values.clear();
                values.put("category_name", "自然科学");
                values.put("category_code", 2);
                db.insert("Category",null, values);
            }
        });


sqlite> select * from book;
1|Tom Bsa|63.4|230|瑗跨摐||
2|Jack DD|50.4|60|ABC SONG||鏉嶭ee

sqlite> select * from category;
1|Abc|1
2|鑷�劧绉戝�|2
sqlite>

数据库有乱码, 暂时不解决

另外,清空表数据,主键自增初始值恢复为1:


sqlite> update sqlite_sequence SET seq = 0 where name ="book";
sqlite> select * from book;
sqlite> delete from category;
sqlite> update sqlite_sequence set seq=0 where name="category";

3.4 查询数据


        //查询数据
        Button btn_query = findViewById(R.id.btn_query);
        btn_query.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHepler.getWritableDatabase();
                Cursor cursor = db.query("Book", new String[]{"name", "price"}
                        , "name=?", new String[]{"ABC SONG"}
                        , null, null
                        , null);

                while (cursor.moveToNext()) {
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    String price = cursor.getString(cursor.getColumnIndex("price"));

                    Toast.makeText(MainActivity.this, "name:" + name + " | price:" + price, Toast.LENGTH_SHORT).show();
                }

                cursor.close(); //记得关闭指针
            }
        });

3.5 更新数据


        //更新数据
        Button btn_update = findViewById(R.id.btn_update);
        btn_update.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHepler.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price", 70);  //只更新标明的字段,其它字段值不会被修改
                db.update("Book", values, "name=?", new String[]{"ABC SONG"});
            }
        });


sqlite> select * from book where name="ABC SONG";
7|Jack DD|70.0|60|ABC SONG||鏉嶭ee
sqlite>

3.6 删除数据


        //删除数据
        Button btn_delete = findViewById(R.id.btn_delete);
        btn_delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHepler.getWritableDatabase();
                db.delete("Book", "pages>?", new String[]{"100"});
            }
        });

删前:

sqlite> select * from book;
6|Tom Bsa|63.4|230|瑗跨摐||
7|Jack DD|70.0|60|ABC SONG||鏉嶭ee

删后:

sqlite> select* from book;
7|Jack DD|70.0|60|ABC SONG||鏉嶭ee
sqlite>

4.使用原生的SQL

  • db.execSQL() : 使用于:增、删、改
  • db.rawQuery(): 使用于:查询

        //使用SQL插入数据
        Button btn_insert_sql = findViewById(R.id.btn_insert_sql);
        btn_insert_sql.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHepler.getWritableDatabase();
                db.execSQL("insert into book(name, author, price, pages) values (?,?,?,?)"
                        , new String[]{"AAA","Jack", "63.55","100"});

                db.execSQL("insert into book(name, author, price, pages) values (?,?,?,?)"
                        , new String[]{"BBB","Jack", "80.55","100" });
            }
        });

        //使用SQL查询
        Button btn_query_sql = findViewById(R.id.btn_query_sql);
        btn_query_sql.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHepler.getWritableDatabase();
                Cursor cursor = db.rawQuery("select * from book where pages=?", new String[]{"100"});

                while (cursor.moveToNext()) {
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    String price = cursor.getString(cursor.getColumnIndex("price"));

                    Toast.makeText(MainActivity.this, "name:" + name + " | price:" + price, Toast.LENGTH_SHORT).show();
                }

                cursor.close(); //记得关闭指针
            }
        });

原文地址:https://www.cnblogs.com/easy5weikai/p/12586795.html