文章目录
持久化技术
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失。保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化技术则提供了一种机制可以让数据在瞬时状态和持久状态之间进行转换。
Android 系统中主要提供了3种方式用于简单地实现数据持久化功能,即文件存储、SharedPreference 存储以及数据库存储。当然,除了这3种方式之外,你还可以将数据保存在手机的SD卡中,不过使用文件、SharedPreference 或数据库来保存数据会相对更简单一些,而且比起将数据保存在SD卡中会更加地安全。
文件存储
文件存储是Android中最基本的一种数据存储方式, 它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的文本数据, 就需要定义一套自己的格式规范,这样可以方便之后将数据从文件中重新解析出来。
将数据存储到文件中
Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/<packagename>/files/
目录下的。第二个参数是文件的操作模式,主要有两种模式可选,MODE_ PRIVATE和MODE APPEND。其中MODE PRIVATE是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,而MODE APPEND则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件。
openFileOutput ()
方法返回的是一个FileOutputStream对象,得到了这个对象之后就可以使用Java 流的方式将数据写人到文件中了。以下是一段简单的代码示例,展示了如何将一段文本内容保存到文件中:
public void save() throws IOException {
String data = "Data to save";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MOOE_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();
}
}
}
这里通过openFileOutput()
方法能够得到一个FileOutputStream 对象,然后再借助它构建出一个OutputStreamWriter对象,接着再使用OutputStreamWriter构建出一个BufferedWriter对象,这样你就可以通过BufferedWriter来将文本内容写人到文件中了。
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit"
android:hint="Type Something..."/>
</LinearLayout>
此时在文本输人框中随意输人点什么内容,再按下Back键,这时输人的内容肯定就已经丢失了,因为它只是瞬时数据,在活动被销毁后就会被回收。而这里我们要做的,就是在数据被回收之前,将它存储到文件当中。修改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中
save(inputText);
}
//将内容存储到文件中
private void save(String inputText) {
FileOutputStream out = null;
BufferedWriter writer = null;
try {
//如果name文件不存在的话创建一个新的 并关联流对象
//Context.MODE_PRIVATE是应用程序私有的
//MODE_WORLD_READABLE则所有应用程序都可以访问的
//MODE_WORLD_WRITEABLE所以应用程序都可以写
//mode_APPEND则是如果要创建的文件存在则新写入的数据不会覆盖以前的数据。
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();
}
}
}
}
通过openFileOutput()方法保存数据
OutputStream os=Activity.this.openFileOutput("txtme.txt",MODE_PRIVATE);
—>在data/包名/files/目录下会创建txtme.txt文件如果该文件不存在的话,MODE_PRIVATE的文件是应用程序私有的 ,MODE_WORLD_READABLE则所有应用程序都可以访问的,MODE_WORLD_WRITEABLE所以应用程序都可以写,MODE_APPEND则是如果要创建的文件存在则新写入的数据不会覆盖以前的数据。
启动程序 在输入框中输入消息 点击back键返回
若要查看文件保存的地方,在打开虚拟机时点击右下角竖着的Device File Explorer,找到/data/data/包名/Files,我们写的文件就储存在这里
从文件中读取数据
类似于将数据存储到文件中,Context 类中还提供了一个openFileInput()
方法,用于从文件中读取数据。这个方法要比openFile0utput()
简单一些,它只接收一个参数,即要读取的文件名,然后系统会自动到/data/data/<package name>/files/
目录下去加载这个文件,并返回一个FileInputStream对象,得到了这个对象之后再通过Java流的方式就可以将数据读取出来了。
以下是一段简单的代码示例,展示了如何从文件中读取文本数据:
//读文件操作
private String load() throws IOException {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder stringBuilder = new StringBuilder();
try {
//通过openFileInput得到一个 FileInputStream对象
in = openFileInput("data");
//借助in构建出一个InputStreamReader 借助InputStreamReader构造BufferedReader
reader = new BufferedReader(new InputStreamReader(in));
//初始化一个字符串 用来读取文件内容
String line = "";
while((line = reader.readLine()) != null) {
//将该内容追加到StringBuilder后
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(reader != null) {
try {
reader.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
return stringBuilder.toString();
}
文件存储方面所用到的核心技术就是Context类中提供的openFileInput()
和openFileOutput()
方法,之后就是利用Java的各种流来进行读写操作。
文件存储的方式并不适合用于保存一些较为复杂的文本数据.
SharedPreferences存储
不同于文件的存储方式,SharedPreferences 是使用键值对的方式来存储数据的。也就是说,当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。而且SharedPreferences还支持多种不同的数据类型存储,如果存储的数据类型是整型,那么读取出来的数据也是整型的;如果存储的数据是一个字符串,那么读取出来的数据仍然是字符串。
将数据存储在SharedPreferences中
要想使用SharedPreferences来存储数据,首先需要获取到SharedPreferences对象。Android中主要提供了3种方法用于得到SharedPreferences对象。
- Context类中的getSharedPreferences ()方法
此方法接收两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个, SharedPreferences 文件都是存放在/data/data/<package name: >/shared_ prefs/
目录下的。第二个参数用于指定操作模式,目前只有MODE PRIVATE这一种模式可选,它是默认的操作模式,和直接传人0效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。其他几种操作模式均已被废弃,
- Activity类中的getPreferences ()方法
这个方法和Context中的getSharedPreferences ()
方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences 的文件名。
- PreferenceManager类中的getDefaultSharedPreferences()方法
这是一个静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences 文件。得到了SharedPreferences 对象之后,就可以开始向Shared-Preferences文件中存储数据了,主要可以分为3步实现。
(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 button = (Button)findViewById(R.id.save_data);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//通过getSharedPreferences()得到SharedPreferences对象
//得到一个SharedPreferences.Editor对象
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
//存入数据
editor.putString("name", "Judicious");
editor.putInt("age", 19);
editor.putBoolean("married", false);
//将数据提交,完成数据存储操作
editor.apply();
}
});
}
}
SharedPreferences文件是使用XML格式来对数据进行管理的。
从SharedPreferences中读取数据
SharedPreferences 对象中提供了一系列的get方法,用于对存储的数据进行读取,每种get方法都对应了SharedPreferences . Editor中的一种put方这些get方法都接收两个参数,第一个参数是键,传人存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,即表示当传人的键找不到对应的值时会以什么样的默认值进行返回。
public void onClick(View view) {
//获取SharedPreferences对象
SharedPreferences sharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
//获取之前存入的内容
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
boolean married = sharedPreferences.getBoolean("married", false);
MainActivity是标签 便于寻找打印日志
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "age is " + age);
Log.d("MainActivity", "married is " + married);
}
实现记住密码功能
CheckBox这是一个复选框控件,用户可以通过点击的方式来进行选中和取消。
public class LoginActivity extends BaseActivity{
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
private CheckBox remeberpass;
private EditText accountEdit;
private EditText passwordEdit;
private Button login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//获取实例 创建sharedPreferences对象
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
remeberpass = (CheckBox) findViewById(R.id.remember_pass);
accountEdit = (EditText) findViewById(R.id.account);
passwordEdit = (EditText) findViewById(R.id.password);
//获取存入的记住密码对象 一开始并不存在remember_password 选用默认对象
Boolean isRemember = sharedPreferences.getBoolean("remember_password", false);
login = (Button) findViewById(R.id.login);
//如果选择记住的话 将密码取出并显示
if(isRemember) {
String account = sharedPreferences.getString("account", "");
String password = sharedPreferences.getString("password", "");
accountEdit.setText(account);
passwordEdit.setText(password);
//将复选框设置为选中状态
remeberpass.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 = sharedPreferences.edit();
//检查复选框是否被选中
if(remeberpass.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();
}
}
});
}
}
在登录成功之后,会调用CheckBox的isChecked( )
方法来检查复选框是否被选中,如果被选中了,则表示用户想要记住密码,这时将remember_password 设置为true,然后把account和password对应的值都存人到SharedPreferences文件当中并提交。如果没有被选中,就简单地调用一下clear()
方法,将SharedPreferences文件中的数据全部清除掉。当用户选中了记住密码复选框,并成功登录一次之后,remember_password 键对应的值就是true了,这个时候如果再重新启动登录界面,就会从SharedPreferences文件中将保存的账号和密码都读取出来,并填充到文本输人框中,然后把记住密码复选框选中,这样就完成记住密码的功能了。
不过需要注意,将密码以明文的形式存储在SharedPreferences文件中是非常不安全的,很容易就会被别人盗取,因此在正式的项目里还需要结合一定的加密算法来对密码进行保护才行。
SQLite数据库存储
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务,SQLite又比一-般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。
创建数据库
Android为了让我们能够更加方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建和升级。
首先你要知道SQLiteOpenHelper是一个抽象类, 这意味着如果我们想要使用它的话,就需要创建一个自己的帮助类去继承它。SQLiteOpenHelper 中有两个抽象方法,分别是onCreate()
和onUpgrade()
, 我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。
SQLiteOpenHelper中还有两个非常重要的实例方法:getReadableDatabase()
和 getWritableDatabase()
。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写人的时候(如磁盘空间已满), getReadableDatabase()
方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()
方法则将出现异常。
SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少一点的那个构造方法即可。这个构造方法中接收4个参数,第一个参数是Context必须要有它才能对数据库进行操作。第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。第三个参数允许我们在查询数据的时候返回一个自定义的Cursor, 一般都是传入null。第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。构建出SQLiteOpenHelper 的实例之后,再调用它的getReadableDatabase( )
或getWritableDatabase( )
方法就能够创建数据库了,数据库文件会存放在/data/data/<package name>/databases/
目录下。此时,重写的onCreate()
方法
也会得到执行,所以通常会在这里去处理一些创建表的逻辑。
这里我们希望创建一个名 为BookStore.db的数据库,然后在这个数据库中新建一张Book表,表中有id(主键)、作者、价格、页数和书名等列。创建数据库表当然还是需要用建表语句的
- 创建自己的帮助类
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;
//获取传进来的上下文 便于后面的Toast
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) {
}
}
- 创建数据库
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper databaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建MyDatabaseHelper对象 并通过构造参数将数据库名指定为BookStore.db
databaseHelper = 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) {
//会检测程序中有没有这个数据库 没有的话会创建该数据库并调用MyDatabaseHelper中的onCreate()方法
//这样会创建Book表 并提示创建完成
databaseHelper.getWritableDatabase();
}
});
}
}
升级数据库
onUpgrade()
用于对数据库进行升级的,它在整个数据库的管理工作当中起着非常重要的作用,
目前DatabaseTest项目中已经有一张Book表用于存放书的各种详细数据,如果我们想再添加一张Category表用于记录图书的分类。就需要onUpgrade()
方法
databaseHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
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)";
public static final String CREATE_CATEGORY = "create table Cetegory ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
private Context mcontext;
//获取传进来的上下文 便于后面的Toast
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();
}
//要使该方法得到执行 在构造时第四个参数传入比1大的数
@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);
}
}
除了上述方法外,还可以卸载程序重新安装,这样原来的文件也会一起消失。然后重新运行程序,添加建表语句即可。
添加数据
对数据进行的操作无非有4种,即CRUD。其中C代表添加( Create), R代表查询( Retrieve), U代表更新(Update), D代表删除( Delete)。每一种操作又各自对应了一种SQL命令,添加数据时使用insert,查询数据时使用select,更新数据时使用update,删除数据时使用delete。Android也提供了一系列的辅助性方法,使得在Android中即使不去编写SQL语句,也能轻松完成所有的CRUD操作。
调用SQLiteOpenHelper的getReadableDatabase( )
或getWritableDatabase()
方法是可以用于创建和升级数据库的,不仅如此,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了。
SQLiteDatabase中提供了insert()
方法,这个方法就是用于添加数据的,他接收三个参数,第一个参数是表名,向哪张表里添加数据就传入哪张表的名字。第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值null,一般传入null。第三个参数是ContentValues对象,它提供了一系列的put()
方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper databaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建MyDatabaseHelper对象 并通过构造参数将数据库名指定为BookStore.db
databaseHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//会检测程序中有没有这个数据库 没有的话会创建该数据库并调用MyDatabaseHelper中的onCreate()方法
//这样会创建Book表 并提示创建完成
databaseHelper.getWritableDatabase();
}
});
Button addData = (Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//获取SQLiteDatabase对象
SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
//使用ContentValues来对要添加的数据进行组装
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);
//插入第一条数据
sqLiteDatabase.insert("Book", null, values);
values.clear();
//开始组装第二条数据
values.put("name", "The Lost Symbol");
values.put("autor", "Dan Brown");
values.put("pages",510);
values.put("price",19.95);
//插入第二条数据
sqLiteDatabase.insert("Book", null, values);
}
});
}
}
id 那一列没并没给它赋值。这是因为在前面创建表的时候,将id列设置为自增长了,它的值会在人库的时候自动生成,所以不需要手动给它赋值了。
更新数据
update()
方法用于对数据进行更新,这个方法接收4个参数,第一个参数和insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是ContentValues对象,要把更新数据在这里组装进去。第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。
Button updateData = (Button) findViewById(R.id.updata_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99); //将需要修改的信息添加进ContentValues
//更新数据 第三个参数是SQL语句的where部分 表示更新所有name=?行, ?是一个占位符,可以通过第四个参数提供占位符相应的内容
sqLiteDatabase.update("Book", values, "name = ?", new String[] {"The Da VinciCode"});
}
});
删除数据
SQLiteDatabase中提供了一个delete()
方法,专门用于删除数据,这个方法接收3个参数,第一个参数仍然是表名,第二、第三个参数又是用于约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
Button deleteData = (Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
//删除超过50页的书
sqLiteDatabase.delete("Book", "pages > ?",new String[] {"500"});
}
});
查询数据
SQLiteDatabase中还提供了一个query( )
方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传人7个参数。那我们就先来看一下这7个参数各自的含义吧。第一个参数还是表名,表示我们希望哪张表中查询数据。第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据。第五个参数用于指定需要去groupby;的列,不指定则表示不对查询结果进行group by操作。第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。更多详细的内容可以参考下表。其他几个query( )
方法的重载其实
也大同小异。
虽然query()
方法的参数非常多,但是我们不必为每条查询语句都指定所有的参数,多数情况下只需要传人少数几个参数就可以完成查询操作了。调用query()
方法后会返回一个Cursor对象,查询到的所有数据都将从这个对象中取出。
SQL游标(cursor)详细说明及内部循环使用示例
游标(cursor)是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果。每个游标区都有一个名字,用户可以用SQL语句逐一从游标中获取记录,并赋给主变量,交由主语言进一步处理。
游标是处理结果集的一种机制吧,它可以定位到结果集中的某一行,多数据进行读写,也可以移动游标定位到你所需要的行中进行操作数据。一般复杂的存储过程,都会有游标的出现,他的用处主要有:
- 定位到结果集中的某一行。
- 对当前位置的数据进行读写。
- 可以对结果集中的数据单独操作,而不是整行执行相同的操作。
- 是面向集合的数据库管理系统和面向行的程序设计之间的桥梁。
Button queryData = (Button) findViewById(R.id.query_data);
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
//使用query()方法查询对象 返回一个Cursor对象
Cursor cursor = sqLiteDatabase.query("Book",null, null, null, null, null, null);
//调用moveFirst方法将数据的指针移动到第一行的位置 进入一个循环 遍历每一行的数据
if(cursor.moveToFirst()) {
do {
//通过getColumnIndex()方法获取到某一列在表中对应位置的索引
//将该索引传入相应的取值方法中 就可以读取到数据了
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("TAG", name);
Log.d("TAG", author);
Log.d("TAG", pages + "");
Log.d("TAG", price + "");
}while (cursor.moveToNext());
}
//关闭Cursor
cursor.close();
}
});
使用SQL操作数据库
使用SQL完成CRUD
使用LitePal操作数据库
LitePal简介
LitePal是一款开源的Android数据库框架,它采用了对象关系映射( ORM )的模式,并将我们平时开发最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表和增删改查的操作。
LitePal的项目主页地址是:https://github.com/LitePalFramework/LitePal。
配置LitePal
在网上寻找最新的版本号,点这里获取最新版本号。将权限加入app/build.gradle中
dependencies {
implementation 'org.litepal.guolindev:core:3.2.3'
}
配置litepal.xml文件,在app/src/main/目录下 new ->Directory,创建一个assets目录,再在assets目录先新建litepal.xml文件,内容如下
<litepal>
/*
dbname指定数据库名 list用于指定所有的映射模型 version用于指定数据库版本号
*/
<dbname value="BookStore" ></dbname>
<version value="1"></version>
<list></list>
</litepal>
配置LitePalApplication,修改AndroidManifest.xml
<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/Theme.DatabaseTest">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
将项目的application配置为org.litepal.LitePalApplication,这样才能让LitePal的所有功能正常工作。
创建和升级数据库
我们使用的编程语言是面向对象语言,而使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了。
定义一个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类对应数据库的Book表,类中的每一个字段对应了表中的每一列。体现了对象映射,接下来将Book类添加到映射模型列表当中,修改litepal.xml
<litepal>
/*
dbname指定数据库名 list用于指定所有的映射模型 version用于指定数据库版本号
*/
<dbname value="BookStore" ></dbname>
<version value="1"></version>
<list>
<mapping class="com.example.litepaltest.Book"></mapping>
</list>
</litepal>
使用mapping标签来声明我们要配置的映射模型类,一定要使用完整的类名。不管有多少模型类需要映射,都使用同样的方法配置在<list>
标签下即可。
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) {
//创建数据库LitePal
LitePal.getDatabase();
}
});
}
}
升级数据库,添加字段 ,直接修改所添加的类即可,也可以添加一张表。 改完想要的东西后需要将版本号+1, 启动程序即可。LitePal会帮助我们自动保存之前的内容,不用担心数据丢失
<version value="2"></version>
添加数据
创建出模型类的实例,再将所有的数据设置好,调用save()方法
LitePal进行表管理操作时不需要模型类有任何的继承结构,但是进行CRUD操作时就不行了,必须要继承自DataSupport类才行,因此这里我们需要先把继承结构给加上。
public class Book extends LitePalSupport {
...
}
向Book表中添加数据
Button addDate = (Button) findViewById(R.id.add_data);
addDate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//创建Book对象
Book book = new Book();
//添加数据
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("Unknow");
//添加 save()是继承来的
book.save();
}
});
更新数据
- 对已存储的对象重新设值,然后重新调用save()方法即可。
对于LitePal来说,对象是否已存储就是根据调用model. isSaved( )方法的结果来判断的,返回true就表示已存储,返回false就表示未存储。
实际上只有在两种情况下model. isSaved()方法才会返回true, 一种情况是已经调用过model.save()
方法去添加数据了,此时model会被认为是已存储的对象。另一情况是model对象是通过LitePal提供的查询API查出来的,由于是从数据库中查到的对象,因此也会被认为是已存储的对象。
这种方法只能对已存储的对象操作。
Button updateData = (Button) findViewById(R.id.updata_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//创建Book对象
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();
//使用set方法对值进行修改
book.setPrice(10.99);
//添加 save()是继承来的
book.save();
}
});
- 第二种更新方式
如果需要把字段设为默认值不可以使用该方法
Button updateData = (Button) findViewById(R.id.updata_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//创建Book对象
Book book = new Book();
//设定需要更新的值
book.setPrice(14.59);
book.setPress("Anchor");
//执行更新操作 如果不指定条件的话 默认更新所有数据
book.updateAll("name = ? and author = ?", "The Lost Symbol", "Dan Brown");
}
});
- 将字段更新为默认值
Book book = new Book();
//将pages更新为默认值
book.setToDefault("pages");
//没有指定条件 默认所有
book.updateAll();
删除数据
- 直接调用delete()删除
- 调用deleteAll()删除
LitePal.delete(Book.class,id); //删除对应的id
//库名,约束条件,具体条件
LitePal.deleteAll(Book.class, "price < ?","15");
不指明条件默认删除所有数据
查询数据
- find()
- findAll()
//查询某个id
Book book = LitePal.find(Book.class, id);
//查询Book表
List<Book> books = LitePal.findAll(Book.class);
查询第一条数据
Book firstBook = LitePal.findFirst(Book.class);
Book lastBook = LitePal.findLast(Book.class);