背景
现在第三方ORM框架很多,包括greenDao,ORMLite等,以前用来处理增删查改甚是方便。但实际上对于储存要求不高的应用,使用开源框架会增加包体且不尽其能,同时也需要学习成本。如果自己能了解存储原理及设计整个模块,除了能提升模块的效率外,也能打实自己的基础并扩展应用到以后的应用开发。
思路
逻辑图
上图是整个存储模块的主要逻辑,分为逻辑层和业务层。
抽象层类
- AbstractDatabaseHelper,数据库操作类,包括增删查改及升级
- AbstractDatabaseProvider,暴露数据类,底层借助sqlite实现增删查改
- UpgradeAction,数据库升级类,负责新旧版本的升级操作
实现层类
- DatabaseHelper,继承AbstractDatabaseHelper,定义版本及数据库文件,实现创表及升级相关
- DatabaseProvider,继承AbstractDatabaseProvider,定义AUTHORITY返回helper
- UpgradeAction1ToX,继承UpgradeAction,于版本一一对应,实现不同版本的增量升级
- XTable,定义具体的业务表
- XResolver,处理数据类
实现
抽现层实现
编写AbstractDatabaseHelper类,onCreate方法中调用抽象方法handleCreateSQL和handleOther,子类根据业务自主决定创建哪些表及初始化操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41public abstract class AbstractDatabaseHelper extends SQLiteOpenHelper {
protected Context mContext;
public AbstractDatabaseHelper(Context context, String databaseName, int databaseVersion) {
super(context, databaseName, null, databaseVersion);
mContext = context;
getWritableDatabase();
}
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.beginTransaction();
try {
handleCreateSQL(sqLiteDatabase);
sqLiteDatabase.setTransactionSuccessful();
} catch (SQLException e) {
e.printStackTrace();
} finally {
sqLiteDatabase.endTransaction();
}
handleOther(sqLiteDatabase);
}
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
if(oldVersion < getDbVersionBegin() || oldVersion > newVersion || newVersion > getDbVersionLatest()){
return ;
}
List<UpgradeAction> upgradeActionList = getUpgradeActionList();
int startIndex = oldVersion - 1;
int endIndex = newVersion - 1;
for (int i = startIndex; i < endIndex; i++) {
UpgradeAction upgradeAction = upgradeActionList.get(i);
if (!upgradeAction.onUpgradeDB(sqLiteDatabase)) {
break;
}
}
}
protected abstract void handleCreateSQL(final SQLiteDatabase db);
protected abstract void handleOther(SQLiteDatabase db);
protected abstract int getDbVersionBegin();
protected abstract int getDbVersionLatest();
protected abstract List<UpgradeAction> getUpgradeActionList();
}编写AbstractDatabaseProvider类,实际上是借用AbstractDatabaseHelpter之手实现增删查改,并保证操作的原子性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163public abstract class AbstractDatabaseProvider extends ContentProvider {
//数据库访问助手类
private AbstractDatabaseHelper mOpenHelper;
public boolean onCreate() {
mOpenHelper = newDatabaseHelper(getContext());
return true;
}
public String getType(Uri uri) {
return null;
}
public Uri insert(Uri uri, ContentValues contentValues) {
SqlArguments args = new SqlArguments(uri);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final long rowId = db.insert(args.table, null, contentValues);
if (rowId <= 0) {
return null;
}
uri = ContentUris.withAppendedId(uri, rowId);
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
public int delete(Uri uri, String s, String[] strings) {
SqlArguments args = new SqlArguments(uri, s, strings);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int deletedCount = db.delete(args.table, args.where, args.args);
if (deletedCount > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return deletedCount;
}
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
SqlArguments args = new SqlArguments(uri, s, strings1);
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(args.table);
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor result = qb.query(db, strings, args.where, args.args, null, null, s1);
result.setNotificationUri(getContext().getContentResolver(), uri);
return result;
}
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
SqlArguments args = new SqlArguments(uri, s, strings);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
if (null == contentValues) {
//执行多条sql语句
String[] sqlArray = args.where.split("##");
if (null == sqlArray || sqlArray.length <= 0) {
return 0;
}
int ret = 0;
try {
db.beginTransaction();
int len = sqlArray.length;
for (int i = 0; i < len; i++) {
db.execSQL(sqlArray[i]);
}
db.setTransactionSuccessful();
ret = 1; // 返回1表示通知成功
} finally {
db.endTransaction();
}
return ret;
} else {
return db.update(args.table, contentValues, args.where, args.args);
}
}
/**
* 保证原子性
* @param operations
* @return
* @throws OperationApplicationException
*/
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
if (null == operations) {
return null;
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int numOperations = operations.size();
ContentProviderResult[] results = new ContentProviderResult[operations.size()];
try {
db.beginTransaction();
for (int i = 0; i < numOperations; i++) {
ContentProviderOperation operation = operations.get(i);
results[i] = operation.apply(this, results, i);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return results;
}
/**
* 保证原子性
* @param uri
* @param values
* @return
*/
public int bulkInsert(Uri uri, ContentValues[] values) {
SqlArguments args = new SqlArguments(uri);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count = values.length;
try {
db.beginTransaction();
for (int i = 0; i < count; i++) {
if (db.insert(args.table, null, values[i]) < 0) {
return 0;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
/**
* 获取数据库操作对象
* @param context
* @return
*/
protected abstract AbstractDatabaseHelper newDatabaseHelper(Context context);
public static class SqlArguments {
public final String table;
public final String where;
public final String[] args;
public SqlArguments(Uri url, String where, String[] args) {
if (url.getPathSegments().size() == 1) {
this.table = url.getPathSegments().get(0);
this.where = where;
this.args = args;
} else if (url.getPathSegments().size() != 2) {
throw new IllegalArgumentException("Invalid URI: " + url);
} else if (!TextUtils.isEmpty(where)) {
throw new UnsupportedOperationException("WHERE clause not supported: " + url);
} else {
this.table = url.getPathSegments().get(0);
this.where = "_id=" + ContentUris.parseId(url);
this.args = null;
}
}
public SqlArguments(Uri url) {
if (url.getPathSegments().size() == 1) {
table = url.getPathSegments().get(0);
where = null;
args = null;
} else {
throw new IllegalArgumentException("Invalid URI: " + url);
}
}
}
}编写UpgradeAction类,暴露升级操作方法,供子类实现,同时提供了修改表结构的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68public abstract class UpgradeAction {
/**
* 子类实现 对应具体版本升级操作
* @param db
* @return
*/
public abstract boolean onUpgradeDB(SQLiteDatabase db);
/**
* 新添加字段到表中
* @param db
* @param tableName : 修改表名
* @param columnName :新增字段名
* @param columnType :新增字段类型
* @param defaultValue :新增字段默认值。为null,则不提供默认值
*/
public void addColumnToTable(SQLiteDatabase db, String tableName,
String columnName, String columnType, String defaultValue) {
if (!isExistColumnInTable(db, tableName, columnName)) {
db.beginTransaction();
try {
String updateSql = "ALTER TABLE " + tableName + " ADD "
+ columnName + " " + columnType;
db.execSQL(updateSql);
if (defaultValue != null) {
if (columnType.equals(Table.TYPE_TEXT)) {
defaultValue = "'" + defaultValue + "'";
}
updateSql = "update " + tableName + " set " + columnName
+ " = " + defaultValue;
db.execSQL(updateSql);
}
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
}
}
}
/**
* 检查表中是否存在该字段
* @param db
* @param tableName
* @param columnName
* @return
*/
private boolean isExistColumnInTable(SQLiteDatabase db, String tableName,
String columnName) {
boolean result = false;
Cursor cursor = null;
try {
String columns[] = {columnName};
cursor = db.query(tableName, columns, null, null, null, null, null);
if (cursor != null && cursor.getColumnIndex(columnName) >= 0) {
result = true;
}
} catch (Exception e) {
Log.i("DatabaseHelper", "isExistColumnInTable has exception");
e.printStackTrace();
result = false;
} finally {
if (null != cursor) {
cursor.close();
}
}
return result;
}
}
实现层实现
编写DatabaseHelper,定义应用数据库初始和当前版本号及数据库文件名,同时针对升级操作进行特定处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37public class DatabaseHelper extends AbstractDatabaseHelper {
//数据库名称
public static final String DATABASE_NAME = "databaseName.db";
//数据库开始版本号
private static final int DB_VERSION_ONE = 1;
//数据库当前版本号
private static final int DATABASE_VERSION = 1;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, DATABASE_VERSION);
}
protected int getDbVersionBegin() {
return DB_VERSION_ONE;
}
protected int getDbVersionLatest() {
return DATABASE_VERSION;
}
protected void handleCreateSQL(SQLiteDatabase db) {
if (DATABASE_VERSION == 1) {
// TODO: 2016/10/8 0008 创建第一版本的表
} else {
// TODO: 2016/9/14 0014 以后数据库扩展可用
}
}
protected void handleOther(SQLiteDatabase db) {
// TODO: 2016/10/8 0008 选择性实现,比如可能需要清除特定数据操作等
}
protected List<UpgradeAction> getUpgradeActionList() {
List<UpgradeAction> upgradeActionList = new ArrayList<UpgradeAction>();
// TODO: 2016/10/8 0008 添加特定的 UpgradeAction子类
return upgradeActionList;
}
}编写DatabaseProvider类,暴露sqlite数据,提供系统权限入口。
1
2
3
4
5
6
7
8public class DataBaseProvider extends AbstractDatabaseProvider {
//数据库访问鉴权,在androidmanifest文件中定义
public static final String AUTHORITY = "com.example.yummylau.dbdemo.DataBaseProvider";
protected AbstractDatabaseHelper newDatabaseHelper(Context context) {
return newDatabaseHelper(context);
}
}根据需要编写UpgradeActionxToy类,一般是一个版本对应一个子类进行增量升级,从x版本升级到y版本,其中应该满足y-x=1。
1
2
3
4
5
6
7
8
9
10
11/**
* 以后从1版本升级到x版本可用
* Created by yummyLau on 2016-09-14 11:15.
*/
public class UpgradeActionxToy extends UpgradeAction {
public boolean onUpgradeDB(SQLiteDatabase db) {
// TODO: 2016/9/14 0014 增添表或者修改表结构操作
return true;
}
}
总结
- 从抽象类到实现类的实现,基本完成了数据的暴露,业务中操作数据是借助ContentResolver来操作,具体业务可写对应的xxResolver <1 : n> xxTable 来操作特定的业务。1>
- 数据时保证工作线程为异步调用。