《第一行代码 第二版》 第 六 章
《第一行代码 第二版》
本博客是对第一行代码的精简总结,仅供个人学习使用。如需系统学习请购买正版或者电子书籍。
链接附上 🔗图灵社区:https://www.ituring.com.cn/book/2744/
第 6 章 数据存储全方案——详解持久化技术
6.1 持久化技术简介
保存在内存中的数据是瞬时状态的,保存在存储设备中的数据是处于持久状态的,文件存储、SharedPerference存储和数据库存储三种方式。(1)文件存储就是利用输入输出流等Java常用的方法将文件写入,可以用于存储文本或二进制数据;(2)SharedPreferences用于存储键值对,可以支持不同类型的数据;(3)SQLite可以存储复杂的关系型数据库,实现创建、增删查改等功能。(4)作者维护的一个开源库LitePal,相比于SQLite,它采用对象关系映射(ORM)的模式,对数据库功能进行封装,较为简单。利用对象的思想去解决关系型数据库的问题,什么时候自己能维护一个就厉害了。
6.2 文件存储
Context
类中提供了一个openFileOutput()
方法,可以用于将数据存储到指定的文件中。
两个参数
第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/
第二个参数是文件的操作模式,主要有两种模式可选
- MODE_PRIVATE
- MODE_PRIVATE是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容
- MODE_APPEND
- MODE_APPEND则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件。
其实文件的操作模式本来还有另外两种:
MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE,这两种模式表示允许其他的应用程序对我们程序中的文件进行读写操作,不过由于这两种模式过于危险,很容易引起应用的安全性漏洞,已在Android 4.2版本中被废弃。
文件存储是Android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的文本数据,就需要定义一套自己的格式规范,这样可以方便之后将数据从文件中重新解析出来。
openFileOutput()
方法返回的是一个FileOutputStream
对象,得到了这个对象之后就可以使用Java流的方式将数据写入到文件中了。以下是一段简单的代码示例,展示了如何将一段文本内容保存到文件中:
public void save() {
String data = "Data to save";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
如果你已经比较熟悉Java流了,理解上面的代码一定轻而易举吧。这里通过openFileOutput()
方法能够得到一个FileOutputStream
对象,然后再借助它构建出一个OutputStreamWriter
对象,接着再使用OutputStreamWriter
构建出一个BufferedWriter
对象,这样你就可以通过BufferedWriter
来将文本内容写入到文件中了。
总结
(一)文件存储
不做格式化处理,原封不动存储文本数据和二进制数据。
1.1.将数据存储到文件中
第一步:openFileOutput将数据存储到指定的文件当中去。第一个是文件名,第二个是文件的操作模式,MODE_PRIVATE为默认,会覆盖之前内容,APPEND会追加内容。默认存储到/data/data/pacak name/file下
第二步:获得FileOutputStream对象,借助他构建OutputStreamWriter对象,在构建BufferedWriter对象,使用Java流方式将其写到文件当中去。
第三步:输入结束关闭输入流。
第四步:onDestroy中复写存储方法,确保活动销毁之前调用save方法。
查看好像不太好查看,在Android Device Monitor中没有看到file文件下的data。(可能需要刷机吧)
6.2.1 从文件中读取数据
类似于将数据存储到文件中,Context
类中还提供了一个openFileInput()
方法,用于从文件中读取数据。这个方法要比openFileOutput()
简单一些,它只接收一个参数,即要读取的文件名,然后系统会自动到/data/data/FileInputStream
对象,得到了这个对象之后再通过Java流的方式就可以将数据读取出来了。
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();
}
在这段代码中,首先通过openFileInput()
方法获取到了一个FileInputStream
对象,然后借助它又构建出了一个InputStreamReader
对象,接着再使用InputStreamReader
构建出一个BufferedReader
对象,这样我们就可以通过BufferedReader
进行一行行地读取,把文件中所有的文本内容全部读取出来,并存放在一个StringBuilder
对象中,最后将读取到的内容返回就可以了。
对字符串进行非空判断的时候使用了TextUtils.isEmpty()
方法,这是一个非常好用的方法,它可以一次性进行两种空值的判断。当传入的字符串等于null
或者等于空 字符串的时候,这个方法都会返回true
,从而使得我们不需要先单独判断这两种空值再使用逻辑运算符连接起来了。
从文件中读取数据
第一步.openFileInput获得FileInputStream对象;
第二步.借助上一步结果构建InputStreamReader对象,在构建BufferedReader对象,再一行行构读取文本中的所有内容,存放至Stringbuilder对象中去。读取并返回,关闭Reader流。
第三步.在onCreate方法中调用load()方法,获取字符串。!TextUtils.isEmpty(inputText)判断字符串不为null或空时,setText设置字符串。使用editText.setSelection(inputText.length());将光标移至末尾。
此方法不适合保存一些复杂的文本数据,故而....我们来看下一种存储方式。
6.3 SharedPreferences存储
SharedPreferences是使用键值对的方式来存储数据的。也就是说,当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。
要想使用SharedPreferences来存储数据,首先需要获取到SharedPreferences
对象。Android中主要提供了3种方法用于得到SharedPreferences
对象。
-
Context
类中的getSharedPreferences()
方法- 此方法接收两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data/
/shared_prefs/目录下的。 - 第二个参数用于指定操作模式,目前只有MODE_PRIVATE这一种模式可选,它是默认的操作模式,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。
- 此方法接收两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data/
-
Activity
类中的getPreferences()
方法- 这个方法和Context中的
getSharedPreferences()
方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名。
- 这个方法和Context中的
-
PreferenceManager
类中的getDefaultSharedPreferences()
方法-
这是一个静态方法,它接收一个
Context
参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件。 -
得到了
SharedPreferences
对象之后,就可以开始向SharedPreferences文件中存储数据了-
(1) 调用
SharedPreferences
对象的edit()
方法来获取一个SharedPreferences.Editor
对象。(2) 向
SharedPreferences.Editor
对象中添加数据,比如添加一个布尔型数据就使用putBoolean()
方法,添加一个字符串则使用putString()
方法,以此类推。(3) 调用
apply()
方法将添加的数据提交,从而完成数据存储操作。 -
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 v) { SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit(); editor.putString("name", "Tom"); editor.putInt("age", 28); editor.putBoolean("married", false); editor.apply(); } }); } }
-
-
-
6.3.2 从SharedPreferences中读取数据
-
应该已经感觉到了,使用SharedPreferences来存储数据是非常简单的,不过下面还有更好的消息,其实从SharedPreferences文件中读取数据会更加地简单。
SharedPreferences
对象中提供了一系列的get
方法,用于对存储的数据进行读取,每种get
方法都对应了SharedPreferences.Editor
中的一种put
方法,比如读取一个布尔型数据就使用getBoolean()
方法,读取一个字符串就使用getString()
方法。这些get
方法都接收两个参数,第一个参数是键,传入存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,即表示当传入的键找不到对应的值时会以什么样的默认值进行返回。' -
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... Button restoreData = (Button) findViewById(R.id.restore_data); restoreData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { 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); } }); } }
-
**总结
-
SharedPreferences存储
使用键值对存储数据,支持多种不同类型数据存储。
2.1将数据存储至SharedPreferences中
第一步.使用三种方法获得SharedPreferences对象,第一种是getSharedPreferences:参数是文件名和指定的操作模式;第二种是getPreferences获得对象,只有一个参数,文件名类名;第三种是getDefaultPreferences,程序包名存储。
第二步.SharedPreferences的edit方法获取editor对象。
第三步.在Editor对象中添加数据,比如添加布尔类型putBoolean,添加字符串putString
第四步.将添加的数据进行提交。 -
从SharedPreferences中拿数据
第一步.getSharedPreferences获得sharedPreferences对象;
第二步.getString、getInt获得相应数值,若没有,则用后面的默认值替代。
第三步.logd输出
实战
- 实现记住密码功能
复用P185代码。需要注意的有以下几点:
1.加入复选框,如果选定就记住密码,若没有选中就不记住密码。
2.点击之后会将几个值存进sharedPreferences中,用boolean类型更改为true。下一次在执行OnCreate的时候进boolean,用户名,密码setText和setChecked进去。刚开始默认boolean是false。
6.4 SQLite数据库存储
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。
轻量级关系型数据库,速度快,占用资源少,遵循数据库ACID事务。当存储度咋的关系型数据库时,以上两种存储方式就会有比较大的问题。
6.4.1 创建数据库
SQliteHelper 抽象类 帮助类来对数据库进行创建和升级。
其中有两个抽象方法。分别是onCreate和onUpgrade,在这两个方法中创建和升级数据库的逻辑。使用getReadableDatabase()和getWritableDatabase()来创建或打开一个数据库。主分为以下几步:
(1).创建MyDatabaseHelper继承自SQLiteHelper,创建数据库的语句,将建表语句定义成字符串常量。
建表:
public static final String CREATE_BOOK = "create table Book("
+"id integer primary key autoincrement,"
+"author text,"
+"price real,"
+"pages integer,"+"name text)";
(2).在onCreate调用SQLiteDatabase的execSQL执行这条建表语句,弹出Toast提示成功。
(3).创建相应的布局。
(4).MyDatabaseHelper的getWritableDatabase方法创建并打开数据库。 Adb shell如何配置于系统中,进入相应的Database目录下,ls命令看里面内容(必须要root权限),sqlite3 BookStore.db打开数据库;.table查看哪些表;.schema查看建表语句。
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 v) {
dbHelper.getWritableDatabase();
}
});
}
}
-
升级数据库
(1).在Book表基础之上再添加Category表;
(2).在onUpgrade中增加两条Drop语句,若存在则删除该表,并转而执行onCreate方法;
(3)在onCreate方法中执行创表语句;
(4)dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);将版本号改为比1大的。 -
添加数据
数据操作无非就是4种。 -
C(Create添加->insert)
-
R(Retrieve查询->select)
-
U(Update更新->update)
-
D(Delete删除->delete),调用SQLIteOpenHelper的getWritableDatabase可以用于创建和升级数据库,而且其会返回一个SQLiteDatabase对象供我们去进行GUID操作。示例如下:
-
插入数据:
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
//1.开始组装并插入第一条数据;
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();
- 更新数据库
首先构建一个ContentValues对象,指定更改后的数据,调用SQLiteDatabase的update对象,第一个参数和insert()
方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是ContentValues
对象,要把更新数据在这里组装进去。第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",10.99);
db.update("Book",values,"name=?",new String[]{"The Da Vinci Code"});
这里在更新数据按钮的点击事件里面构建了一个ContentValues
对象,并且只给它指定了一组数据,说明我们只是想把价格这一列的数据更新成10.99。然后调用了SQLiteDatabase
的update()
方法去执行具体的更新操作,可以看到,这里使用了第三、第四个参数来指定具体更新哪几行。第三个参数对应的是SQL语句的where
部分,表示更新所有name
等于?
的行,而?
是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。因此上述代码想表达的意图是将名字是The Da Vinci Code的这本书的价格改成10.99。
- 删除数据库
SQLiteDatabase
中提供了一个delete()
方法,专门用于删除数据,这个方法接收3个参数,第一个参数仍然是表名,这个已经没什么好说的了,第二、第三个参数又是用于约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book","pages>?",new String[]{"500"});
//通过第二三参数来删除页数超过500的书。
-
查询数据
SQLiteDatabase中还提供了一个
query()
方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入7个参数。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
指定查询结果的排序方式 较为复杂,总共七个参数;调用query之后会返回一个Cursor对象。查询到的数据从中取出。
(1)db.query("Book",null...)查询Book表中的所有数据,并将数据以Cursor对象进行返回。
(2)将数组指针通过moveToFirst移到第一行的位置。
(3)遍历Cursor对象,取出数据并打印,通过getColumnIndex获取某一列在表中对应的位置索引,然后将这索引传入到相应的取值方法中,这样便可以得到从数据库中读取到的数据了。
(4)Log输出。moveToNext获取下一条数据。
(5)调用Close方法来关闭Cursor流。
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 queryButton = (Button) findViewById(R.id.query_data);
queryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
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();
}
});
}
}
这里的query()
方法非常简单,只是使用了第一个参数指明去查询Book表,后面的参数全部为null
。这就表示希望查询这张表中的所有数据,虽然这张表中目前只剩下一条数据了。查询完之后就得到了一个Cursor
对象,接着我们调用它的moveToFirst()
方法将数据的指针移动到第一行的位置,然后进入了一个循环当中,去遍历查询到的每一行数据。在这个循环中可以通过Cursor
的getColumnIndex()
方法获取到v ,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。接着我们使用Log的方式将取出的数据打印出来,借此来检查一下读取工作有没有成功完成。最后别忘了调用close()
方法来关闭Cursor
。
6.4.7 使用SQL操作数据库
-
添加数据的方法如下:
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" }); db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" }); 复制代码
-
更新数据的方法如下:
db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" }); 复制代码
-
删除数据的方法如下:
db.execSQL("delete from Book where pages > ?", new String[] { "500" }); 复制代码
-
查询数据的方法如下:
db.rawQuery("select * from Book", null);
可以看到,除了查询数据的时候调用的是SQLiteDatabase的rawQuery()
方法,其他的操作都是调用的execSQL()
方法。
6.5 使用LitePal操作数据库
本书中我们将会学习多个开源库的使用方法,而现在你将正式开始接触第一个开源 库——LitePal。
LitePal是一款开源数据库框架(稳定),采用对象关系映射(ORM)的模式,对数据库功能进行封装,较为简单。
6.1 配置LitePal
(1)Build.gradle中的dependencies中添加compile 'org.litepal.android:core:1.3.2'
(2)在main目录下建立assets目录,在assets目录下建立litepal.xml,编辑里面内容。指定相应的数据库名、数据库版本名、以及list所指的所有映射模型。
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="BookStore" ></dbname>
<version value="1" ></version>
<list>
</list>
</litepal>
<dbname>
标签用于指定数据库名<version>
标签用于指定数据库版本号,<list>
标签用于指定所有的映射模型
(3)在AndroidManifest.xml中增加Appliacation配置
这里我们将项目的application
配置为org.litepal.LitePalApplication
,这样才能让LitePal的所有功能都可以正常工作。
6.2 创建和升级数据库
(1)建立Book类,定义id、author、price、pages、name这几个字段及相应的get和set方法,每个字段对应表中的每一列。
(2)将将Book
类添加到映射模型列表当中
<litepal>
<dbname value="BookStore" ></dbname>
<version value="1" ></version>
<list>
<mapping class="com.example.litepaltest.Book"></mapping>
</list>
</litepal>
(3)在MainActivity中进行Litepal.getDatabase();完成一次最简单的数据库操作,这时数据库就会自动创建完成了。
(4)升级数据库的话,若是在一张表里面添加列,则只需要在Book中添加相应的变量名和相应的set和get方法;若是需要添加表,则新建Category类并在list映射模型中添加新的模型类。LitePal还自动帮我们做了一项非常重要的工作,就是保留之前表中的所有数据,这样就再也不用担心数据丢失的问题了。
6.3使用LitePal添加数据
(1)先将模型类继承自DataSupport类,这样才能进行GUID操作;
public class Book e xtends DataSupport
(2)创建出Book的实例,然后各种set方法,最后调用book.save()方法完成数据添加操作。
6.4使用LitePal更新数据库
(1)对已存储的对象进行操作;直接setPrice()对价格进行修改,然后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();
save()
方法是从哪儿来的呢?当然是从DataSupport
类中继承而来的了。
(2)更为灵活的更新书名、印刷商。更新所有该条件判断语句下的对象。
Book book = new Book();
book.setPrice(14.95);
book.setAuthor("Anchor");
book.updateAll("name=? and author=?","The Lost Symbol","Dan Brown");
首先new出了一个Book的实例,然后直接调用setPrice()
和setPress()
方法来设置要更新的数据,最后再调用updateAll()
方法去执行更新操作。注意updateAll()
方法中可以指定一个条件约束,和SQLiteDatabase中update()
方法的where
参数部分有点类似,但更加简洁,如果不指定条件语句的话,就表示更新所有数据。这里我们指定将所有书名是The Lost Symbol并且作者是Dan Brown的书价格更新为14.95,出版社更新为Anchor。
(3)将Pages字段更新为默认值。
Book book = new Book();
book.setToDefault("pages");
book.updateAll();
4.5使用Litepal删除数据
DataSupport.deleteAll删除数据,第一个参数用于指定那个表,第二个和第三个是约束条件。
DataSupport.deleteAll(Book.class,"price < ?","15");
4.6利用LitePal查询数据
使用List来获取Book类型的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());
}
}
});
4.7其他操作语句
除了findAll()
方法之外,LitePal还提供了很多其他非常有用的查询API。比如我们想要查询Book表中的第一条数据就可以这样写:
Book firstBook = DataSupport.findFirst(Book.class);
查询Book表中的最后一条数据就可以这样写:
Book lastBook = DataSupport.findLast(Book.class);
我们还可以通过连缀查询来定制更多的查询功能。
-
select()
方法用于指定查询哪几列的数据,对应了SQL当中的select
关键字。比如只查name
和author
这两列的数据,就可以这样写:List<Book> books = DataSupport.select("name", "author").find(Book.class);
-
where()
方法用于指定查询的约束条件,对应了SQL当中的where
关键字。比如只查页数大于400的数据,就可以这样写:List<Book> books = DataSupport.where("pages > ?", "400").find(Book.class);
-
order()
方法用于指定结果的排序方式,对应了SQL当中的order by
关键字。比如将查询结果按照书价从高到低排序,就可以这样写:List<Book> books = DataSupport.order("price desc").find(Book.class);
其中
desc
表示降序排列,asc
或者不写表示升序排列。 -
limit()
方法用于指定查询结果的数量,比如只查表中的前3条数据,就可以这样写:List<Book> books = DataSupport.limit(3).find(Book.class);
-
offset()
方法用于指定查询结果的偏移量,比如查询表中的第2条、第3条、第4条数据,就可以这样写:List<Book> books = DataSupport.limit(3).offset(1).find(Book.class);
由于limit(3)
查询到的是前3条数据,这里我们再加上offset(1)
进行一个位置的偏移,就能实现查询第2条、第3条、第4条数据的功能了。limit()
和offset()
方法共同对应了SQL当中的limit
关键字。
当然,你还可以对这5个方法进行任意的连缀组合,来完成一个比较复杂的查询操作:
List<Book> books = DataSupport.select("name", "author", "pages")
.where("pages > ?", "400")
.order("pages")
.limit(10)
.offset(10)
.find(Book.class);
这段代码就表示,查询Book表中第11~20条满足页数大于400这个条件的name
、author
和pages
这3列数据,并将查询结果按照页数升序排列。
怎么样?是不是感觉LitePal的查询功能非常强大,并且代码明显更加简洁?我们需要用到一个方法的时候直接连缀一下就可以了,不需要的话就可以不写,而不是像之前的query()
方法,不管需不需要用到,都必须要传固定的参数进去才行。
关于LitePal的查询API差不多就介绍到这里,这些API已经足够我们应对绝大多数场景的查询需求了。当前,如果你实在有一些特殊需求,上述的API都满足不了你的时候,LitePal仍然支持使用原生的SQL来进行查询:
Cursor c = DataSupport.findBySQL("select * from Book where pages > ? and price < ?", "400", "20");
调用DataSupport.findBySQL()
方法来进行原生查询,其中第一个参数用于指定SQL语句,后面的参数用于指定占位符的值。注意findBySQL()
方法返回的是一个Cursor
对象,接下来你还需要通过之前所学的老方式将数据一一取出才行。