数据存储全方案 — 详解持久化技术
持久化技术简介
数据持久化就是指将那些内存中的瞬时设备保存到存储设备中,保证即使在手机或者电脑关机的情况下,这些数据仍然不会丢失。保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化技术则提供了一种机制,可以让数据在瞬时状态和持久状态之间进行转换
Android系统主要提供了3种方式用于简单地实现数据持久化功能,即文件存储、SharedPreference存储及数据库存储。
文件存储
文件存储是Android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有的数据都是原封不动地保存到文件当中的,因而比较适合用于存储一些简单的文本数据或二进制数据。
将数据存储到文件中
Context类中提供给了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数,第一个参数是文件名,在文件创建时使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data//files/目录下的。第二个参数是文件的操作模式,主要有两种模式可选,MODE_PRIVATE和MODE_APPEND。其中MODE_PRIVA是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,而MODE_APPEND则表示文件已经存在,就往里面追加内容,不存在就创建新文件。
新建一个名为FilePersistenceTest的项目
activity_main.xml中的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.demo.filepersistencetest.MainActivity">
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type smothing here" />
</LinearLayout>
MainActivity中的代码:
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);
;
}
//把输入的内容存储到文件中
private 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 (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
首先我们再onCreate()方法中获取了EditText的实例,然后重写了onDestroy()方法,这样就可以保证在活动销毁之前一定会调用这个方法。在onDestroy()方法中我们获取了EditText输入的内容,并调用save()方法把输入的内容存储到文件中,文件命名为data。
从文件中读取数据
Context类还提供了一个openFileInput()方法,用于从文件中读取数据,这个方法值接收一个参数,即要读取的文件名,然后系统会自动到/data/data//files/目录下去加载这个文件,并返回一个FileInputStream对象,得到这个对象之后再通过Java流的方式就可以将数据读取出来
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(inputText.length());
Toast.makeText(this , "Restoring succeeded" , Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
;
}
private 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 (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//读取文件的存储内容
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
}
SharedPreferences存储
使用键值对的方式来存储数据,支持多种不同的数据类型存储
Android中提供了3中方法获得SharedPewferences对象
- Context类中的getSharedPreferences()方法
此方法接收两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data//shared_prefs/目录下第二个参数用于指定操作模式,目前只有MODE_PRIVATE这一种操作模式可选,它使默认的操作模式,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。其他几种操作模式均已被废弃,MODE_WORLD_READBLE和MODE_WORLD_WRITEABLE这两种模式是在Android4.2版本中被废弃的,MODE_MULTI_PROCESS模式是在Android6.0版本中被废弃的
- Activity类中的getPreferences()方法
这个方法和Context中的getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名
- PreferenceManager类中的getDefaultSharePreferences()方法
这是一个静态方法,它接收了一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件。得到SharedPreferences对象之后,就可以开始向 SharedPreferences 文件存储数据了,主要分为以下散步实现:
* 调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象
* 向 SharedPreferences.Editor 对象中添加数据,比如添加一个布尔类型数据就是用putBoolean()方法,添加一个字符串则使用putString()方法,依次类推
* 调用apply()方法将添加的数据提交,从而完成数据存储操作
新建一个SharedPreferencesTest项目,然后修改activity_main.xml中的代码,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.demo.sharedpreferencestest.MainActivity">
<Button
android:id="@+id/save_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save data"
android:textAllCaps="false" />
</LinearLayout>
修改MAinActivity中的代码,如下所示:
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) {
SharedPreferences.Editor editor = getSharedPreferences("data" , MODE_PRIVATE).edit();
editor.putString("name" , "Tom");
editor.putInt("age" , 28);
editor.putBoolean("married" , false);
editor.apply();
}
});
}
}
从SharedPreferences中读取数据
SharedPreferences 对象提供了一系列的个get方法,这些get方法都接收两个参数,第一个参数是键,传入存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,即当传入的键找不到对应的值是会以什么样的值进行返回
修改SharedPreferencesTest项目中的代码。修改activity_main.xml中的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.demo.sharedpreferencestest.MainActivity">
<Button
android:id="@+id/save_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save data"
android:textAllCaps="false" />
<Button
android:id="@+id/restore_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Restore data"
android:textAllCaps="false"
/>
</LinearLayout>
修改MAinActivity中的代码,如下所示:
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) {
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 pref = getSharedPreferences("data" , MODE_PRIVATE);
String name = pref.getString("name" , "");
int age = pref.getInt("age" , 0);
boolean married = pref.getBoolean("married" , false);
Log.d("MainActivity" , "name is " + name);
Log.d("MainActivity" , "age is " + age);
Log.d("MainActivity" , "married is " + married);
}
});
}
}
在还原数据按钮的点击事件中首先通过getSharedPreferences()方法得到 SharedPreferences 对象,然后分别调用它的 getString()、getInt()和getBoolean()方法,去获取前面所存储的姓名、年龄和是否已婚,如果没有找到相应的值,就会使用方法中传入的默认值来代替,最后通过 Log将这些值打印出来
01-13 04:06:57.696 3514-3514/com.demo.sharedpreferencestest D/MainActivity: name is Tom
01-13 04:06:57.696 3514-3514/com.demo.sharedpreferencestest D/MainActivity: age is 28
01-13 04:06:57.696 3514-3514/com.demo.sharedpreferencestest D/MainActivity: married is false
所有之前的数据成功堆取出来了
应用场景:应用程序的偏好设置功能都是用了SharedPreferences
实现记住密码功能
打开 BroadcastBestPractive 项目,来编辑一下登录界面的布局。修改activity_login.xml中的代码,如下所示:
public class LoginActivity extends BaseActivity {
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" , "");
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();
//如果账号是admin,且密码是123456,就认为登录成功
if (account.equals("admin") && password.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 passwprd is invalid" ,
Toast.LENGTH_SHORT).show();
}
}
});
}
}
这个记住密码功能只是一个简单的示例,并不能在实际的项目中直接使用。因为将密码以明文的形式存储在 SharedPreferences文件中是非常不安全的,很容易就会被别人盗取,因此在证实的项目中还需要结合一定的加密算法来对密码进行保护才行
SQLite数据库存储
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少。SQLite不仅支持标准的 SQL 语法,还遵循了数据库的 ACID事务
创建数据库
Android中提供了一个 SQLiteOpenHelper 帮助类,可以简单地对数据库进行创建和升级。
SQLiteOpenHelper是一个抽象类,如果要使用它,就需要创建一个自己的帮助类去继承它。SQLiteOpenHelper有两个抽象方法,分别是onCreate()和onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建,升级数据库的逻辑。
SQLiteOpenHelper中有两个非常重要的实例方法:getReadableDatabase()和getWriteableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已经存在直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()则将出现异常
SQLiteOpenHelper 中有两个构造方法可供重写,一般使用参数较少的构造方法。这个构造方法可以接收4个参数,第一个参数是Context。第二个参数是数据库名,创建数据库时指定的就是这里指定的名称。第三个参数允许我们在查询数据的时候返回一个Cursor,一般传入null。第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。数据库会存放在/data/data//database/目录下。此时,重写的onCreate()方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑
新建一个DatabaseTest新建MyDatabaseHelper类继承自 SQLiteOpenHer类,代码如下:
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 sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_BOOK);//执行创建表语句
Toast.makeText(mContext , "Create succeeded" , Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
修改activity_main.xml中的代码,如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.demo.databasetest.MainActivity">
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"
android:textAllCaps="false" />
</LinearLayout>
修改MAinActivity中的代码,如下:
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 , 1);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dbHelper.getWritableDatabase();
}
});
}
}
升级数据库
修改MyDatabaseHelper中的代码:
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)";
public static final String CREATE_CATEGORY = "create table 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 sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_BOOK);
sqLiteDatabase.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext , "Create succeeded" , Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL("drop table if exists book");
sqLiteDatabase.execSQL("drop table if exists category");
onCreate(sqLiteDatabase);
}
}
在onUpgrade()方法中执行了两条DROP语句,现将两张表删除,然后再调用onCreate()方法重新创建
修改MAinActivity中的代码:
只需修改dbHelper = new MyDatabaseHelper(this , “BookStore.db” , null , 2);
添加数据
对数据进行的操作有4种,即CRUD。其中C代表添加(Create),R代表查询(Retrieve),U代表更新(Update),D代表删除(Delete)
想数据库表中添加数据。SQLiteDatabase中提供了一个insert()方法,这个方法就是专门用于添加数据的。它接收3个参数,第一个参数是表名,第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值为NULL,一般我们用不到这个功能,直接传入null即可。第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列明以及相应的待添加数据传入即可
修改MAinActivity中的代码:
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.put("name" , "The Lost Symbol");
values.put("author" , "Dan Brown");
values.put("pages" , 510);
values.put("price" , 19.95);
db.insert("Book" , null , values);//插入第二条数据
}
});
删除数据
Button deleteButton = (Button) findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book" , "pages > ?" , new String[] {"500"});
}
});
通过第二、第三个参数来指定进删除页数超过500页的书。
查询数据
SQL的全称是 Structured Query Language,翻译成中文就是结构化查询语言。
SQLiteDatabase中提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要7个参数。是第一个参数是表名,表示我们希望从呢个表中查询数据。第二个参数用于指定去查询那几列,如果不指定则默认查询所有列。第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据。第五个参数用于指定需要去group by的列,不指定则不对查询结果进行group by操作。第七个参数用于指定查询结构的排序方式,不指定则表示使用默认的排序方式。
调用query()方法会返回一个Cursor对象,查询到的所有数据豆浆从这个对象中取出
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);
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);
} while (cursor.moveToNext());
}
cursor.close();
}
});
打印log如下:
01-14 08:34:30.069 3832-3832/com.demo.databasetest D/MainActivity: book name is The Da Vinci Code
01-14 08:34:30.069 3832-3832/com.demo.databasetest D/MainActivity: book author is Dan Brown
01-14 08:34:30.069 3832-3832/com.demo.databasetest D/MainActivity: book pages is 454
01-14 08:34:30.069 3832-3832/com.demo.databasetest D/MainActivity: book price is 16.96
使用LitePal操作数据库
LitePal简介
LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发最长用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种表和增删改查的操作。LitePal项目的主页也有详细的使用文档,地址是:https://github.com/LitePalFramework/LitePal
配置LitePal
编辑app/build.gradle文件,在dependencies闭包中添加如下内容:
compile ‘org.litepal.android:core:1.4.1’配置litepal.xml文件。右击app/src/main目录->New->Directory,创建一个assets目录,然后在assets目录下再新建一个litepal.xml文件,接着编辑litepal.xml文件中的内容,如下所示:
创建和升级数据库
litePal采取的是对象关系映射(ORM)的模式。对象关系映射:我们使用的编程语言是面向对象语言,而使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了。(用面向对象的思维来操作数据库,而不用再和SQL语句打交道了) 新建一个名为LitePalTest的项目,activity_main中的代码如下:<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.demo.databasetest.MainActivity">
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"
android:textAllCaps="false" />
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Data"
android:textAllCaps="false" />
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data"
android:textAllCaps="false" />
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query data"
android:textAllCaps="false" />
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update data"
android:textAllCaps="false" />
</LinearLayout>
定义一个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;
}
}
接下来将Book类添加到映射模型列表当中,修改litepal.xml中的代码,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="demo" />
<version value="1“ />
<list>
<mapping class="com.demo.litepaltest.Book"></mapping>
</list>
</litepal>
这里使用标签来声明我们要配置的模型类,注意一定要使用完整的类名。不管哟多少模型类需要映射,都使用同样的方式配置在标签下即可。 修改MAinActivity中的代码,如下所示 :
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Connector.getDatabase();
}
});
}
}
使用LitePal来升级数据库非常简单,只需要该你想改的任何内容,然后将版本号加1就行了。 比如我们向Book类中添加一个press(出版社)列,直接修改Book类中的代码,添加一个press字段即可,如下所示:
public class Book {
private int id;
private String author;
private double price;
private int pages;
private String name;
private String press;
public String getPress() {
return press;
}
public void setPress(String press) {
this.press = 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;
}
}
再添加一个Category表,只需要创建一个Category类就可以了,代码如下:
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;
}
}
改完之后,只需要记得将版本号加1就行了。修改litepal.xml中的代码,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="demo" />
<version value="2" />
<list>
<mapping class="com.demo.litepaltest.Book"></mapping>
<mapping class="com.demo.litepaltest.Category"></mapping>
</list>
</litepal>
使用LitePal添加数据
LitePal进行管理操作时不需要模型类有任何的继承结构,但是进行CRUD操作时就不行了,必须要继承自DataSupport类才行,因此这里我们需要先把继承机构给加上。修改Book类中的代码,如下所示:public class Book extends DataSupport {
...
}
接着向Book表中添加数据,修改MAinActivity中的代码,如下所示:
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();
}
});
首先创建一个Book的实例,然后调用Book类中的各种set方法对数据进行设置,最后再调用book.save()方法就能完成数据添加操作了。
使用LitePal更新数据
对于LitePal来说,对象是否已存储就是根据调用model.isSaved()方法的结构来判断的,返回true就表示已存储,返回false就表示未存储。实际上两种情况下model.siSaved()方法才会返回true,一种情况时已经调用该model.save()方法去添加数据,此时model会被认为是已存储的对象。另一种情况时model对象通过LitePal提供的查询API查出来的,由于是从数据库中查到的对象,试音词也会被认为是已存储的对象Button updateData = (Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Book book = new Book();
book.setName("The Lost Symbol");
book.setAuthor("Dan Brown");
book.setPages(510);
book.setPrice(19.95);
book.setPress("Unknow");
book.save();
book.setPrice(10.99);
book.save();
}
});
这种方式只能对已存储的对象进行操作,限制性比较大,接下来学习一种更加灵巧的更新方式,修改MAinActivity中的代码,输入下所示:
Button updateData = (Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Book book = new Book();
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name = ? and author = ?" , "The Lost Symbol" , "Dan Brown");
}
});
首先new出一个Book的实例,然后直接调用 setPrice()方法和setPress()方法来设置要更新的数据,然后再调用 updateAll()方法去执行更新操作。注意updateAll()方法方法中可以指定一个条件约束,和SQLiteDatabase中的update()方法的where参数部分优点类似,但更加简洁,如果不指定条件语句的话,就表示更新所有数据。 使用updateAll()方法时,还有一个非常重要的知识点是你需要知晓的,就是当你想把一个字段的值更新成默认值时,是不可以使用上面的方式来set数据的。对于想要将数据更新成默认值的操作,LitePal同一提供了一个setToDefault()方法,然后传入相应的列明就可以实现了。比如我们可以写成这样:
Book book = new Book();
book.setToDefault("pages");
book.updateAll();
这段代码的意思是将所有书的页数都更新为0,因为updateAll()方法中没有指定约束条件,因此更新操作对所有数据都生效了
使用LitePal删除数据
有两种购房时,第一种是直接调用以存储对象的delete()方法就可以。也就是说调用save()方法的对象,或者是通过LitePal提供的查询API查出来的对象,都是可以直接使用delete()方法来删除数据的。这种方法很简单。是直接来看另外一种删除数据的方式。 修改MAinActivity中代码,如下所示:Button deleteButton = (Button) findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DataSupport.deleteAll(Book.class , "price < ?" , "15");
}
});
调用DataSupport.deleteAll()方法来删除数据,其中deleteAll()方法的第一个参数用于指定删除那张表中的数据,Book.class就意味着删除Book表中的数据,后面的参数用于指定约束条件,应该不难理解。 deleteAll()方法如果不指定约束条件,就意味着你要删除表中的所有数据,这一点和updateAll()方法比较相似
使用LitePal查询数据
修改MAinActivity中的代码,如下所示:Button queryButton = (Button) findViewById(R.id.query_data);
queryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
List<Book> books = DataSupport.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());
}
}
});
查询结果在logcat中打印如下:
D/MainActivity: book name is The Da Vinci Code
D/MainActivity: book author is Dan Brown
D/MainActivity: book pages is 454
D/MainActivity: book price is 16.96
D/MainActivity: book press is Unknow
除了findAll()方法之外,LitePal还提供了很多其他非常有用的查询API。比如我们想要查询Book表中的第一条数据就 可以这样写:
Book firstBook = DataSupport.findFirst(Book.class);
查询Book表最后一条数据:
Book lastBook = DataSupport.findLast(Book.class);
还可以通过连缀查询来定制更多的查询功能:
select()方法用于指定查询那几列的数据,对应了SQL当中的select关键字。比如只查询name和author这两列的数据,就可以这样写:
List books = DataSupport.select(“name” , “author”).find(Book.class);
where()方法用于指定查询的约束条件,对应SQL当中的where关键字
List books = DataSupport.where(“pages > ?” , “400”).find(Book.class);
order()方法用于指定结果的排序方式,对应了SQL当中的order by关键字
List books = DataSupport.order(“price desc”).find(Book.class);
limit()方法用于指定查询结果的数量
List books = DataSupport.limit(3).find(Book.class);//只查询表的前三调数据
offset()方法用于指定查询结果的偏移量,比如查询表中的第二条、第三条、第四条数,就可以这样写:
List books = DataSupport.limit(3).offset(1).find(Book.class);