《Android本地存储SQLite优化》
一.简介
SQLite是一款开源的、嵌入式关系型数据库,第一个版本Alpha发布于2000年。SQLite在便携性、易用性、紧凑性、高效性和可靠性方面有着突出的表现。
SQLite和C/S模式的数据库软件不同,它是一款嵌入式数据库,没有独立运行的进程,与所服务的应用程序在应用程序进程空间内共生共存。它的代码与应用程序代码也是在一起的,或者说嵌入其中,作为托管它的程序的一部分。因此不存在数据库的客户端和服务器,使用SQLite一般只需要带上它的一个动态库,就可以享受它的全部功能。
数据库服务器在程序中的好处是不需要网络配置或管理。将数据库客户端与服务器运行在同一个进程中,可以省去不少的操作及麻烦:不用担心防火墙或者地址解析;不用浪费时间管理复杂的授权和权限;可以减少网络调用相关的消耗;可以简化数据库管理并使程序更容易部署。
SQLite数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但是只有一个可以写入数据。在某个进程向数据库执行写操作之前,必须获得独占锁定。在发出独占锁定后,其他的读写操作将不会再发生。
此外,SQLite数据库中的所有信息(比如表、视图、触发器等)都包含在一个文件内,方便管理和维护。SQLite数据库还支持大部分操作系统,除电脑上使用的操作系统之外,很多手机上使用的操作系统同样可以运行。同时,SQLite数据库还提供了多语言的编程接口,供开发者使用。
二.SQLite的相关类
- SQLiteOpenHelper:抽象类,通过继承该类,重写数据库创建以及更新的方法, 我们还可以通过该类的对象获得数据库实例,或者关闭数据库
- SQLiteDatabase:数据库访问类:通过该类的对象来对数据库做一些增删改查的操作
- Cursor:游标,类似于JDBC里的resultset(结果集),可以简单理解为指向数据库中某一个记录的指针
三.优化格式
1.正常的数据库创建流程
首先基本上在进行android的SQLite的时候,基本上每次进行表格修改的时候都会进行一次数据库table的创建。
一般都是继承SQLiteOpenHelper 来实现数据库的表的创建,但是如果数据库的表每次都需要修改的话,每次都进行修改是否太麻烦了,而且如果你的项目中的依赖module太多,或者这个他是在maven当中,再找到他是不是很麻烦。
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* Created by 先森 on 2017/9/21.
*/
public static final String CREATE_BOOK = "CREATE TABLE book ("
+ "id integer PRIMARY KEY Autoincrement ,"
+ "author text ,"
+ "price real ,"
+ "pages integer,"
+ "name text )";
/**
* integer:整形
* real:浮点型
* text:文本类型
* blob:二进制类型
* PRIMARY KEY将id列设置为主键
* AutoIncrement关键字表示id列是自动增长的
*/
public class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context, String name, int version) {
super(context, name, null, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_BOOK);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
2.配置一个ddl.sql数据库表和关键字的文件
数据库表创建的资源文件位置所以我觉得不如就把他这个数据库表创建的sql语句放在一个资源文件当中相当于是一个配置文件了,这样配置字段什么的不仅看起来十分方便,而且十分方便去修改它。其次我们只要进行文件的读取,再转换成String格式不就可以了么
下面把数据库创建的表sql语句如下,创建一个table1,版本号version :1
# ddl版本号
version=1
# 存储配置表
# 如果存在则删除表
drop table if exists table1;
create table table1(
id varchar(200) not null, # 存储的key
json text, # 存储的内容
primary key(id)
)
3.对文件ddl读取
1.读取输入流把它变成String格式的文件
2.获取版本号
3.通过split来把Stringbuilder格式SQL语句变成String[]格式
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Created by 张都 on 2017/9/20.
*/
public class DDLReader {
private BufferedReader bufferedReader;
private StringBuilder allsql;
private int version;
public DDLReader(InputStream in) {
this.bufferedReader = new BufferedReader(new InputStreamReader(in));
allsql=new StringBuilder();
version=0;
}
public void read(){
try {
while(bufferedReader.ready()){
String line=bufferedReader.readLine();
if(line==null){
break;
}
lineRead(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//数据库的每条读取
private void lineRead(String line) {
if(line.isEmpty())return;
line=line.replaceAll("\\/\\*[\\S\\s]*?\\*\\/","").replaceAll("\\/\\/[\\S\\s]*$","").replaceAll("\\#[\\S\\s]*$","").trim();
if(line.isEmpty())return;
if(line.startsWith("version")){
String[] versions=line.split("=");
if(versions.length<2){
throw new RuntimeException("ddl文件version设置错误 必须version=版本号为单独一行");
}
try {
this.version=Integer.valueOf(versions[1].trim());
}catch (Exception e){
throw new RuntimeException("ddl文件version设置错误 version必须为正整数");
}
return;
}
allsql.append(line);
}
//获取当前数据库版本
public int getVersion(){
return version;
}
/**
* 返回所有创建数据库的sql语句
* @return
*/
public String[] getAllddlsSql(){
String[] ddl=allsql.toString().split(";");
return ddl;
}
}
定义一个接口对返回的数据的格式和种类进行
public interface IDao {
/**
* 查询
* @param sql 查询语句
* @return
*/
List<HashMap<String,Object>> query(String sql);
/**
* 查询返回第几页的个数
* @param sql 查询语句
* @return
*/
List<HashMap<String,Object>> query(String sql,int page,int pageSize);
/**
* 插入和更新操作
* @param sql 查询语句
* @return
*/
int update(String sql);
/**
*获取本地数据库的数据个数
* @param sql 查询语句
* @return
*/
int getCount(String sql);
}
实现接口进行具体方法的实现
public class Dao implements IDao {
//数据库版本
private int version;
//创建数据库的sql
private String[] ddls;
//获取数据库
private SQLiteDatabase db;
public Dao(Context context,String name) {
getDDLs(context);
this.db=new DataBaseHelper(context, name,version,ddls).getWritableDatabase();
}
private void getDDLs(Context context){
try{
InputStream in=context.getResources().openRawResource(R.raw.ddl);
DDLReader ddlReader=new DDLReader(in);
ddlReader.read();
version=ddlReader.getVersion();
ddls=ddlReader.getAllddlsSql();
}catch (Exception e){
throw new RuntimeException("ddl的version或者ddls没找到或者文件ddl没找到");
}
}
@Override
public List<HashMap<String, Object>> query(String sql) {
if(db==null){
return null;
}else{
Cursor cursor = db.rawQuery(sql, null);
try {
List<HashMap<String, Object>> list = new ArrayList();
while (cursor.moveToNext()) {
HashMap<String, Object> map = new HashMap<String, Object>();
for (int i = 0; i < cursor.getColumnCount(); i++) {
Log.i(cursor.getColumnName(i), cursor.getString(i) == null ? "null" : cursor.getString(i));
map.put(cursor.getColumnName(i).toLowerCase(), cursor.getString(i));
}
list.add(map);
}
return list;
}finally {
cursor.close();
}
}
}
@Override
public List<HashMap<String, Object>> query(String sql, int page, int pageSize) {
int offset=(page-1)*pageSize;
sql="select * from ("+sql+") a limit "+pageSize+" offset "+offset;
return query(sql);
}
@Override
public int update(String sql) {
if(db==null){
return -1;
}else{
try {
db.execSQL(sql);
return 1;
}catch (Exception e){
Log.e("appsql",sql);
e.printStackTrace();
return 0;
}
}
}
@Override
public int getCount(String sql) {
sql="select count(*) from ("+sql+") a";
Cursor cursor = db.rawQuery(sql, null);
try {
cursor.moveToFirst();
return cursor.getInt(0);
}finally {
cursor.close();
}
}
}
最后一步进行相关的输入参数的格式进行规定,提供一个公开的类CacheProvider
转换成json格式的话就需要进行json的依赖
在项目中添加如下fastjso的依赖
compile 'com.alibaba:fastjson:1.1.52.android'
public class CacheProvider {
private static IDao dao;
public static void initDao(Context context, String name){
dao=new Dao(context,name);
}
//插入数据和更新数据
@Nullable
public static void setString(String key,String data){
int size = dao.getCount("select * from storage where id='" + key + "'");
String value=String.valueOf(data).replaceAll("'","\\'");
if (size == 0) {
dao.update("insert into storage values('" + key + "','" + value + "')");
} else {
int s = dao.update("update storage set json='" + value + "' where id='" + key + "'");
}
}
@Nullable
public static void setObject(String key,Object data){
setString(key, JSON.toJSONString(data));
}
@Nullable
public static void setDouble(String key,double v){
setObject(key,v);
}
@Nullable
public static void setObjects(String key,List<? extends Object> list){
setString(key, JSON.toJSONString(list));
}
@Nullable
public static String getString(String key){
List<HashMap<String, Object>> list = dao.query("select * from storage where id='" + key + "'");
if (list.isEmpty()) {
return null;
} else {
return list.get(0).get("json").toString();
}
}
/**
* 获取存储的boolean值
* @param key
* @return
*/
@Nullable
public static boolean getBoolean(String key){
return Boolean.valueOf(getString(key));
}
@Nullable
public static int getInteger(String key){
try {
return Integer.valueOf(getString(key));
}catch (Exception e){
return 0;
}
}
@Nullable
public static double getDouble(String key){
try {
return Double.valueOf(getString(key));
}catch (Exception e){
return -1;
}
}
@Nullable
public static <T>T getObject(String key, Class<T> clazz){
String json=getString(key);
if(json==null){
return null;
}
return JSON.parseObject(json,clazz);
}
@NonNull
public static <T>List<T> getObjects(String key, Class<T> clazz){
String json=getString(key);
if(json==null){
return new ArrayList<>();
}
return JSON.parseArray(json,clazz);
}
}