在使用应用程序的时候,存储数据是必不可少的,比如记录一些用户的行为习惯设置等,保存用户的用户名和密码等等,都需要持久性保存在设备上,而之前提到的聊天输入等等,都是临时性的,关闭程序后就没有了。持久性技术使得数据即使在设备关机的情况瞎依然可以保存数据不会丢失。主要提供了三种方法
- 文件存储
- SharedPreferences
- 数据库
文件存储
比较适合存储一些简单的文本数据,比如用户自定义的一些基本设置等,如果存储一些复杂结构的数据,自己定义一套规则和格式就好了,这个比较容易的。
Context提供了一个openFileOutput()
方法用于将数据写入指定文件中,有两个参数,第一个参数是文件名,不可包含路径名,默认路径是/data/data//files/
目录下,第二个是文件的操作模式,两种模式MODE_PRIVATE和mode_append
,前者是默认操作,如果存在同名文件则会覆盖,后者是若存在同名文件则会追加内容,不存在则创建新文件。方法返回一个FileOutputStream
对象,然后就可以用流进行读写了。
一个小栗子,主界面layout就放一个按钮,点击按钮将我自定义的字符串保存到文件中。
编写writeData()方法
void writeData(Context context){
String Data="ss";
FileOutputStream fos=null;
BufferedWriter bw=null;
try {
fos=context.openFileOutput("mydata",MODE_PRIVATE);
bw=new BufferedWriter(new OutputStreamWriter(fos));
bw.write(Data);
bw.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
然后给Button添加一个监听
DoItButton=findViewById(R.id.DoIt);
DoItButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
writeData(MainActivity.this);
Toast.makeText(MainActivity.this,"weitr successfully",Toast.LENGTH_SHORT).show();
}
});
现在就写入成功了,怎么在电脑查看文件呢,点击顶部菜单的Tools -> Android -> Android Device Monitor,点击File Explorer,讲道理应该是能在data文件夹里找到的,但是我找不到,不过通过下面的从文件中读取内容的结果看,文件是成功创建了,因为可以成功读取出内容。
可以通过abd进入shell然后cd到data的文件夹里,就能找到了
编写readData()方法,把Out方法都换成In就行了,这里是利用StringBuilder,从文件中逐行读取,依次添加到Builder里,最后转成String。
void readData(){
FileInputStream fis=null;
BufferedReader br=null;
StringBuilder sb=new StringBuilder();
try {
fis=openFileInput("data");
br=new BufferedReader(new InputStreamReader(fis));
String line="";
while ((line=br.readLine())!=null){
sb.append(line);
}
if (br!=null){
br.close();
Toast.makeText(MainActivity.this,sb.toString(),Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(MainActivity.this,"0000000000000000000",Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
这里就不放图了,我刚才输入到文件的内容是sssss,把readData()方法添加到onCreate()末尾,启动程序就可以加载了,然后可以看到输出是ssss而不是0000000,说明成功读取到数据了,也说明刚才写入成功了。
SharedPreferences
是利用键值对的方式存储数据的。保存一条数据的时候提供对应的键即可。支持多种数据类型,存入是int取出还是int,存入是String取出就是String。
首先需要获取SharedPreferences对象,提供了三种方法
- Context对象中的getSharedPreferences()方法。两个参数,第一个指定文件名称,不存在的话会创建一个,文件存放在
/data/data//shared_prefs/
目录下,第二个参数指定操作模式,只有一种MODE_PRIVATE
可选,是默认操作,其余模式都被废弃了。 - Activity类重的getPreferences()方法,类似上一个,不过只有一个操作模式参数,会自动将当前活动类名作为SharedPreferences的文件名。
- Preferencemanager类重的getDefaultSharedPreferences()方法,是一个静态方法,接收一个Context参数,自动使用当前应用包名作为前缀命名文件。
得到对象以后就可以进行数据的读写了,还是有三步实现: - 调用SharedPreferences对象的exit()方法获取一个SharedPreferences.Editor对象
- 向对象中添加数据,如putBoolean()方法用于添加一个布尔型数据,添加字符串则使用putString()方法
- 调用apply()方法提交数据,完成数据存储
来个例子。
DoItButton.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",10);
editor.apply();
}
});
就在按钮监听里面多了一点点东西。生成文件格式是xml好像,文件名是data.xml,同样,这个文件我还是找不到在哪。
我找不到文件,就随便放一个吧,大概内容就是这样
<?xml version='1,0' encoding='tuf-8' standalone='yes' ?>
<map>
<string name="Name">Tom</string>
<int name="Age">10</int>
</map>
然后就是读取数据了。还是那个Button,方法从put改成get。先获取SharedPreferences对象,调用get方法。
DoItButton.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",10);
editor.apply();
SharedPreferences pref=getSharedPreferences("data",MODE_PRIVATE);
String Name=pref.getString("Name","没有");
int Age = pref.getInt("Age",-2);
Toast.makeText(MainActivity.this,Name+"\t"+Age,Toast.LENGTH_SHORT).show();
}
});
现在就可以实现记住密码的功能了。
在输入密码的界面放置一个复选框控件 ,需要记住密码就勾选,然后点击登录的时候查看复选框的状态,如果选了,就获取一个SharedPreferences对象,使用PreferenceManager.getDefaultSharedPreferences()
获取,在启动活动的时候检查是否记住,这里通过检查SharedPreferences里面是True还是False,如果是True记住密码就调用get方法获取数据填充进来;在登录的时候如果记住了就调用put方法保存用户名和密码,同时将保存的False或者True保存起来。
上面提到的两个都是比较简单的方法,同时数据是明文保存的,不安全,需要借助一些加密措施才行。
SQLite
Android内置是由数据库的,SQLite是一款轻量级的关系型数据库,速度很快,占用资源很少,内存只有几百KB,支持标准SQL语法,遵循数据库ACID事务,比一般数据库简单,甚至不需要用户名密码就可以登录。
Android提供了一个SQLiteOpenHelper帮助类,很方便对数据库进行操作。
这个类是一个抽象类,需要使用一个类然后继承它,主要有两个抽象方法,一个是onCreate()和onUpgrade()
。
有两个实例方法getReadableDatabase()和getWritableDatabase()
。都可以创建或者打开一个现有数据库,不存在则新建。当不可写入的是偶(硬盘满了),调用getWritableDatabase
方法的时候会报错。
有两个构造方法可以重写,一般用参数少的那个,有四个参数,第一个时Context;第二个时数据库名称;第三个允许查询数据库的时候返回一个自定义Cursor,一般为null;第四个是数据库版本参号,用于对数据库进行升级等。
构建出SQLiteOpenHelper实例后,调用上面两个实例方法创建或者打开数据库,数据库文件会存放在/data/data//databases/
目录下,重写的onCreate()方法也是在这里实现的,可以进行一些建表的操作等等。
然后是栗子,创建一个BookStore.db数据库,有一个表Book,有字段id(主键)、作者、价格等。
建表的语句就是SQL语句
CREATE TABLE Book(
id INTEGER PRIMARY KEY AUTOINCREMENT,
author TEXT,
price REAL)
SQLite的数据类型有:
- integer:整形
- real:浮点型
- text:文本类型
- bolb:二进制类型
其中PRIMARY KEY表示主键,AUTOINCREMENT表示自动自增长。
编写类继承SQLiteOpenHelper
class MySQLiteOpenHelper extends SQLiteOpenHelper{
public static final String CreateTableBookSQL="CREATE TABLE Book(id INTEGER PRIMARY KEY AUTOINCREMENT,author TEXT,price REAL)";
private Context MyContext=null;
public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
MyContext=context;
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CreateTableBookSQL);
Toast.makeText(MyContext,"Create successfully",Toast.LENGTH_SHORT).show();
}
}
然后修改一下按钮监听
final MySQLiteOpenHelper dbHelper=new MySQLiteOpenHelper(this,"BookStore.sb",null,1);
DoItButton=findViewById(R.id.DoIt);
DoItButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});
然后运行第一次点击按钮会创建数据库弹出Toast,再次点击就没用了。
怎么验证数据库是否存在呢,使用adb可以查看。
进入SDK目录,在platform-tools
文件夹下找到adb.exe.,控制台打开。
执行adb shell
,可以看到进入到终端了,如果提示符时¥,说明是普通用户,输入su切换到超级用户。
ohhhhhhhhhhhhhhhhh,可以在终端查看数据文件夹,我终于看到data文件夹在哪了!!!
用过cd和ls就可以进入到这个目录下,看到有数据库文件。
可以直接使用sqlite打开数据库。执行sqlite3 BookStore.db
就可以进入数据库。
突然发现我这里写错了。。我把db写成sb了。。算了不改了,那就执行sqlite3 BookStore.sb
。。。。。。。
然后的一些命令,比如查看表,查看建表语句的命令,看下面的图。
升级数据库
onUpgrade()方法干什么用呢。用于对数据库进行升级,好像用处不小。
现在给sb数据库添加一个表Category记录图书分类,其中有字段id(主键),分类名等等,建表语句如下
CREATE TABLE Category(
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_name TEX,
category_code INTEGER)
这时候如果把Category建表语句在onCreate()方法里执行,会看到并没有创建成功,因为onCreate()方法只有在数据库不存在的时候才会调用,而此可数据库已经有了,就不会调用这个方法了。如果一定想在onCreate()中创建,删除程序或者删除数据,数据库文件就会删除了,然后就可以创建成功了。
同时修改onCreate()和onUpgrade(),更新的时候把存在的表都删完,然后重新建表。
public static final String UpgradeTableBookSQL="CREATE TABLE Category(id INTEGER PRIMARY KEY AUTOINCREMENT,category_name TEX,category_code INTEGER)";
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CreateTableBookSQL);
db.execSQL(CreateTableCategorySQL);
Toast.makeText(MyContext,"Create successfully",Toast.LENGTH_SHORT).show();
}
那么怎么能让onUpgrade()方法执行呢,只需要把构造方法的第四个参数设置为比1大的数就会执行了。只需要修改onCreate()中这一行就行。
final MySQLiteOpenHelper dbHelper=new MySQLiteOpenHelper(this,"BookStore.sb",null,2);
运行程序以后可以在adb shell里面看到表已经有了。
CRUD
数据库少不了增删改查。
调用getReadableDatabase()和getWritableDatabase()
方法可以创建和升级数据库,同时会返回一个SQLiteDatabase对象,通过这个对象可以调用CRUD操作。
insert
有三个参数,第一个参数是表名,第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值null,写null就行,第三个是ContentValues对象,提供了一系列的put()重载,用于向ContentValues中添加数据,只需要将表中的每个列名和对应的带添加数据传入即可。
修改一下按钮监听,ContentValues对象是通过键值存储的数据。
DoItButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db= dbHelper.getWritableDatabase();
ContentValues cv=new ContentValues();
cv.put("author","balabala");
cv.put("price","balabala_price");
db.insert("Book",null,cv);
cv.clear();
cv.put("category_name","category_name_balabala");
cv.put("category_code","code_balabbala");
db.insert("Category",null,cv);
}
});
运行程序,在adb shell里面查询一下数据,可以看到数据有了。
update
更新数据可以用update()方法,有四个参数,第一个是表名,第二个是ContentValues,把更新的数据放进来,第三个个第四个参数是用于约束更新某一行或者某几行的数据,不指定就默认更新所有行。
还是修改按钮监听,第三个和第四个就类似SQL语句中的where后面的约束判断,这里要注意,好像即便字段是数字,后面还是要用字符串数组保存数字。
DoItButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db= dbHelper.getWritableDatabase();
ContentValues cv=new ContentValues();
cv.put("price","low price");
db.update("Book",cv,"author=?",new String[]{"balabala"});
}
});
可以看到价格已经改了。
delete
删除操作使用delete()方法,有三个参数,第一个参数是表名,第二个和第三个是列约束条件。
栗子
DoItButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db= dbHelper.getWritableDatabase();
db.delete("Book","author=?",new String[]{"balabala"});
}
});
可以看到删除成功了。
query
提供query()方法查询数据,包括七个参数,很复杂
- 表名
- 查询哪几列,不指定的话默认所有列
- 第三个第四个参数用来约束某一行或者某几行数据,不指定则默认查询所有行
- 指定需要group by的列,不指定则不对查询结果group by操作
- 对group by后的数据进一步过滤,不指定则不过滤
- 指定查询结果的排序方式,不指定则默认排序方式
还是修改按钮监听,调用moveToFirst()将指针移动到第一个数据位置,然后开始循环,调用getColumnIndex()方法获取到某一列字段的位置索引,然后利用索引从cursor中获取数据,在获取完毕后要调用clode()关闭Cursor。
DoItButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db= dbHelper.getWritableDatabase();
Cursor cursor=db.query("Category",null,null,null,null,null,null,null);
if (cursor.moveToFirst()){
do {
String Name=cursor.getString(cursor.getColumnIndex("category_name"));
String Code=cursor.getString(cursor.getColumnIndex("category_code"));
Toast.makeText(MainActivity.this,Name+"\t"+Code,Toast.LENGTH_SHORT).show();
}while (cursor.moveToNext());
}
cursor.close();
}
});
可以看到Toast输出了数据库中的信息,查询成功。
SQL操作
当然实时提供了直接利用SQL语句进行数据库的操作。
添加数据
db.execSQL("insert into Book (name,author,pages,price) values(?,?,?,?)",new String[] {"balabala","sss","11","22"});
更新数据
db.execSQL("update Book set price = ? where name=?",new String[] {"111","sss"});
删除数据
db.execSQL("delete from Book where pages >?",new String[] {"220"});
查询
db.rawQuery("select * from Book",null);
LitePal
据说学了这个再也不想碰SQLiteDatabase了。
这是一个开源库-LitePal,采用对象关系映射(ORM)模式,对常用的数据库功能进行封装,不需要编写SQL语句就可以完成建表和增删改查操作。
配置LitePal
修改以下gradle文件,添加依赖compile ‘org.litepal.android:core:1.4.1’
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
compile 'com.android.support:design:26.1.0'
androidTestCompile('com.android.support:support-annotations:26.1.0') {
force = true
}
compile 'org.litepal.android:core:1.4.1'
}
还要配置一下litepal.xml文件,目录是app/src/main/assets/litepal.xml
。
其中表示数据库名,为数据库版本号,标签指定所有的映射模型。
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="BookStore"></dbname>
<version value="1"></version>
<list>
</list>
</litepal>
最后要配置一下LitePalApplication,修改AndroidManifest.xml中的代码
<application>
...
android:name="org.litepal.LitePalApplication"
...
</applicaition>
然后就可以使用了。
LitePal使用的是对象关系映射模式,我们使用的编程语言是面向对象语言,而使用的数据库则是关系型数据库,那么面向对象的语言和面向关系的数据库之间的映射关系就是对象关系映射。
可以用面向对象的思维操作数据库,不需要和SQL打交道。
定义一个类Book类,设置getter和setter,可以用快捷键alt+insert快速添加。
class Book{
private int id;
private String author;
private String price;
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 String getPrice(){
return price;
}
public void setPrice(String price){
this.price=price;
}
}
将Book类添加到模型中,修改litepal.xml,如果要映射多个,都添加到list里面
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="BookStore"></dbname>
<version value="1"></version>
<list>
<mapping class="com.example.k.androidpractie.Book"></mapping>
</list>
</litepal>
这样准备工作就做完了,随便进行一个操作都会开始建立数据库。
然后修改MainActivity.java,在onCreate()中添加LitePal.getDatabase()
,就会自动创建数据库了。
可以看到成功建立了数据库,还可以看看建表语句是什么样。
在上面使用SQLOpenHelper类升级数据库的时候需要drop原有的表,重新建库,会导致数据丢失,而LitePal不会出现这个问题。
使用LitePal升级数据库非常非常简单,完全不用向任何逻辑,只需要改想改的内容,在版本号加一就行了。
比如,我给Book加了一个name字段,写一下getter和setter,用alt+insert快速插入
class Book{
...
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
...
}
然后我需要再来一个表Category,新建一个类
class Category{
private int id;
private String categoryname;
private String categorycode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCategoryname() {
return categoryname;
}
public void setCategoryname(String categoryname) {
this.categoryname = categoryname;
}
public String getCategorycode() {
return categorycode;
}
public void setCategorycode(String categorycode) {
this.categorycode = categorycode;
}
}
然后修改一下litepal.xml,添加一个类,把version改为2
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="BookStore"></dbname>
<version value="2"></version>
<list>
<mapping class="com.example.k.androidpractice.Book"></mapping>
<mapping class="com.example.k.androidpractice.Category"></mapping>
</list>
</litepal>
然后运行程序,可以看到有两个表了,说明升级成功了。
添加数据
原先需要先创建一个ContentValues然后put数据然后传入参数在调用insert()。LitePal只需要创建模型类的实例,设置好数据,调用save()方法即可。
这里需要继承一个类DataSupport,修改一下按钮监听,然后等着被惊讶吧,真的简单过火了。
TestButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//LitePal.getDatabase();
Book book=new Book();
book.setAuthor("作者");
book.setName("书名");
book.setPrice("价格");
book.save();
}
});
...
class Book extends DataSupport{
...
}
运行程序,点击按钮后可以看到数据已经插入成功了。
!]image-12](https://my-web-image.oss-cn-beijing.aliyuncs.com/20-2-28/12.png)
更新数据
更新要麻烦一点,API接口有很多,最佳那等你的方法就是对存储的对象重新设置数值,调用save()方法。
已存储的对象,对象是否从存储通过调用model.isSaved()
方法判断,已存储返回true。只有两种情况会返回true,一种是已经调用了model…save()添加过数据了,另一种是通过查询API查出来的。
目前只能通过第一种方法判断,修改一下按钮监听的代码,直接重新set一下就可以了。
TestButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//LitePal.getDatabase();
Book book=new Book();
book.setAuthor("作者");
book.setName("书名");
book.setPrice("价格");
book.save();
book.setPrice("新的价格");
book.save();
}
});
报错
但是我这里调用updateAll()方法一直报错,原因就是没有得到访问许可,然后解决方法就是,把Book类写成public就行了。
...
Caused by: java.lang.IllegalAccessException: java.lang.Class<com.example.k.androidpractice.Book> is not accessible from java.lang.Class<org.litepal.crud.DataHandler>
...
因为刚才程序出点问题,我把数据换了一遍,Book和Category如下所示,类最好是public的。
public class Book extends DataSupport {
private int id;
private String author;
private double price;
private String name;
private int pages;
private String press;
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 getPress() {
return press;
}
public void setPress(String press) {
this.press = press;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = 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;
}
}
class Category{
private int id;
private String categoryname;
private String categorycode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCategoryname() {
return categoryname;
}
public void setCategoryname(String categoryname) {
this.categoryname = categoryname;
}
public String getCategorycode() {
return categorycode;
}
public void setCategorycode(String categorycode) {
this.categorycode = categorycode;
}
}
再来一遍,代码如下,updateAll()参数就相当于SQL语句中的where,约束判断的,如果不带参数,就把所有数据的特定字段全部更新。
TestButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book=new Book();
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");
}
});
这是把符合书名和作者的书的价格和出版社都设为14.95和Anchor
当想把数据替换为默认值的时候,就不能这么用了。Java中任意一个数据类型都有默认值,int默认0,boolean默认false,String默认null,new一个Book对象的时候,所有字段都初始化了,如Pages字段是0,想更新为0的时候,setPages(0)没用,因为本身就是0,不会更新。
想设置为默认值的时候调用setToDefault()方法,传入相应字段名即可。
修改监听
TestButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book=new Book();
book.setToDefault("pages");
book.updateAll();
}
});
前后对比一下,数据发生了变化
删除数据
有两种方法,一种是调用已存储对象(调用过save()方法或者通过查询API查询出来的对象)的delete()方法,另一种是调用DataSupport.deleteAll()方法,有三个参数,第一个指定哪个表中的数据,如Book.class,第二个和第三个参数是进行约束的。如果不指定后面两个参数,就会删除所有数据/
栗子
TestButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DataSupport.deleteAll(Book.class,"price < ?","15");
}
});
可以看到只剩一条信息了。
查询数据
之前的query()方法使用很不方便,参数很多,很多即便不使用还得写null,LitePal在查询API优化了很多,比如查询所有数据
List<Book> Books=DataSupport.findAll(Book.class);
由于返回是一个集合,因此也不需要使用Cursor一个一个遍历,但是也可以通过for循环进行各种操作。
for (Book item : Books){
...
}
查询第一条数据
Book book=DataSupport.findFirst(Book.class);
最后一条数据
Book book=DataSupport.findLast(Book.class);
可以通过select()方法查询特定字段数据
List<Book> books=DataSupport.select("name","author").find(Book.class);
可以通过where()方法查询符合特定约束的数据
List<Book> books=DataSupport.where("pages > ?","400").find(Book.class);
使用order()方法进行排序,其中desc表示降序,asc是升序
List<Book> Books=DataSupport.order("price desc").find(Book.class);
使用linit()方法用于指定查询结果的数量,比如只查询前三条
List<Book> Books=DatSupport.linit(3).find(Book.class);
使用offset()方法指定查询结果的偏移量,如查询第二条第三条第四条数据,执行如下代码即可,即从第一条数据开始查询三条
List<Book> Books=DataSupport.limit(3).offset(1).find(Book.class);
同样也提供原始SQL查询方法
Cursor cursor=DataSupport.findBySQL("select * from Book where pages > ? and price < ?","400","20");