Android数据库Sqlite完全解析

不会数据库的司机不是好厨师。今天抽了点时间对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、建表

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE " + TABLE + " ("
            + ID + " INTEGER NOT NULL,"
            + NAME + " TEXT NOT NULL,"
            + AGE + " INTEGER NOT NULL);");
}

就是拼接String,调用db.execSQL执行。和在命令行敲SQL语句没区别。


4、更新

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (newVersion > oldVersion) {
        db.execSQL("DROP TABLE " + TABLE + " IF EXISTS");
    }
}

为什么系统要提供这个方法呢,是因为随着App的更新,数据库可能会增加新的字段,或者删除字段,导致之前的表不能再使用。

所以需要更新表结构,或者删除表,重新创建。

这里就是判断数据库版本号有更新后,就删除表,重新创建。


5、插入一条User

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;
}

通过ContentValues把user的属性值保存起来(其实就是一个map),然后调用db.insert(TABLE, ID, values);插入。

第二个参数是什么意思呢?

第二个参数叫做nullColumnHack,是用于当你的values为空时,指定一个列插入空值,以此来保证插入操作不会失败。

既然同样是插入空值,那和values是空有什么区别呢?这个问题要回到原始的SQL语句来理解。

Android调用系统Api实行数据库操作,其实最终都是转化成SQL语句去执行,我们可以想象一下,如果我们不添加nullColumnHack的话,那么我们的sql语句最终的结果将会类似insert into user () values();这语句显然是不允许的。而如果我们添加上nullColumnHack呢,sql将会变成这样,insert into user (nullColumnHack) values(null);哎~这样在语法上就是OK的了。


6、删除一条

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;
}

这个比较简单,删除表中指定id的一条,一目了然。


7、更新一条

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;
}

这个也挺清晰的,就是指定一个id,找到一条记录,然后更新这条记录为ContentValues的值。


8、查询

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;
}

代码看起来比较多,但是主要的就db.query这句。

它有7个参数,分别是:表名、查询的列数组、条件、条件参数数组(用于替换条件中的占位符?)、groupby分组、having过滤、orderby排序。


9、分页查询

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;
}

对比一下查询,其实就是多了一个limit和offset

limit是指定一组查多少个,offset是指定跳过多少条记录。

比如我们每组查询10个,查询第3组,那limit就是10 ,offset就是10 * (3 - 1)


10、批量插入

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循环,但是有一个很重要的就是启用了事物。

如果不用事务,每一次插入都是一次事务,耗时较大,for循环200条就就要好几秒。

但是在for循环前db.beginTransaction();

在for循环后db.setTransactionSuccessful();db.endTransaction();

这样,整个for循环就是一个事务,整体速度提升非常大。


以上就是主要的常见的数据库操作,还有一些其它的操作就不细举了。比如索引之类的。


本期节目就到这里,感谢大家的收看,下期再见!















发布了40 篇原创文章 · 获赞 88 · 访问量 9万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章