SQLite学习笔记
前言:上一章我们介绍了sqlite的一些基本知识以及在dos命令下对sqlite进行的增删改查的操作,这一章我们将在android项目中实际来操作sqlite。
1、 SQLiteDatabase的介绍
Android提供了创建和是用SQLite数据库的API。SQLiteDatabase代表一个数据库对象,提供了操作数据库的一些方法。在 Android的SDK目录下有sqlite3工具,我们可以利用它创建数据库、创建表和执行一些SQL语句。下面是SQLiteDatabase的常用方法:
方法名称 |
方法描述 |
openOrCreateDatabase(String path,SQLiteDatabase.CursorFactory factory) |
打开或创建数据库 |
insert(String table,String nullColumnHack,ContentValues values) |
添加一条记录 |
delete(String table,String whereClause,String[] whereArgs) |
删除一条记录 |
query(String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy) |
查询一条记录 |
update(String table,ContentValues values,String whereClause,String[] whereArgs) |
修改记录 |
execSQL(String sql) |
执行一条SQL语句 |
close() |
关闭数据库 |
2、SQLiteOpenHelper
该类是SQLiteDatabase一个辅助类。这个类主要生成一 个数据库,并对数据库的版本进行管理。当在程序当中调用这个类的方法getWritableDatabase()或者 getReadableDatabase()方法的时候,如果当时没有数据,那么Android系统就会自动生成一个数据库。 SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里面的3个函数:
1.onCreate(SQLiteDatabase)
在数据库第一次生成的时候会调用这个方法,也就是说,只有在创建数据库的时候才会调用,当然也有一些其它的情况,一般我们在这个方法里边生成数据库表。
2. onUpgrade(SQLiteDatabase,int,int)
当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。
3. onOpen(SQLiteDatabase):
这是当打开数据库时的回调函数,一般在程序中不是很常使用。
写了这么多,改改用实际例子来说明上面的内容了。下面这个操作数据库的实例实现了创建数据库,创建表以及数据库的增删改查的操作。
3、 实例一,利用execSQL()和rawQuery()方法来实现sqlite数据库的增删改查
程序中需要用到的模型层Student:
package com.demo.sqlite.model; public class Student { private int sid; private String sname; private short age; public Student(int sid, String sname, short age) { super(); this.sid = sid; this.sname = sname; this.age = age; } public Student() { super(); } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public short getAge() { return age; } public void setAge(short age) { this.age = age; } @Override public String toString() { return "sid:" + sid + ",sname:" + sname + ",age:" + age; } }
(1) 编写一个类继承自SQLiteOpenHelper
package com.demo.sqlite.dao; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; //sqlite中文社区:http://www.sqlite.com.cn/POPlist.asp?classid=5 public class DBOpenHelper extends SQLiteOpenHelper { // 数据库的版本 private static final int VERSION = 1; // 数据库的名称 private static final String DBNAME = "data.db"; // 数据表名 private static final String TABLE_NAME = "t_student"; public DBOpenHelper(Context context) { super(context, DBNAME, null, VERSION); } // 数据第一次创建的时候执行 @Override public void onCreate(SQLiteDatabase db) { // 创建数据库表 // db.execSQL("create table t_student(sid integer,sname varchar(20),age integer)"); db.execSQL("create table " + TABLE_NAME + " (sid integer,sname varchar(20),age integer)"); } // 更新版本,更新数据(数据的备份) @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.i("StudentDaoTest", "Upgrade"); String tempstudent = "temp_student"; // 修改t_student表为temp_student db.execSQL("alter table " + TABLE_NAME + " rename to " + tempstudent); // 创建t_student表 db.execSQL("create table " + TABLE_NAME + " (sid integer,sname varchar(20),age integer,sex varchar(4))"); // 查询temp_student表中的数据并插入到t_student表中 String sql = "insert into " + TABLE_NAME + " (sid,sname,age,sex) select sid,sname,age,‘男‘ from " + tempstudent; db.execSQL(sql); } }
(2) 业务逻辑层StudentDao的编写
package com.demo.sqlite.dao; import java.util.ArrayList; import java.util.List; import com.demo.sqlite.model.Student; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; public class StudentDao { private DBOpenHelper helper; private SQLiteDatabase db; public StudentDao(Context context) { helper = new DBOpenHelper(context); } /** * 添加学生信息 * @param stu */ public void add(Student stu) { db = helper.getWritableDatabase(); db.execSQL("insert into t_student(sid,sname,age) values(?,?,?)", new Object[] { stu.getSid(), stu.getSname(), stu.getAge() }); } /** * 更新学生信息 * @param stu */ public void update(Student stu) { db = helper.getWritableDatabase(); db.execSQL("update t_student set sname = ? ,age = ? where sid = ? ", new Object[] {stu.getSname(), stu.getAge() ,stu.getSid()}); } /** * 根据sid查询学生信息 * @param sid * @return */ public Student findStudentById(int sid) { db = helper.getWritableDatabase(); Cursor cursor = db.rawQuery( "select sid,sname,age from t_student where sid = ? ", new String[] { String.valueOf(sid) }); if (cursor.moveToNext()) { return new Student(cursor.getInt(cursor.getColumnIndex("sid")), cursor.getString(cursor.getColumnIndex("sname")), cursor.getShort(cursor.getColumnIndex("age"))); } return null; } /** * 根据sid删除学生信息 * @param sid */ public void delete(Integer... sids) { if (sids.length > 0) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < sids.length; i++) { sb.append("?").append(","); } sb.deleteCharAt(sb.length() - 1); db = helper.getWritableDatabase(); db.execSQL("delete from t_student where sid in (" + sb + ")", (Object[]) sids); } } /** * 查找学生信息 * @param start 起始位置 * @param count 学生数量 * @return */ public List<Student> getStudent(int start,int count){ List<Student> students = new ArrayList<Student>(); db = helper.getWritableDatabase(); Cursor cursor = db.rawQuery("select sid,sname,age from t_student limit ?,? ",new String[]{String.valueOf(start),String.valueOf(count)}); while(cursor.moveToNext()){ students.add(new Student(cursor.getInt(cursor.getColumnIndex("sid")), cursor.getString(cursor.getColumnIndex("sname")), cursor.getShort(cursor.getColumnIndex("age")))); } return students; } /** * 获取学生数量 * @return */ public long getCount(){ db = helper.getWritableDatabase(); Cursor cursor = db.rawQuery("select count(sid) from t_student",null); if(cursor.moveToNext()){ return cursor.getLong(0); } return 0; } }
(3) 为了更快速的检测我们写的代码的正确性,我这里就不在使用Activity来展示并实行功能了,而是直接采用单元测试,这样更方便
3-1)编写测试代码,在与src同级下建立一个test资源文件夹,接着建一个和android项目包名一致的包,我这里是com.demo.sqlite.activity,然后编写一个类继承AndroidTestCase,测试代码如下:
package com.demo.sqlite.activity; import java.util.List; import com.demo.sqlite.dao.StudentDao; import com.demo.sqlite.model.Student; import android.test.AndroidTestCase; import android.util.Log; /** * 单元测试 * @author yinbenyang * */ public class StudentDaoTest extends AndroidTestCase { //日志输出 private static final String TAG = "StudentDaoTest"; //测试添加方法 public void testAdd(){ StudentDao sdao = new StudentDao(this.getContext()); Student stu = new Student(1,"zhangsan",(short)23); sdao.add(stu); Log.i(TAG, "添加学生成功"); } //测试修改方法 public void testUpdate(){ StudentDao sdao = new StudentDao(this.getContext()); Student stu = sdao.findStudentById(1); stu.setSname("lisi"); sdao.update(stu); Log.i(TAG, "修改学生信息成功"); } //测试根据id查找学生方法 public void testFindStudentById(){ StudentDao sdao = new StudentDao(this.getContext()); Student stu = sdao.findStudentById(1); if(stu == null){ Log.i(TAG, "not find"); }else{ Log.i(TAG, stu.toString()); } } //测试删除方法 public void testDelete(){ StudentDao sdao = new StudentDao(this.getContext()); sdao.delete(1,2); Log.i(TAG, "删除成功"); } //测试获取总人数的方法 public void testGetCount(){ StudentDao sdao = new StudentDao(this.getContext()); long count = sdao.getCount(); Log.i(TAG, count+""); } //测试分页方法 public void testGetStudent(){ StudentDao sdao = new StudentDao(this.getContext()); List<Student> lists = sdao.getStudent(0, 1); for (Student s : lists) { Log.i(TAG, s.toString()); } } }
3-2)在android的AndroidManifest.xml文件中进行单元测试的配置:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demo.sqlite.activity"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.demo.sqlite.activity"
android:label="Test My App" >
</instrumentation>
</manifest>
------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------
实例二,利用SqliteDatabase自带的insert(),delete(),update(),query()进行增删改查的操作
(1)编写一个类继承自SQLiteOpenHelper,同上,不在赘述
(2)业务逻辑层StudentDao2的编写:
package com.demo.sqlite.dao; import java.util.ArrayList; import java.util.List; import com.demo.sqlite.model.Student; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; public class StudentDao2 { private DBOpenHelper helper; private SQLiteDatabase db; public StudentDao2(Context context) { helper = new DBOpenHelper(context); } /** * 添加学生信息 * @param stu */ public void add(Student stu) { db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("sid", stu.getSid()); values.put("sname", stu.getSname()); values.put("age", stu.getAge()); /** * 第一个参数:String table:插入数据的数据表名 * 第二个参数:String nullColumnHack:当values参数为空或者里面没有内容的时候,insert是会失败的(不允许插入空行), * 为了防止这种情况,我们指定一个列名,如果发现要插入的行为空行时,就会将你设定的列名的值设为null,然后在向数据库表中插入, * 例如当values为空时,实际上sql语句变成了:insert into t_student(sid) values(null); * 第三个参数:ContentValues values:一个ContentValues对象,类似于map */ db.insert("t_student", "sid", values); } /** * 更新学生信息 * @param stu */ public void update(Student stu) { db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("sname", stu.getSname()); values.put("age", stu.getAge()); db.update("t_student", values, " sid = ? ", new String[]{String.valueOf(stu.getSid())}); } /** * 根据sid查询学生信息 * @param sid * @return */ public Student findStudentById(int sid) { db = helper.getWritableDatabase(); Cursor cursor = db.query("t_student", new String[]{"sid","sname","age"},"sid = ? " ,new String[]{String.valueOf(sid)},"","",""); if(cursor.moveToNext()){ //这里使用0,1,2是根据各自在t_student表中的位置来定位的,也可以使用cursor.getInt(cursor.getColumnIndex("sid")) return new Student(cursor.getInt(0), cursor.getString(1),cursor.getShort(2)); } return null; } /** * 根据sid删除学生信息 * @param sid */ public void delete(Integer... sids) { if (sids.length > 0) { StringBuffer sb = new StringBuffer(); String[] strSid = new String[sids.length]; for (int i = 0; i < sids.length; i++) { sb.append("?").append(","); strSid[i] = String.valueOf(sids[i]); } sb.deleteCharAt(sb.length() - 1); db = helper.getWritableDatabase(); db.delete("t_student", "sid in("+sb+")", strSid); } } /** * 查找学生信息 * @param start 起始位置 * @param count 学生数量 * @return */ public List<Student> getStudent(int start,int count){ List<Student> students = new ArrayList<Student>(); db = helper.getWritableDatabase(); /** query()方法中各参数的说明 * ①table:表名称 ②columns:列名称数组 ③selection:条件字句,相当于where ④selectionArgs:条件字句,参数数组 ⑤groupBy:分组列 ⑥having:分组条件 ⑦orderBy:排序列 ⑧limit:分页查询限制 */ Cursor cursor = db.query("t_student", new String[]{"sid","sname","age"}, null, null, null, null, "sid desc",start+","+count); while(cursor.moveToNext()){ students.add(new Student(cursor.getInt(0), cursor.getString(1), cursor.getShort(2))); } return students; } /** * 获取学生数量 * @return */ public long getCount(){ db = helper.getWritableDatabase(); Cursor cursor = db.query("t_student", new String[]{"count(*)"}, null, null, null, null, null); if(cursor.moveToNext()){ return cursor.getLong(0); } return 0; } }
(3) 单元测试类的编写,和上面第一种实例的差不多,就是将StudentDao换成StudentDao2,此处不在赘述。
4、 运行测试
(1)测试添加方法testAdd(),点击项目名-->test-->包名-->StudentDaoTest-->testAdd(),右键run as,选择Android JUnit Test,如图
接着我们在测试查询的结果,如图:
生成的data.db存放在data/data/项目包名/dataases/data.db中。可以通过ddms视图看到,也可以在dos下使用命令来查看:
(2)在上面的DBOpenHelper 这个类里面有一个onUpgrade()方法,这个是用来更新数据库版本的操作的,当数据库版本有改变时,将会执行这个方法,例如现在将DBOpenHelper 里面的VERSION改为2,在运行测试testFindStudentById()方法会出现什么情况呢?如下图:
这时我们看到onUpgrade里面的log日志打印出来了,并且执行了里面的操作【向t_student表中添加一个sex字段,实现的思路是:先将t_student表重命名为temp_student,然后重新创建一个带有sex字段的表,接着查询出temp_student表中的数据并插入到t_student表中,也就是说此时的data.db中有两张表:t_student和temp_student,t_student表中有四个四段,而temp_studnet表中只有三个字段】,使用命令来查看效果: