Android 学习之《第一行代码》第二版 笔记(十四)详解持久化技术(二)

持久化技术之SQLite 数据库存储

一、SQLite数据库

SQLite是一款轻量级的关系型数据库,运算速度非常快,占用资源少。支持标准的SQL语法,遵循数据库的ACID事务。不用设置用户名和密码即可使用。

二、SQLiteOpenHelper帮助类

  1. 这是一个抽象类,使用需要创建一个类继承它,并重写两个抽象方法(onCreate(…) && onUpgrade(…)),在这两个方法中去实现创建、升级数据库的逻辑。
  2. 两个重要方法:getReadableDatabase()getWritableDatabase()
    这两个方法都可以创建或打开一个现有的数据库,并返回一个可对数据库进行读写操作的对象。
  3. 两个构造函数,一般使用参数少的那个。
    public 类名 (…) 四个参数:
    参数一:Context;
    参数二:(String)数据库名;
    参数三:允许查询数据的时候返回一个自定义的Cursor,一般传入null;
    参数四:当前数据库的版本号。
  4. 数据库文件会存放在/data/data//databases/目录下
  5. 使用 adb shell 来对数据库和表的创建情况进行检查。

三、对数据库进行CRUD操作

C添加(Create)R查询(Retrieve)U更新(Update)D删除(Delete)
使用 getReadableDatabase()getWritableDatabase() 返回的 SQLiteDatabase 对象对数据进行CRUD操作。

1. 添加数据 insert(…)方法:3个参数

参数一:表名;
参数二:在未指定添加数据的情况下给某些可为空的列自动赋值NULL;
参数三:ContentValues对象 提供一系列put(列名,数据)方法重载。

2. 更新数据 update(…)方法:4个参数

参数一:表名;
参数二:ContentValues对象 提供一系列put(列名,数据)方法重载;将要修改的数据放入其中;
参数三:对应于SQL语句的WHERE部分,这里表示更新所有name等于?的行?是一个占位符,通过参数四赋值;
参数四:字符串数组 来指定相应内容。
第三、四个参数用于约束更新某一行或某几行中的数据,不指定的话默认更新所有行。

3. 删除数据 delete(…)方法:三个参数

参数一:表名;
参数二:WHERE条件;
参数三:字符串数组。
第二、三个参数用于约束删除某一行或某几行中的数据,不指定的话默认删除所有行。

4. 查询数据 query(…)方法:七个参数

参数一:表名;
参数二:用于查询哪几列,不指定则默认查询所有列;
第三(WHERE)、四(为占位符提供具体的值)个参数用于约束查询某一行或某几行中的数据,不指定的话默认查询所有行;
参数五:用于指定需要group by的列,不指定则表示不对查询结果进行group by操作;
参数六:对group by 之后的数据进行进一步的过滤,不指定则表示不过滤;
参数七:对查询结果指定排序方式,不指定则表示使用默认的排序方式。

四、Demo 代码

1. 效果界面:

Android 学习之《第一行代码》第二版 笔记(十四)详解持久化技术(二)_第1张图片
Android 学习之《第一行代码》第二版 笔记(十四)详解持久化技术(二)_第2张图片

2. 具体代码:

A.)activity_main.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.thinkpad.databasetest.MainActivity">
    
    <Button
        android:id="@+id/create_database"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Create database"/>
    
    <Button
        android:id="@+id/add_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Add data"/>
    
    <Button
        android:id="@+id/update_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Update data"/>
    
    <Button
        android:id="@+id/delete_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Delete data"/>
    
    <Button
        android:id="@+id/query_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Query data"/>
    
    <EditText
        android:id="@+id/show_query_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
LinearLayout>

B.)MyDatabaseHelper.java

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

public class MyDatabaseHelper extends SQLiteOpenHelper {
    //建表语句1
    public static final String CREATE_BOOK = "create table Book ("
            + "id integer primary key autoincrement,"
            + "author text,"
            + "price real,"
            + "pages inteegr,"
            + "name text)";
    //建表语句2
    public static final String CREATE_CATEGORY = "create table Category("
            + "id integer primary key autoincrement,"
            + "category_name text,"
            + "category_code integer)";

    private Context mContext;

    //构造器 参数一:上下文;参数二:数据库名;参数三:自定义的Cursor,一般为null;参数四:数据库的版本号
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,int version){
        super(context,name,factory,version);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
        Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {
        db.execSQL("drop table if exists Book");
        db.execSQL("drop table if exists Category");
        onCreate(db);
    }
}

