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 setAge(String name) {
		this.name = name;
	}
	
	public int getAge() {
		return age;
	}
	
	public void setAge(int age) {
		this.age = age;
	}
}

然后是数据库操作,就一个类:SqliteOpenHelper

我先把整个类的代码贴出来,再一段段注释。

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 int _ID = "_id";
	
	public static final int ID = "id";
	
	public static final String NAME = "name";
	
	public static final int 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 PRIMARY KEY AUTOINCREMENT,"
				+ ID + " LONG 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 deleteUser(User user) {
		boolean result = false;
		if(user != null) {
			synchronized (db) {
				int deleteRows = db.delete(TABLE, ID + "=", new String[]{user.getId()});
				result = deleteRows > 0;
			}
		}
		return result;
	}
	
	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[]{user.getId()});
				result = updateRows > 0;
			}
		}
		return result;
	}
	
	//查询年龄大于20岁的所有人
	public List<User> queryUser(int minAge) {
		List<User> users = new ArrayList<User>();
		if(minAge > 0) {
			synchronized (db) {
				Cursor cursor = db.query(TABLE, new String[]{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 user;
	}
	
	//分页查询:页面上显示User按年龄排序,每页显示10个,查询第n页的User
	public List<User> queryUser(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(query, 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();
				}
			}
		}
	}
	
	//批量插入
	public boolean insertUsers(List<User> users) {
		boolean result = false;
		if(users != null && users.size() > 0) {
			db.beginTransaction();
			for(User user : users) {
				insertUser(user);
			}
			db.setTransactionSuccessful();
			db.endTransaction();
		}
		return result;
	}
}

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 int _ID = "_id";
	
	public static final int ID = "id";
	
	public static final String NAME = "name";
	
	public static final int AGE = "age";

分别是:

数据库名(在文件系统中的文件名)

数据库版本号(版本更新时有用)

表名(一个数据库可以有多个表)

数据库id(有下划线_,是数据库记录的id,表的主键,自增长,区别User Id

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 PRIMARY KEY AUTOINCREMENT,"
				+ ID + " LONG 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(User user) {
		boolean result = false;
		if(user != null) {
			synchronized (db) {
				int deleteRows = db.delete(TABLE, ID + "=", new String[]{user.getId()});
				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[]{user.getId()});
				result = updateRows > 0;
			}
		}
		return result;
	}
这个也挺清晰的,就是指定一个id,找到一条记录,然后更新这条记录为ContentValues的值。


8、查询

	//查询年龄大于20岁的所有人
	public List<User> queryUser(int minAge) {
		List<User> users = new ArrayList<User>();
		if(minAge > 0) {
			synchronized (db) {
				Cursor cursor = db.query(TABLE, new String[]{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 user;
	}
代码看起来比较多,但是主要的就db.query这句。

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


9、分页查询

	//分页查询:页面上显示User按年龄排序,每页显示10个,查询第n页的User
	public List<User> queryUser(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(query, 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();
				}
			}
		}
	}
对比一下查询,其实就是多了一个limit和offset

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

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


10、批量插入

	public boolean insertUsers(List<User> users) {
		boolean result = false;
		if(users != null && users.size() > 0) {
			db.beginTransaction();
			for(User user : users) {
				insertUser(user);
			}
			db.setTransactionSuccessful();
			db.endTransaction();
		}
		return result;
	}
看上去和插入一条的差别就是for循环,但是有一个很重要的就是启用了事物。

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

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

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

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


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


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















你可能感兴趣的:(java,android,数据库,Android开发,APP)