不会数据库的司机不是好厨师。今天抽了点时间对Sqlite数据库的使用做了个总结记录。
老规矩,在节目开始之前,首先来一个搞笑段子:
未婚女发现自己怀孕后,第一反应是:“完了,我妈非弄死我不可。”殊不知,她肚子里的孩子也在想:“完了,我妈非弄死我不可。。。”
Android中对数据库的操作主要就是四个:增、删、改、查,加上建表和更新。相比Oracle很多的DDL、DCL操作还是简单很多的。
这篇文章使用User作为例子,记录一下增、删、改、查的使用方法。
首先是Java Bean:User
public class User { private int id = 0; private String name = null; private int age = 0; public User(){} public User(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "[" + id + ", " + name + ", " + age + "]"; } }
然后是数据库操作,就一个类:SqliteOpenHelper我先把整个类的代码贴出来,再一段段注释。
import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import java.util.ArrayList; import java.util.List; public class DataBaseHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "test.db"; private static final int VERSION_CODE = 101; public static final String TABLE = "user"; public static final String ID = "id"; public static final String NAME = "name"; public static final String AGE = "age"; private SQLiteDatabase db; private static DataBaseHelper instance = null; public static DataBaseHelper getInstance(Context context) { if(instance == null) { instance = new DataBaseHelper(context); } return instance; } private DataBaseHelper(Context context) { super(context, DATABASE_NAME, null, VERSION_CODE); db = this.getWritableDatabase(); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE + " (" + ID + " INTEGER NOT NULL," + NAME + " TEXT NOT NULL," + AGE + " INTEGER NOT NULL);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (newVersion > oldVersion) { db.execSQL("DROP TABLE " + TABLE + " IF EXISTS"); } } public boolean insertUser(User user) { boolean result = false; if(user != null) { synchronized (db) { ContentValues values = new ContentValues(); values.put(ID, user.getId()); values.put(NAME, user.getName()); values.put(AGE, user.getAge()); long insertRowId = db.insert(TABLE, ID, values); result = insertRowId != -1; } } return result; } //批量插入 public boolean insertUsers(List<User> users) { boolean result = true; if(users != null && users.size() > 0) { db.beginTransaction(); for(User user : users) { boolean insert = insertUser(user); if (!insert) { result = false; break; } } db.setTransactionSuccessful(); db.endTransaction(); } return result; } //删除user public boolean deleteUser(int id) { boolean result = false; if(id >= 0) { synchronized (db) { int deleteRows = db.delete(TABLE, ID + "=?", new String[]{String.valueOf(id)}); result = deleteRows > 0; } } return result; } //更新user public boolean updateUser(User user) { boolean result = false; if(user != null) { synchronized (db) { ContentValues values = new ContentValues(); values.put(NAME, user.getName()); values.put(AGE, user.getAge()); int updateRows = db.update(TABLE, values, ID, new String[]{String.valueOf(user.getId())}); result = updateRows > 0; } } return result; } //查询所有人 public List<User> queryAllUsers() { List<User> users = new ArrayList<User>(); synchronized (db) { Cursor cursor = db.query(TABLE, new String[]{ID, NAME, AGE}, null, null, null, null, String.valueOf(ID)); if(cursor != null && cursor.getCount() > 0) { while(cursor.moveToNext()) { User user = new User(); user.setId(cursor.getInt(cursor.getColumnIndex(ID))); user.setName(cursor.getString(cursor.getColumnIndex(NAME))); user.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); users.add(user); } } if (cursor != null) { cursor.close(); } } return users; } //查询年龄大于20岁的所有人 public List<User> queryUsersByAge(int minAge) { List<User> users = new ArrayList<User>(); if(minAge > 0) { synchronized (db) { Cursor cursor = db.query(TABLE, new String[]{ID, NAME, AGE}, ID + ">?", new String[]{String.valueOf(minAge)}, null, null, String.valueOf(ID)); if(cursor != null && cursor.getCount() > 0) { while(cursor.moveToNext()) { User user = new User(); user.setId(cursor.getInt(cursor.getColumnIndex(ID))); user.setName(cursor.getString(cursor.getColumnIndex(NAME))); user.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); users.add(user); } } if (cursor != null) { cursor.close(); } } } return users; } //分页查询:页面上显示User按年龄排序,每页显示10个,查询第n页的User public List<User> queryUsersByPage(int pageNumber) { List<User> users = new ArrayList<User>(); if(pageNumber > 0) { synchronized (db) { String queryString = "select * from " + TABLE + " order by " + AGE + "limit 10 offset " + (10 * (pageNumber - 1)); Cursor cursor = db.rawQuery(queryString, null); if(cursor != null && cursor.getCount() > 0) { while(cursor.moveToNext()) { User user = new User(); user.setId(cursor.getInt(cursor.getColumnIndex(ID))); user.setName(cursor.getString(cursor.getColumnIndex(NAME))); user.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); users.add(user); } } if (cursor != null) { cursor.close(); } } } return users; } }
1、常量
private static final String DATABASE_NAME = "test.db"; private static final int VERSION_CODE = 101; public static final String TABLE = "user"; public static final String ID = "id"; public static final String NAME = "name"; public static final String AGE = "age";
分别是:数据库名(在文件系统中的文件名)
数据库版本号(版本更新时有用)
表名(一个数据库可以有多个表)
User Id(User的唯一标识,区别数据库id)
姓名、年龄不解释
2、构造函数
指定数据库名和版本号,通过单例模式获取。private DataBaseHelper(Context context) { super(context, DATABASE_NAME, null, VERSION_CODE); db = this.getWritableDatabase(); }
3、建表
就是拼接String,调用db.execSQL执行。和在命令行敲SQL语句没区别。@Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE + " (" + ID + " INTEGER NOT NULL," + NAME + " TEXT NOT NULL," + AGE + " INTEGER NOT NULL);"); }
4、更新
为什么系统要提供这个方法呢,是因为随着App的更新,数据库可能会增加新的字段,或者删除字段,导致之前的表不能再使用。@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (newVersion > oldVersion) { db.execSQL("DROP TABLE " + TABLE + " IF EXISTS"); } }
所以需要更新表结构,或者删除表,重新创建。
这里就是判断数据库版本号有更新后,就删除表,重新创建。
5、插入一条User
通过ContentValues把user的属性值保存起来(其实就是一个map),然后调用db.insert(TABLE, ID, values);插入。public boolean insertUser(User user) { boolean result = false; if(user != null) { synchronized (db) { ContentValues values = new ContentValues(); values.put(ID, user.getId()); values.put(NAME, user.getName()); values.put(AGE, user.getAge()); long insertRowId = db.insert(TABLE, ID, values); result = insertRowId != -1; } } return result; }
第二个参数是什么意思呢?
第二个参数叫做nullColumnHack,是用于当你的values为空时,指定一个列插入空值,以此来保证插入操作不会失败。
既然同样是插入空值,那和values是空有什么区别呢?这个问题要回到原始的SQL语句来理解。
Android调用系统Api实行数据库操作,其实最终都是转化成SQL语句去执行,我们可以想象一下,如果我们不添加nullColumnHack的话,那么我们的sql语句最终的结果将会类似insert into user () values();这语句显然是不允许的。而如果我们添加上nullColumnHack呢,sql将会变成这样,insert into user (nullColumnHack) values(null);哎~这样在语法上就是OK的了。
6、删除一条
这个比较简单,删除表中指定id的一条,一目了然。public boolean deleteUser(int id) { boolean result = false; if(id >= 0) { synchronized (db) { int deleteRows = db.delete(TABLE, ID + "=?", new String[]{String.valueOf(id)}); result = deleteRows > 0; } } return result; }
7、更新一条
这个也挺清晰的,就是指定一个id,找到一条记录,然后更新这条记录为ContentValues的值。public boolean updateUser(User user) { boolean result = false; if(user != null) { synchronized (db) { ContentValues values = new ContentValues(); values.put(NAME, user.getName()); values.put(AGE, user.getAge()); int updateRows = db.update(TABLE, values, ID, new String[]{String.valueOf(user.getId())}); result = updateRows > 0; } } return result; }
8、查询
代码看起来比较多,但是主要的就db.query这句。public List<User> queryUsersByAge(int minAge) { List<User> users = new ArrayList<User>(); if(minAge > 0) { synchronized (db) { Cursor cursor = db.query(TABLE, new String[]{ID, NAME, AGE}, ID + ">?", new String[]{String.valueOf(minAge)}, null, null, String.valueOf(ID)); if(cursor != null && cursor.getCount() > 0) { while(cursor.moveToNext()) { User user = new User(); user.setId(cursor.getInt(cursor.getColumnIndex(ID))); user.setName(cursor.getString(cursor.getColumnIndex(NAME))); user.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); users.add(user); } } if (cursor != null) { cursor.close(); } } } return users; }
它有7个参数,分别是:表名、查询的列数组、条件、条件参数数组(用于替换条件中的占位符?)、groupby分组、having过滤、orderby排序。
9、分页查询
对比一下查询,其实就是多了一个limit和offsetpublic List<User> queryUsersByPage(int pageNumber) { List<User> users = new ArrayList<User>(); if(pageNumber > 0) { synchronized (db) { String queryString = "select * from " + TABLE + " order by " + AGE + "limit 10 offset " + (10 * (pageNumber - 1)); Cursor cursor = db.rawQuery(queryString, null); if(cursor != null && cursor.getCount() > 0) { while(cursor.moveToNext()) { User user = new User(); user.setId(cursor.getInt(cursor.getColumnIndex(ID))); user.setName(cursor.getString(cursor.getColumnIndex(NAME))); user.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); users.add(user); } } if (cursor != null) { cursor.close(); } } } return users; }
limit是指定一组查多少个,offset是指定跳过多少条记录。
比如我们每组查询10个,查询第3组,那limit就是10 ,offset就是10 * (3 - 1)
10、批量插入
看上去和插入一条的差别就是for循环,但是有一个很重要的就是启用了事物。public boolean insertUsers(List<User> users) { boolean result = true; if(users != null && users.size() > 0) { db.beginTransaction(); for(User user : users) { boolean insert = insertUser(user); if (!insert) { result = false; break; } } db.setTransactionSuccessful(); db.endTransaction(); } return result; }
如果不用事务,每一次插入都是一次事务,耗时较大,for循环200条就就要好几秒。
但是在for循环前db.beginTransaction();
在for循环后db.setTransactionSuccessful();db.endTransaction();
这样,整个for循环就是一个事务,整体速度提升非常大。
以上就是主要的常见的数据库操作,还有一些其它的操作就不细举了。比如索引之类的。
本期节目就到这里,感谢大家的收看,下期再见!