C.)MainActivity.java

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);
        //创建表
        Button createBase = (Button)findViewById(R.id.create_database);
        createBase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dbHelper.getWritableDatabase();
            }
        });
        //添加数据
        Button addData = (Button)findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                //开始组装第一条数据
                values.put("name","The Da Vinci Code");
                values.put("author","Dan Brown");
                values.put("pages",454);
                values.put("price",16.96);
                //插入第一条数据
                //参数一:表名;
                //参数二:在未指定添加数据的情况下给某些可为空的列自动赋值NULL;
                //参数三:ContentValues对象 提供一系列put(列名,数据)方法重载
                db.insert("Book",null,values);
                //开始组装第二条数据
                values.put("name","The Lost Symbol");
                values.put("author","Dan Brown");
                values.put("pages",510);
                values.put("price",19.95);
                //插入第二条数据
                db.insert("Book",null,values);
            }
        });
        //更新数据 将名字为The Da Vinci Code 的这本书的价格改为10.99
        Button updateButton = (Button)findViewById(R.id.update_data);
        updateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price",10.99);
                //更新数据
                //参数一:表名;
                //参数二:ContentValues对象 提供一系列put(列名,数据)方法重载;将要修改的数据放入其中;
                //参数三:对应于SQL语句的WHERE部分,这里表示更新所有name等于?的行
                //?是一个占位符,通过参数四:字符串数组 来指定相应内容。
                //第三、四个参数用于约束更新某一行或某几行中的数据,不指定的话默认更新所有行
                db.update("Book",values,"name=?",new String[]{"The Da Vinci Code"});
            }
        });
        //删除数据
        //参数一:表名;
        //参数二:WHERE条件;
        //参数三:字符串数组;
        //第二、三个参数用于约束删除某一行或某几行中的数据,不指定的话默认删除所有行
        Button deleteButton = (Button)findViewById(R.id.delete_data);
        updateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                db.delete("Book","pages > ? ",new String[]{"500"});
            }
        });
        //查询数据
        //参数一:表名;
        //参数二:用于查询哪几列,不指定则默认查询所有列;
        //第三(WHERE)、四(为占位符提供具体的值)个参数用于约束查询某一行或某几行中的数据,不指定的话默认查询所有行;
        //参数五:用于指定需要group by的列,不指定则表示不对查询结果进行group by操作;
        //参数六:对group by 之后的数据进行进一步的过滤,不指定则表示不过滤;
        //参数七:对查询结果指定排序方式,不指定则表示使用默认的排序方式。
        Button queryButton = (Button)findViewById(R.id.query_data);
        queryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                //查询Book表中所有数据
                Cursor cursor = db.query("Book",null,null,null,null,null,null);
                TextView showQueryText = (TextView)findViewById(R.id.show_query_data);
                if(cursor.moveToFirst()){
                    do{
                        //遍历Cursor对象,取出数据并打印
                        //先通过列名得出索引值,根据索引值获得相应列下的属性值
                        String name = cursor.getString(cursor.getColumnIndex("name"));
                        String author = cursor.getString(cursor.getColumnIndex("author"));
                        int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                        double price = cursor.getDouble(cursor.getColumnIndex("price"));
                        Log.d("MainActivity","book name is" + name);
                        Log.d("MainActivity","book author is" + author);
                        Log.d("MainActivity","book pages is" + pages);
                        Log.d("MainActivity","book price is" + price);
                        String text = name + " " + author;
                        showQueryText.setText(text);
                    }while(cursor.moveToNext());
                }
                cursor.close();
            }
        });
    }
}

五、使用 LitePal 操作数据库

LitePal 是一款开源得到Android数据库框架,采用对象关系映射(ORM)的模式,将一些常用的数据库功能进行了封装。
使用文档:https://github.com/LitePalFramework/LitePal

1. 基本配置说明:

A.)编辑app/build.gradle文件,在dependencies闭包中添加:

implementation 'org.litepal.android:core:3.0.0'

固定部分:implementation ‘org.litepal.android:core:’
变动部分:3.0.0 版本号
B.)在main目录下新建目录assets,再在这个目录下新建文件litepal.xml


<litepal>
    <dbname value="BookStore">dbname>
    <version value="1">version>

    <list>list>
litepal>

C.)配置项目的application(AndroidManifest.xml)


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.thinkpad.litepaltest">

    <application
        android:name="org.litepal.LitePalApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>
        activity>
    application>

manifest>

2. 对象关系映射

将面向对象的语言和面向关系的数据库之间建立一种映射关系。

3. 创建数据库

A.)新建Book类

//Book类会对应数据库中的Book表
//类中的每一个字段对应表中的每一个列
public class Book {
    
    private int id;
    
    private String author;
    
    private double price;
    
    private int pages;
    
    private String name;

    public int getId() {
        return id;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public String getName() {
        return name;
    }

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

B.)将Book类添加到映射模型列表当中(litepal.xml)


<litepal>
    <dbname value="BookStore">dbname>
    <version value="1">version>

    <list>
        <mapping class="com.example.thinkpad.litepaltest.Book">mapping>
        
