标签:android sqlite 数据库 android数据存储
SQLite是一款轻量级的关系型数据库,它运算速度快,占用资源少,通常只需要几百k的内存就够了,支持标准的sql语法和数据库的ACID事务。在android中为了能够更加方便的管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单的对数据库进行创建和升级。
SQLiteOpenHelper是一个抽象类,如果我们要使用的话,就需要创建一个自己的类去继承它。SQLiteOpenHelper中有2个抽象方法,分别是onCreate和onUpgrade方法,我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中实现创建,升级数据库的逻辑。
SQLiteOpenHelper中还有2个非常重要的实例方法,getReadableDatabase()和getWritableDatabase()方法。这两个方法都可以创建或打开一个现有的数据库(如果数据库已经存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法方法将出现异常。
SQLiteOpenHelper中有2个构造方法可供重写,一般使用参数少点的那个构造方法即可。这个构造方法中接收4个参数,第一个参数是Context,必须要有Context才能对数据库进行操作。第二参数是数据库名,创建数据库时使用的就是这里使用的名称。第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传如null。第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。构建出SQLiteOpenHelper的实例后,在调用它的getReadableDatabase()或getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data/<package name>/databases/目录下。此时,重写的onCreate方法也会得到执行,所以通常在这里去处理一些创建表的逻辑。
下面我们通过例子来学习SQLiteOpenHelper的用法吧。
创建数据库
在android中integer表示整形,real表示浮点型,text表示文本类型,blob表示二进制类型。
首先创建一个android项目,项目名为DatabaseTest。
新建MyDatabaseHelper类继承SQLiteOpenHelper,代码如下所示:
package com.jack.databasetest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; 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 context; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub this.context=context; } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(CREATE_BOOK); Toast.makeText(context, "create succeeded", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } }
修改activity_main.xml中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/create_database" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="create database" /> </LinearLayout>
package com.jack.databasetest; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { 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 OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub /* * 在onCreate方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数 * 将数据库名指定为BookStore.db,版本号为1,然后在create database按钮的点击 * 事件里调用了getWritableDatabase()方法,这样当第一次点击create database按钮时 * 就会检测当前程序并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper * 中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击 * create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次了。 * * */ dbHelper.getWritableDatabase(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
当你在次点击按钮的时候不在出现Toast提示了,因为数据库已经存在了。这时我们用File Explor查看,你会发现databases目录下面出现了一个BookStore.db文件,但是book表是无法通过File Exploer看到的。
由于不能通过File Exploer进行查看所以我们这次借助于adb shell来对数据库和表的创建情况进行检查。
adb是android sdk中自带的一个调试工具,使用这个工具可以直接对连接在电脑上的手机或模拟器进行调试操作。它存放在sdk的platform-tools目录下,如果想要在命令中使用这个工具,就需要把它的路径配置到环境变量中。由于我使用的是window7,所以右击我的电脑---》属性---》高级----》环境变量,然后找到我们当前电脑配置的path路径,点击编辑,将platform-tools目录配置进去。
配置好环境变量之后就可以使用adb工具了,打开命令行界面,输入adb shell,就会进入到设备控制台如下所示:
然后使用cd命令进入到/data/data/com.jack.databasetest/databases/目录下面,并使用ls命令查看到该目录里的文件,如下:
这个目录下面出现了2个数据库文件,一个正是我们创建的BookStore.db,而另一个BookStore.db-journal则是为了让数据库能够支持事务而产生的临时日志文件,通常情况下这个文件的大小都是0字节。
接下来我们就借助sqlite命令来打开数据库了,只需键入sqlite3,后面加上数据库名即可,如下:
这时已经打开了BookStore.db数据库,现在就可以对这个数据库中的表进行管理了,首先来看看目前数据库中有哪些表,键入.table命令,如下所示:
此时数据库中有2张表,android_metadata是每个数据库中都会自动生成的,不用管它,而另外一张book表就是我们在MyDatabaseHelper中创建的。这里我们可以使用.schema命令来查看它们的建表语句,如下所示:
由上面的操作,我们知道数据库和表的确已经创建成功了,之后我们键入.exit或.quit命令可以退出数据库的编辑,在键入exit命令就可以退出设备控制台了。
升级数据库
MyDatabaseHelper中有一个onUpgrade()方法是用于对数据库进行升级的,下面我们对数据库进行升级操作,在数据库中在添加一张Category表用于记录书籍的分类。修改 MyDatabaseHelper类中的代码,如下所示:
package com.jack.databasetest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; 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)"; public static final String CREATE_CATEGORY="create table category(" + "id integer primary key autoincrement, " + "category_name text, " + "category_code integer)"; private Context context; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub this.context=context; } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); Toast.makeText(context, "create succeeded", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub db.execSQL("drop table if exists book"); db.execSQL("drop table if exists category"); onCreate(db); } }
在onUpgrade()方法中执行了2条drop语句,如果发现数据库中已经存在book表或category表,就将这2张表删掉,然后在调用onCreate()方法重写创建。接下来修改创建数据库的版本号,以前的是1,现在我们改成比1大的数,这里改成2.修改MainActivity中的代码,如下:
package com.jack.databasetest; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { 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 createDatabase=(Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub /* * 在onCreate方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数 * 将数据库名指定为BookStore.db,版本号为1,然后在create database按钮的点击 * 事件里调用了getWritableDatabase()方法,这样当第一次点击create database按钮时 * 就会检测当前程序并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper * 中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击 * create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次了。 * * */ dbHelper.getWritableDatabase(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
由此可以看出category表已经创建成功了,同时也说明我们的升级功能,的确起到作用了。
添加数据
数据库的操作有4种,即CRUD。其中c代表添加create,r代表查询retrieve,u代表更新update,d代表删除delete。
调用SQLiteOpenHelper的getReadableDatabase()或getWritableDatabase()方法是可以用于创建和升级,不仅如此,这2个方法还都会返回一个SQLiteDatabase对象,借助这个对象可以对数据库进行CRUD操作了。
SQLiteDatabase中提供了一个insert()方法,这个方法专门用于添加数据的。它接收3个参数,第一个参数是表名,我们希望向哪张表添加数据,这里就传入该表的名字。第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能直接传入null即可。第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
下面进行数据的添加,修改activity_main.xml中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/create_database" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="create database" /> <Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="add data" /> </LinearLayout>
修改MainActivity中的代码,如下所示:
package com.jack.databasetest; import android.app.Activity; import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { 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 createDatabase=(Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub /* * 在onCreate方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数 * 将数据库名指定为BookStore.db,版本号为1,然后在create database按钮的点击 * 事件里调用了getWritableDatabase()方法,这样当第一次点击create database按钮时 * 就会检测当前程序并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper * 中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击 * create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次了。 * * */ dbHelper.getWritableDatabase(); } }); //添加数据 Button addData=(Button) findViewById(R.id.add_data); addData.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); //开始组装第一条数据 values.put("name", "the da vinci code"); values.put("author", "jack"); values.put("pages", 500); values.put("price", 18.98); db.insert("book", null, values);//插入第一条数据 values.clear(); //开始组装第二条数据 values.put("name", "the lost symbol"); values.put("author", "jack"); values.put("pages", 600); values.put("price", 56.87); db.insert("book", null, values);//插入第二条数据 } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
可以看到刚刚插入的2条数据都已经准确的插入了book表中。
更新数据
SQLiteDatabase中提供了一个非常好用的update()方法用于对数据进行更新,这个方法接收4个参数,第一个参数和insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是ContentValues对象,要把更新数据在这里组装进去。第三个参数,第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。
修改activity_main.xml中的代码,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/create_database" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="create database" /> <Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="add data" /> <Button android:id="@+id/update_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="update data" /> </LinearLayout>
package com.jack.databasetest; import android.app.Activity; import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { 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 createDatabase=(Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub /* * 在onCreate方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数 * 将数据库名指定为BookStore.db,版本号为1,然后在create database按钮的点击 * 事件里调用了getWritableDatabase()方法,这样当第一次点击create database按钮时 * 就会检测当前程序并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper * 中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击 * create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次了。 * * */ dbHelper.getWritableDatabase(); } }); //添加数据 Button addData=(Button) findViewById(R.id.add_data); addData.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); //开始组装第一条数据 values.put("name", "the da vinci code"); values.put("author", "jack"); values.put("pages", 500); values.put("price", 18.98); db.insert("book", null, values);//插入第一条数据 values.clear(); //开始组装第二条数据 values.put("name", "the lost symbol"); values.put("author", "jack"); values.put("pages", 600); values.put("price", 56.87); db.insert("book", null, values);//插入第二条数据 } }); //更新数据 Button updateDate=(Button) findViewById(R.id.update_data); updateDate.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); values.put("price", 99.9); db.update("book", values, "name=?", new String[]{"the da vinci code"}); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
运行程序,点击updat data按钮,在查看数据库里面的book表的内容,如下:
可以看到数据已经改变了,update方法中的第三个参数对应sql语句的where部分,表示更新所有name=?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数据为第三个参数中的每一个占位符指定相应的内容。
删除数据
删除数据应该是最简单的,SQLiteDatabase中提供了一个delete()方法专门用于删除数据,这个方法接收三个参数,第一个参数是表名,第二,第三个参数又是用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
修改activity_main.xml中的代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/create_database" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="create database" /> <Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="add data" /> <Button android:id="@+id/update_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="update data" /> <Button android:id="@+id/delete_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="delete data" /> </LinearLayout>
package com.jack.databasetest; import android.app.Activity; import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { 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 createDatabase=(Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub /* * 在onCreate方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数 * 将数据库名指定为BookStore.db,版本号为1,然后在create database按钮的点击 * 事件里调用了getWritableDatabase()方法,这样当第一次点击create database按钮时 * 就会检测当前程序并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper * 中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击 * create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次了。 * * */ dbHelper.getWritableDatabase(); } }); //添加数据 Button addData=(Button) findViewById(R.id.add_data); addData.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); //开始组装第一条数据 values.put("name", "the da vinci code"); values.put("author", "jack"); values.put("pages", 500); values.put("price", 18.98); db.insert("book", null, values);//插入第一条数据 values.clear(); //开始组装第二条数据 values.put("name", "the lost symbol"); values.put("author", "jack"); values.put("pages", 600); values.put("price", 56.87); db.insert("book", null, values);//插入第二条数据 } }); //更新数据 Button updateDate=(Button) findViewById(R.id.update_data); updateDate.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); values.put("price", 99.9); db.update("book", values, "name=?", new String[]{"the da vinci code"}); } }); //删除数据 Button deleteButton=(Button) findViewById(R.id.delete_data); deleteButton.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); db.delete("book", "pages>?", new String[]{"500"}); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
运行程序,界面如下所示:
删除数据之前:
点击delete data按钮之后,再次查询数据,数据情况如下:
可见,页数大于500页的那一条的数据被删除了
查询数据:
SQLiteDatabase中提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入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 | 指定查询结果的排序方式 |
下面我们进行查询的操作,首先修改activity_main.xml中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/create_database" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="create database" /> <Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="add data" /> <Button android:id="@+id/update_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="update data" /> <Button android:id="@+id/delete_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="delete data" /> <Button android:id="@+id/query_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="query data" /> </LinearLayout>
package com.jack.databasetest; import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { 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 createDatabase=(Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub /* * 在onCreate方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数 * 将数据库名指定为BookStore.db,版本号为1,然后在create database按钮的点击 * 事件里调用了getWritableDatabase()方法,这样当第一次点击create database按钮时 * 就会检测当前程序并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper * 中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击 * create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次了。 * * */ dbHelper.getWritableDatabase(); } }); //添加数据 Button addData=(Button) findViewById(R.id.add_data); addData.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); //开始组装第一条数据 values.put("name", "the da vinci code"); values.put("author", "jack"); values.put("pages", 500); values.put("price", 18.98); db.insert("book", null, values);//插入第一条数据 values.clear(); //开始组装第二条数据 values.put("name", "the lost symbol"); values.put("author", "jack"); values.put("pages", 600); values.put("price", 56.87); db.insert("book", null, values);//插入第二条数据 } }); //更新数据 Button updateDate=(Button) findViewById(R.id.update_data); updateDate.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); values.put("price", 99.9); db.update("book", values, "name=?", new String[]{"the da vinci code"}); } }); //删除数据 Button deleteButton=(Button) findViewById(R.id.delete_data); deleteButton.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); db.delete("book", "pages>?", new String[]{"500"}); } }); //查询数据 Button queryButton=(Button) findViewById(R.id.query_data); queryButton.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); //查询book表中的所有数据 Cursor cursor=db.query("book", null, null, null,null, null, null); /* * 调用moveToFirst()方法将数据的指针移动到第一行的位置,然后进入一个循环当中,去遍历查询到 * 的每一行数据。在这个循环中可以通过Cursor的getColumnIndex()方法取到某一列在表中对应的位置索引, * 然后将这个索引传入到相应的取值方法当中,接可以得到从数据库中查询到的数据了。接着使用Log的方式将 * 取出来的数据打印出来,借此来检查一下读取工作有没有成功完成。最后别忘了调用close()方法来关闭Cursor。 * */ 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(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
点击查询按钮,打印出如下的内容:
book中的数据已经查询出来了,这只是简单的查询,如果要进行复杂的查询,当然要用到查询中的参数了。
使用SQL操作数据库
虽然android中提供了很多方便的api用于操作数据库,不过总会有一些人不太习惯,下面我们试试用sql来操作数据库。
添加数据:
db.execSQL("insert into book(name,author,pages,price) values(?,?,?,?)",
new String[]{"the da vinci code","dan brown","454","17.98"});
db.execSQL("insert into book(name,author,pages,price) values(?,?,?,?)",
new String[]{"the lost symbol","dan brown","600","20.88"});
更新数据:
db.execSQL("update book set price =? where name =?",new String[]{"12.99","the da vinci code"});
删除数据:
db.execSQL("delete from book where pages>?",new String[]{"500"});
查询数据的方法如下:
db.rawQuery("select * from book",null);
上面是使用sql语句进行数据的CRUD操作的。
SQLite数据库的最佳实践
使用事务:
SQLite数据库是支持事务的,事务的特性可以保证某一系列的操作要么全部完成,要么一个都不会完成。
下面我们试试在android中的事务吧,仍然在DatabaseTest项目的基础上进行修改。比如book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可以先使用delete方法将book表中的数据删除,然后在使用insert方法将新的数据添加到列表中。我们要保证的是,删除旧数据和添加新数据的操作必须一起完成,否则就还要继续保留原来的旧数据。修改activity_main.xml中的代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/create_database" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="create database" /> <Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="add data" /> <Button android:id="@+id/update_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="update data" /> <Button android:id="@+id/delete_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="delete data" /> <Button android:id="@+id/query_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="query data" /> <Button android:id="@+id/replace_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="replace data" /> </LinearLayout>
修改MainActivity中的代码:
package com.jack.databasetest; import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { 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 createDatabase=(Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub /* * 在onCreate方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数 * 将数据库名指定为BookStore.db,版本号为1,然后在create database按钮的点击 * 事件里调用了getWritableDatabase()方法,这样当第一次点击create database按钮时 * 就会检测当前程序并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper * 中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击 * create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次了。 * * */ dbHelper.getWritableDatabase(); } }); //添加数据 Button addData=(Button) findViewById(R.id.add_data); addData.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); //开始组装第一条数据 values.put("name", "the da vinci code"); values.put("author", "jack"); values.put("pages", 500); values.put("price", 18.98); db.insert("book", null, values);//插入第一条数据 values.clear(); //开始组装第二条数据 values.put("name", "the lost symbol"); values.put("author", "jack"); values.put("pages", 600); values.put("price", 56.87); db.insert("book", null, values);//插入第二条数据 } }); //更新数据 Button updateDate=(Button) findViewById(R.id.update_data); updateDate.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); ContentValues values=new ContentValues(); values.put("price", 99.9); db.update("book", values, "name=?", new String[]{"the da vinci code"}); } }); //删除数据 Button deleteButton=(Button) findViewById(R.id.delete_data); deleteButton.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); db.delete("book", "pages>?", new String[]{"500"}); } }); //查询数据 Button queryButton=(Button) findViewById(R.id.query_data); queryButton.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); //查询book表中的所有数据 Cursor cursor=db.query("book", null, null, null,null, null, null); /* * 调用moveToFirst()方法将数据的指针移动到第一行的位置,然后进入一个循环当中,去遍历查询到 * 的每一行数据。在这个循环中可以通过Cursor的getColumnIndex()方法取到某一列在表中对应的位置索引, * 然后将这个索引传入到相应的取值方法当中,接可以得到从数据库中查询到的数据了。接着使用Log的方式将 * 取出来的数据打印出来,借此来检查一下读取工作有没有成功完成。最后别忘了调用close()方法来关闭Cursor。 * */ 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(); } }); //android数据库中的事务处理 Button replaceButton=(Button) findViewById(R.id.replace_data); replaceButton.setOnClickListener(new OnClickListener(){ /* * SQLiteDatabase的beginTransaction()方法来开启一个事务,然后在一个异常捕获 * 的代码中去执行具体的数据库操作,当所有的操作完成之后,调用setTransactionSuccessful(), * 表示事务已经执行成功了,最后在finally代码块中调用endTransaction()来结束事务。 * 注意观察,我们在刚删除旧数据的操作完成之后手动抛出一个NullPointerException,这样 * 添加新数据的代码就得不到执行。不过由于事务的存在,中途出现异常会导致事务的失败,此时 * 旧数据应该是删除不掉的。 * */ @Override public void onClick(View arg0) { // TODO Auto-generated method stub SQLiteDatabase db=dbHelper.getWritableDatabase(); db.beginTransaction();//开启事务 try{ db.delete("book", null, null); if(true){ throw new NullPointerException(); } ContentValues values=new ContentValues(); values.put("name", "jack"); values.put("author", "tom"); values.put("pages", 810); values.put("price",60.78); db.insert("book", null, values); db.setTransactionSuccessful();//事务已经执行成功 }catch(Exception e){ e.printStackTrace(); }finally{ db.endTransaction();//结束事务 } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
运行程序并点击replace data按钮,你会发现book表中存在的还是之前的旧数据。然后将手动抛出异常的那行代码去除掉,在重新运行下程序,此时点击一下replace data按钮就会将book表中的数据替换成新数据了。
升级数据库的最佳实战
在产品上线了的时候,怎么升级数据库,并保存原来的数据呢?下面我们来学习如何升级数据库。我们知道每一个数据库都有一个版本号,当指定的数据库版本号大于当前数据库版本号的时候,就会进入到onUpgrade()方法中执行更新操作。这里需要为每一个版本号赋予它各自改变的内容,然后在onUpgrade()方法中对当前数据库版本号进行判断,在执行相应的改变就可以了。
下面我们来一个模拟数据库升级的案例,还是由MyDatabaseHelper类来对数据库进行管理。第一版的程序程序,只需要创建一张book表,MyDatabaseHelper中的代码如下所示:
package com.jack.databasetest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; 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)"; /*public static final String CREATE_CATEGORY="create table category(" + "id integer primary key autoincrement, " + "category_name text, " + "category_code integer)";*/ private Context context; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub this.context=context; } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(CREATE_BOOK); /*db.execSQL(CREATE_CATEGORY);*/ /*Toast.makeText(context, "create succeeded", Toast.LENGTH_SHORT).show();*/ } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub /*db.execSQL("drop table if exists book"); db.execSQL("drop table if exists category"); onCreate(db);*/ } }
不过,几个星期之后又有了新的需求,这次需要向数据库中在添加一张Category表,于是修改MyDatabaseHelper中的代码,如下所示:
package com.jack.databasetest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; 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)"; public static final String CREATE_CATEGORY="create table category(" + "id integer primary key autoincrement, " + "category_name text, " + "category_code integer)"; //private Context context; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub //this.context=context; } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); /*Toast.makeText(context, "create succeeded", Toast.LENGTH_SHORT).show();*/ } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub /*db.execSQL("drop table if exists book"); db.execSQL("drop table if exists category"); onCreate(db);*/ switch(oldVersion){ case 1: db.execSQL(CREATE_CATEGORY); default:break; } } }
但是没过多久,新的需求又来了,这次要给book表和category表之间建立联系,需要在book表中添加一个category_id的字段。再次修改MyDatabaseHelper中的代码,如下所示:
package com.jack.databasetest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; 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, " + "category_id integer)"; public static final String CREATE_CATEGORY="create table category(" + "id integer primary key autoincrement, " + "category_name text, " + "category_code integer)"; //private Context context; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub //this.context=context; } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); /*Toast.makeText(context, "create succeeded", Toast.LENGTH_SHORT).show();*/ } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub /*db.execSQL("drop table if exists book"); db.execSQL("drop table if exists category"); onCreate(db);*/ switch(oldVersion){ case 1: db.execSQL(CREATE_CATEGORY); case 2: db.execSQL("alert table book add column category_id integer"); default:break; } } }
可以看到,首先我们在book表的建表语句中添加了一个category_id列,这样当用户直接安装第三版的程序时,这个新增的列就已经自动添加成功了,然而,如果用户之前已经安装了某一版本的程序,现在需要覆盖安装,就会进入到升级数据库的操作中。在onUpgrade()方法里,我们添加了一个新的case,如果当前数据库版本号是2,就会执行alter命令来为book表新增加一个category_id列。
注意:switch中每个case的最后都是没有使用break的,这是为了保证在跨数据库版本升级的时候,每次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序,那么case 2中的逻辑就会执行。而如果用户是直接从第一版升级到第三版的程序,那么case 1和case2中的逻辑都会执行,使用这种方式来维护数据库的升级不管数据库怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失。
转载请注明来自:http://blog.csdn.net/j903829182/article/details/40981205
android学习十一(android的SQLite数据库的使用)
标签:android sqlite 数据库 android数据存储
原文地址:http://blog.csdn.net/j903829182/article/details/40981205