第六章 数据存储权方案——详解持久化技术
6.1 持久化技术简介
- 概述:Android 系统中主要提供了3种方式用于简单地实现数据持久化功能,即文件存储、SharedPreference存储以及数据库存储。当然,除了这3种方式之外,你还可以将数据保存在手机的SD卡中,不过使用文件、SharedPreference 或数据库来保存数据会相对更简单一些,
而且比起将数据保存在SD卡中会更加地安全。
6.2 文件存储
6.2.1 将文件存储到文件中
- Context类中提供了一个openFileOutput()方法,这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这
里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data//files/目录下的。第二个参数是文件的操作模式,主要有两种模式可选,MODE_ PRIVATE
和MODE APPEND。其中MODE_ PRIVATE 是默认的操作模式,表示当指定同样文件名的时候,
所写入的内容将会覆盖原文件中的内容,而MODE_ APPEND 则表示如果该文件已存在,就往文
件里面追加内容,不存在就创建新文件。 - 给主活动界面添加EditText控件用于录入数据
<?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"
android:orientation="vertical"
>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/type_something_here"
android:id="@+id/edit"/>
</LinearLayout>
- 修改主活动代码,使其再销毁活动时实现数据的存储:
package com.example.filepersistencetest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.os.Bundle;
import android.widget.EditText;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class MainActivity extends AppCompatActivity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.edit);
}
@Override
protected void onDestroy() {
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
}
public void save(String inputText) { //存储数据逻辑
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE); //获得字节输出流
writer = new BufferedWriter(new OutputStreamWriter(out)); //将字节流转换为字符流
writer.write(inputText); //写出数据
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6.2.2 从文件中读取数据
- 概述:Context 类中还提供了一个openFileInput()方法,用于从文件中读取数据。这个方法要比openFile0utput()简单一些,它只接收一个参数,即要读取的文件名,然后系统会自动到/data/data//files/目录下去加载这个文件,并返回一个
FileInputStream对象,得到了这个对象之后再通过Java流的方式就可以将数据读取出来了。 - 修改主活动代码,编写数据写入方法
package com.example.filepersistencetest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.EditText;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class MainActivity extends AppCompatActivity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.edit);
String inputText = load(); //输入数据
if(!TextUtils.isEmpty(inputText)) {
edit.setText(inputText);
edit.setSelection(1); //设置Edit的光标位置
Toast.makeText(this, "Restoring succeeded", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
}
public void save(String inputText) {
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputText);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String load() { //数据写入方法
FileInputStream in = null;
BufferedReader read = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data"); //设置数据源
read = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = read.readLine()) != null) {
content.append(line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(read != null) {
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
}
6.3 SharedPreferences存储
- 概述:不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的。当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过
**这个键把相应的值取出来。**而且SharedPreferences 还支持多种不同的数据类型存储,存什么数据类型就拿出什么数据类型
6.3.1 将数据存储到SharedPreferences中
- 概述:使用SharedPreference来存储数据,首先的获取到SharedPreferences对象。Android提供了3种方法用于得到SharedPreferences对象。
- 三种获得SharedPreferences对象的方式:
- Context类中的getSharedPreferences()方法:
此方法接收两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个SharedPreferences 文件都是存放在/data/data//shared_ _prefs/
目录下的。第二个参数用于指定操作模式,目前只有MODE _PRIVATE这一种模式可选,它是默认的操作模式,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个
SharedPreferences文件进行读写。 - Activity类中的getPreferences()方法:
这个方法和Context中的getSharedPreferences( )方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名。 - PreferenceManager类中的getDefaultSharedPreferences()方法:
这是一个静态方法,它接收一个Context 参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences 文件。得到了SharedPreferences 对象之后,就可以开始向SharedPreferences文件中存储数据了,主要可以分为3步实现。
- 使用步骤:
- 调用SharedPreferences对象的edit()方法来获取一个SharedPreferences . Editor对象。
- 向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用
putBoolean()方法,添加一个字符串则使用putString()方法,以此类推。 - 调用apply()方法将添加的数据提交,从而完成数据存储操作。
- getDefaultSharedPreferences()方法,使用流程:
为活动设置一个按钮
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/save_data"
android:text="@string/save_data"/>
</LinearLayout>
修改主活动代码,为按钮设置点击事件,点击事件为存储数据
package com.example.sharedpreferencestest;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button saveData = (Button) findViewById(R.id.save_data);
saveData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@SuppressLint("CommitPrefEdits")
//获取editor对象
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
editor.putString("name", "Tom"); //向对象中添加数据
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.apply(); //调用apply方法将添加的数据提交,完成数据的存储
}
});
}
}
- 补充:此方式类似于map集合的存储的方式,它只会为唯一键匹配唯一值,相同键会在第二添加数据时覆盖第一次添加的数据,键相同时如果类型不一样,下面出现的将对上面出现的类型进行覆盖
6.3.2 从SharedPreferences中读取数据
- 为主活动添加一个按钮并为其设置带年纪事件:
package com.example.sharedpreferencestest;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button saveData = (Button) findViewById(R.id.save_data);
saveData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@SuppressLint("CommitPrefEdits")
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.apply();
Button restoreData = (Button) findViewById(R.id.restore_data);
restoreData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SharedPreferences pre = getSharedPreferences("data", MODE_PRIVATE);
String name = pre.getString("name", "");
int age = pre.getInt("age", 0);
boolean married = pre.getBoolean("married", false);
Log.d(TAG, "name is " + name); //将数据打印出来
Log.d(TAG, "age is " + age);
Log.d(TAG, "married is " + married);
}
});
}
});
}
}
结果展示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zd4KwhvY-1631454087612)(C:\Users\过客\AppData\Roaming\Typora\typora-user-images\image-20210803114536805.png)]
6.3.3 实现记住密码功能
- 新控件,CheckBox:这是一个复选框控件,用户可以通过点击的方式来进行选中或取消。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox //添加复选框控件
android:id="@+id/remember_pass"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/remember_password"
android:textSize="18sp" />
</LinearLayout>
- 再主活动设置记住密码的处理逻辑:(先判断是否点击爆粗密码的复选框,点击则通过SharedPreferences对账号密码进行存储,下次登录将其拿出,未点击则清空存储的信息)
package com.example.broadcastbestpractice;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
public class LoginActivity extends AppCompatActivity {
private SharedPreferences pref;
private SharedPreferences.Editor editor;
private EditText accountEdit;
private EditText passwordEdit;
private Button login;
private CheckBox rememberPass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
pref = PreferenceManager.getDefaultSharedPreferences(this);
accountEdit = (EditText) findViewById(R.id.account);
passwordEdit = (EditText) findViewById(R.id.password);
rememberPass = (CheckBox) findViewById(R.id.remember_pass);
login = (Button) findViewById(R.id.login);
boolean isRemember = pref.getBoolean("remember_password", false); //通过存储的数据判断是否为保存账号状态
if(isRemember) { //是则将数据拿出
String account = pref.getString("account", "");
String password = pref.getString("password", "");
accountEdit.setText(account);
passwordEdit.setText(password);
rememberPass.setChecked(true);
}
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String account = accountEdit.getText().toString();
String password = passwordEdit.getText().toString();
if (account.equals("admin") && pass word.equals("123456")) {
editor = pref.edit(); //保存数据
if(rememberPass.isChecked()) { //复选框被选,则对数据进行性存储
editor.putBoolean("remember_password", true);
editor.putString("account", account);
editor.putString("password", password);
}else {
editor.clear();
}
editor.apply();
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}else {
Toast.makeText(LoginActivity.this, "account or password is invalid", Toast.LENGTH_SHORT).show();
}
}
});
}
}
6.4 SQLite 数据库
6.4.1 创建数据库
- 概述:Android为了让我们能够更加方便地管理数据库,专广]提供了一个SQLiteOpenHelper帮助类,
借助这个类就可以非常简单地对数据库进行创建和升级。 - SQLiteOpenHelper类:此类为一个抽象类,含有两个抽象方法,onCreatae()和onUpgrade()
- 实例方法:两方法都可以创建或者打开一个现有数据库(存在则打开,否则则创建),并返回一个可对数据库进行读写操作的对象。不同:
- getReadableDatabase(): 当数据库不可写入时,将以只读的方式打开数据库
- getWritableDatabase(): 当数据库不可写入时,此方法将抛出异常
- 构造方法参数说明:
- SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少一点的那个构造方法即可。
这个构造方法中接收4个参数,第一个参数是Context,这个没什么好说的,必须要有它才能对数据库进行操作。第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。第三个参数允许我们在查询数据的时候返回一个自定义的Cursor, 一般都是传入null。第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。构建出SQLiteOpenHelper 的实例之后,
- 创建数据库需要创建表:需要使用建表语句
![
](C:\Users\过客\AppData\Roaming\Typora\typora-user-images\image-20210805104809457.png) - SQLite的数据类型很简单,只有四种:
- integer 表示整型
- real表示浮点型
- text表示文本类型
- blob表示二进制类型
- 代码实现:(先创建自己的帮助类,让其继承SQLiteOpenHelper帮助类,借助这个类就可以非常简单的对数据库进行创建和升级)
package com.example.databasetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
public class MyDatabaseHelper extends SQLiteOpenHelper { //建表
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
private Context mContext;
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); //将建表语句定义成为一个字符串常量,SQLiteDatabase的execSQL()方法
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show(); //弹窗提示
}
@Override
public void onUpgrade(SQLiteDatabase db, int olderVersion, int newVersion) {
}
}
- 创建数据库:
package com.example.databasetest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
protected MyDatabaseHelper dpHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dpHelper = new MyDatabaseHelper(this,"Book.dp", null, 1); //传参
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dpHelper.getWritableDatabase(); //创建数据库
}
});
}
}
6.4…2 升级数据库
- 概述:通过onUpgrade()方法对数据库进行升级
- 修改MyDatabaseHelper中代码:
package com.example.databasetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
private static final String CREATE_CATEGORY = "create table Category (" //加入category表
+ "id integer primary key autoincrement, "
+ "category_name text, "
+"category_code integer)";
private Context mContext;
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 olderVersion, int newVersion) { //执行更细数据库方法
db.execSQL("drop table if exists Book"); //判断是否存在此表,存在则删除
db.execSQL("drop table if exists Category");
onCreate(db); //上面不删除则此处抛异常
}
}
- 当主活动中方法执行哪里的版本号大于之前的版本号,则会执行帮助类中的onUpgrade方法
6.4.3 添加数据
- 概述:其实我们可以对数据进行的操作无非有4种,即CRUD。其中C代表添加(Create), R代
表查询( Retrieve), U代表更新( Update), D代表删除(Delete)。 - 前面我们已经知道,调用SQLiteOpenHelper的getReadableDatabase( )或getWritable-
Database()方法是可以用于创建和升级数据库的,不仅如此,这两个方法还都会返回一个
SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了。 - SQLiteDatabase对象提供了一个insert()方法,这个方法专门用于添加数据。、
参数:
- 第一个参数:表名,表示我们希望想拿张表添加数据
- 第二个参数:用于在未指定添加数据的情
况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传人null - 第三个参数:ContentValues对象,它提供了一系列的put方法重载 ,用于向ContentValues中添加数据。
- 修改主活动代码为:
package com.example.databasetest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
protected MyDatabaseHelper dpHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dpHelper = new MyDatabaseHelper(this,"BookStore.dp", null, 2);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dpHelper.getWritableDatabase();
}
});
Button addData = (Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() { //添加数据逻辑
@Override
public void onClick(View view) {
SQLiteDatabase db = dpHelper.getWritableDatabase(); //获取SQLiteDatabase对象
ContentValues values = new ContentValues(); //创建 ContentValues对象
values.put("name", "The Da Vinci Code"); //添加数据
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("Book", null, values); //将数据存储进数据库
values.clear();
values.put("name", "The Da Lost Symbol");
values.put("author", "Dan BBrown");
values.put("pages", 510);
values.put("price", 16.95);
db.insert("Book", null, values);
}
});
}
}
6.4.4 更新数据
- 概述:SQLiteDatabase中也提供了一个非常好用的update()方法,用于对数据进行更新,这个方法接收4
个参数,第一个参数和insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是ContentValues对象,要把更新数据在这里组装进去。第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。 - 修改主活动代码:
package com.example.databasetest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
protected MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDatabaseHelper(this, "BookStore.dp", null, 2);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.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);
db.insert("Book", null, values);
values.clear();
values.put("name", "The Da Lost Symbol");
values.put("author", "Dan BBrown");
values.put("pages", 510);
values.put("price", 16.95);
db.insert("Book", null, values);
}
});
Button button = (Button) findViewById(R.id.update_data);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase(); //获取SQLiteDatabase对象
ContentValues values = new ContentValues(); //创建ContentCValues对象
values.put("price", 10.99); //
db.update("Book", values, "name = ?", new String[] {"The Da Vinci Code"}); //没看太懂
}
});
}
}
6.4.5 删除数据
- 概述:调用SQLiteDatabase中的delete 方法对数据库中内容进行删除,此方法传入三个参数,第一参数为表名,第二第三参数用于约束删除某一行或某几行的数据
Button delete = (Button) findViewById(R.id.Delete);
delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase(); //获取SQLiteDatabase对像
db.delete("Book", "pages > ?", new String[] {"500"}); //约束条件为删除页数超过500页的书
}
});
}
6.4.6 查询数据
- 概述:SQL的全称是Structured Query Language,翻译成中文就是结构化查询语言,SQlLiteDatabase中提供了一个query()方法,
- 参数:最短的一个方法重载也需要传人7个参数。那我们就先来看一下这7个
参数各自的含义吧。第一个参数不用说,当然还是表名,表示我们希望从哪张表中查询数据。第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据。第五个参数用于指定需要去group by
的列,不指定则表示不对查询结果进行group by操作。第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用于指定查询结果的排序方式,不指定
则表示使用默认的排序方式。
query()方法参数 | 对应SQL部分 | 描述 |
table | from table_name | 指定查询的表明 |
columns | select column1, column2 | 指定查询的列表 |
selection | where column = value | 指定where的约束条件 |
selectionArgs | - | 为where中的占位符提供具体的值 |
groupBy | group by column | 指定需要的group by的列 |
having | having column = value | 对group by 后的结果进一步约束 |
orderBy | order by column1,column2 | 指定查询结果的排序方式 |
6.4.7 使用SQLite操作数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RFPJOvuQ-1631454087614)(C:\Users\过客\AppData\Roaming\Typora\typora-user-images\image-20210807114648012.png)]
6.5 使用LitePal操作数据库
6.5.1 LitePal简介
- 概述:LitePal是一款开源的Android数据库框架,它采用了对象关系映射( ORM )的模式,并将我
们平时开发最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表和增删改查的操作。LitePal的项目主页上也有详细的使用文档,地址是: htps://github.com/LitePalFramework/LitePal
6.5.2 配置LitePal
配置流程;
- 添加依赖:
implementation 'org.litepal.guolindev:core:3.2.3'
- 在main下创建assets目录,然后再此目录下创建文件litepal.xml,修改其代码为:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname vaule="People" />
<version value="1" />
<list>
</list>
<!--<dbname>标签用于指定数据库名 <version>标签用于指定版本号 <list>标签用于指定所有的映射模型-->
</litepal>
解释:其中,标签用于指定数据库名,标签用于指定数据库版本号,
标签用于指定所有的映射模型
- 在注册表中添加如下代码:(将其添加在标签下)
android:name="org.litepal.LitePalApplication"
6.5.3 创建和升级数据库
创建数据库:
- 创建需要存储的数据类:(如此数据库添加book类,将想要存储的数据封装进类里面,为其添加get,set方法)
package com.example.litepaltest;
public class Book {
private int id;
private String author;
private double price;
private int pages;
private String name;
private String press;
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;
}
public String getPress() {
return press;
}
public void setPress(String press) {
this.press = press;
}
}
- 修改litepal.xml文件代码为:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="Book" />
<version value="2" />
<list>
<mapping class="com.example.litepaltest.Book"/> //将类的映射添加进来
<mapping class="com.example.litepaltest.Category"/>
</list>
</litepal>
- 调用LitePal.getDatabase()方法创建数据库:
Button create = (Button) findViewById(R.id.Create);
create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LitePal.getDatabase(); //点击按钮完成数据库的创建
}
});
更新数据库:
- 若只修改某一类的部分数据,则在类中修改:
- 若添加新的数据表,则现创建数据类型,然后将其映射关系添加到litepal.xml下的标签下
创建类:
package com.example.litepaltest;
public class Category {
private int id;
private String categoryName;
private int categoryCode;
public void setId(int id) {
this.id = id;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public void setCategoryCode(int categoryCode) {
this.categoryCode = categoryCode;
}
}
添加映射:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="Book" />
<version value="2" />
<list>
<mapping class="com.example.litepaltest.Book"/>
<mapping class="com.example.litepaltest.Category"/> //将新的表添加进list标签内
</list>
</litepal>
- 最后记得将数据库的版本号加一,完成数据库的更新
6.5.4 使用LitePal添加数据
- 添加继承,让自己的数据类继承LitePalSupptor
public class Book extends LitePalSupport
- 创建所存储数据的对象实例,然后向此对象中添加所需存储的数据,最后调用save() 将数据添加至数据库:
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();
6.5.5 使用LitePal更新数据
- 通过获取对象实例类对所存储的数据进行更新:(可通过LitePal提供的查询API查询出来对象实例,然后对其修改在进行存储,实现数据的更新)
Button upData = (Button) findViewById(R.id.Update);
upData.setOnClickListener(view -> {
Book book = new Book(); //只能对一存储的对像实例进行操作
book.setName("The Lost Symbol");
book.setAuthor("Dan Brown");
book.setPages(510);
book.setPrice(19.96);
book.setPress("UnKnow");
book.save(); //对此对象进行存储
book.setPrice(10.99);
book.save(); //由于此对象已经被存储过了,所以此save只会对此对象的数据进行修改
});
- 通过调用updateAll()方法对数据进行更新:
Button upData = (Button) findViewById(R.id.Update);
upData.setOnClickListener(view -> {
Book book = new Book(); //创建Book对象
book.setPrice(16.95);
book.setAuthor("Dan Brown");
book.setPress("Anchor"); //设置想要更新的数据
book.updateAll("name = ?","The Lost Symbol"); //调用方法对数据进行更新,添加约束条件,此约束条件为书名为The Lost Symbol的对象,执行结果为会将所有书名为此的数据的那三条数据更新
});
- 将数据更新为默认值的操作,LitePal统一提供了一个setToDefault()方法,传入响应的列名即可
Book book = new Book();
book.setToDefault("pages");
book.updateAll();
这段代码的意思是,将所有书的页数都更新为0,因为updateAll()方法中没有指定约束件,因此更新操作对所有数据都生效了。
6.5.6 使用LitePal删除数据
- 通过获取存储的数据对象实例,然后调用delete()方法类删除数据
- 使用deleteAll()方法类删除数据:使用LitePal.deleteAll()方法
Button delData = (Button) findViewById(R.id.Delete);
delData.setOnClickListener(View -> {
LitePal.deleteAll(Book.class, "price < ?", "16.96"); //约束条件为价格低于16.96的书
});
3 若不指定约束条件则删除全部数据
6.5.7 使用LitePal查询数据
- LitePal调用
findAll()
方法,返回一个List集合,将数据库中此表的数据全部拿出
Button query = (Button) findViewById(R.id.Query);
query.setOnClickListener(View -> {
List<Book> books = LitePal.findAll(Book.class);
for(Book book:books) {
Log.d("MainActivity", "book name is " + book.getName());
Log.d("MainActivity", "book author is " + book.getAuthor());
Log.d("MainActivity", "book pages is " + book.getPages());
Log.d("MainActivity", "book price is " + book.getPrice());
Log.d("MainActivity", "book press is " + book.getPress());
}
});
- LitePal的查询API:
- 查询Book表中的第一条数据:
Book firstBook = LitePal.findFirst(Book.class);
- 查询Book表中的最后一条数据
Book lastBook = LitePal.findLast(Book.class);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcctXnaj-1631454087616)(C:\Users\过客\AppData\Roaming\Typora\typora-user-images\image-20210808115747983.png)]