    list>
litepal>

C.)只要进行任意一次数据库的操作,BookStore.db数据库就会自动创建出来。在主函数的按钮的点击事件中,写一个最简单的数据库操作:Connector.getDatabase();点击按钮,数据库创建成功。

4. 升级数据库

修改想修改的内容,并且将数据库版本号+1即可。
想要给Book表添加一列,则直接修改Book.java在其中添加一个字段,并且设置它的get和set方法,版本号+1即可。
想要添加一张新的表,则创建一个新的类,写好字段及其对应get和set方法,然后将它添加到映射模型列表中,版本号+1即可。

5. 使用LitePal进行CRUD操作

LitePal进行表管理操作时不需要模型类有任何的继承结构,但是进行CRUD操作时就不行,必须要继承自DataSupport类才行。
A.) 添加数据
将Book类加上继承结构:extends DataSupport
创建模型实例,再将所有要存储的数据设置好,最后调用一下save();

Button addData = (Button)findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
	@Override
    public void onClick(View view) {
    	Book book = new Book();
    	book.setName("The Da Vinci Code");
    	book.setAuthor("Dan Brown");
    	book.setPages(454);
    	book.setPrice(16.96);
    	book.setPress("Unknow");
    	book.save();//继承自DataSupport
	}
});

B.) 更新数据
a.)最简单的一种更新方式就是对已存储的对象重新设值,然后重新调用save()即可。
model.isSaved() 方法判断对象是否已经存储。true 已存储 false 未存储
返回true的两种情况:

  • 已经调用过model.save()方法去添加数据,此时的model会被认为已经存储。
  • model对象是通过LitePal提供的查询API查出来的,由于是从数据库中查出来的对象,所有也认为是已存储的对象。
Button updateData = (Button)findViewById(R.id.update_data);
addData.setOnClickListener(new View.OnClickListener() {
	@Override
    public void onClick(View view) {
    	Book book = new Book();
    	book.setName("The Da Vinci Code");
    	book.setAuthor("Dan Brown");
    	book.setPages(454);
    	book.setPrice(16.96);
    	book.setPress("Unknow");
    	book.save();//继承自DataSupport
    	book.setPrice(10.99);
    	book.save();
	}
});

b.)使用updateAll()方法

Button updateData = (Button)findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
	@Override
    public void onClick(View view) {
    	Book book = new Book(); //new 出一个Book实例
    	//设置要更新的数据
    	book.setPrice(14.95);
    	book.setPress("Anchor");
    	//调用updateAll()方法去执行更新操作
    	//该方法可以制定一个约束条件,若不指定,则表示更新所有数据
    	book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");
	}
});

注意:在使用updateAll()时,当要把一个字段的值更新为默认值时是不可以使用上面的方式来set数据的,而是使用setToDefault(列名)方法
C.) 删除数据
a.)方法一:直接调用已存储对象的 delete() 方法。
b.)方法二:调用 DataSupport.deleteAll(…) 方法。
参数一:指定删除哪张表的数据。
剩下的参数:用于指定约束条件,和给占位符赋值。
如若不指定约束条件,则表示删除表中所有数据。

Button deleteData = (Button)findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
	@Override
    public void onClick(View view) {
    	DataSupport.deleteAll(Book.class,"price < ?","15");
	}
});

D.) 查询数据

List<Book> books = DataSupport.findAll(Book.class);//参数指定要查询的表

查询API:

  • Book firstBook = DataSupport.findFirst(Book.class);//获取第一条数据
  • Book lastBook = DataSupport.findLast(Book.class);//获取最后一条数据
  • 可通过连缀查询定制更多查询功能:
  • select()指定查询哪几列数据,对应SQL中的SELECT关键字
  • where()指定查询的约束条件,对应SQL中的WHERE关键字
  • order()指定结果的排序方式 desc降序 asc(默认值)升序,对应SQL中的ORDER BY关键字
  • limit(n)指定查询结果的数量为n条数据。
  • limit(n).offset(m)指定查询结果的偏移量,查询表中第m+1,m+2,…,m+n条数据。
  • limit()和offset()方法共同对应了SQL中的LIMIT关键字
//查询Book表中第11-20条
//满足页数大于400这个条件的name,author和pages这3列数据,
//并将查询结果按照页数升序排列。
List<Book> books = DataSupport.select("name","author","pages")
							  .where("pages > ?","400")
							  .order("pages")
							  .limit(10)
							  .offset(10)
							  .find(Book.class);

E.) LitePal也支持原生的SQL查询,findBySQL(SQL语句,若干参数指定占位符的值),返回的是Cursor对象。


整理学习自郭霖大佬的《第一行代码》
目前小白一名,持续学习Android中,如有错误请批评指正!

你可能感兴趣的:(Android,《第一行代码》,持久化,SQLite数据库,LitePal,新人学习)