移动架构之面向对象式数据库框架设计

在以前,操作数据库的时候,往往是通过DBHelper类去操作数据库,然而这种操作方式存在其固有缺陷,因为其是创建在DB目录下,会随着软件的卸载而卸载,那么有时有需要在本次卸载并不删除数据库,又或者是通过面向对象的思想去操作数据库,主要是对比ormlite框架

使用DBHelper

在之前,使用DBHelper会比较繁琐,例如添加一个用户,会涉及以下操作

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
public void saveUser(User name) {
DBHelper dbHelper = new DBHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.execSQL("create table if not exists tb_user(name varchar(20),password varchar(10), )");
ContentValues values = new ContentValues();
values.put("name", user.getName());
values.put("password", user.getPassword());
db.beginTransaction();
int scheduleID = -1;
try {
db.insert("schedule", null, values);
db.setTransactionSuccessful();
} finally {
}
db.endTransaction();
db.close();
}

public void updateUser(User user, String name) {
DBHelper dbHelper = new DBHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.execSQL("create table if not exists tb_user(name varchar(20),password varchar(10), )");
ContentValues values = new ContentValues();
values.put("name", user.getName());
values.put("password", user.getPassword());
db.beginTransaction();
try {
db.update("course", values, "name=?", new String[]{String.valueOf(user.getName())});
db.setTransactionSuccessful();
} finally {
}
db.endTransaction();
db.close();
}

数据库框架的实现效果

通过数据库框架的实现,我们可以更简单的操作数据库,以下就是实现其框架以后增加用户的使用例子,明显更加方便易行

1
2
3
4
5
6
public void insert() {
User user = new User();
user.setName("jack");
user.setPassword("123456");
baseDao.insert(user);
}

数据库框架的设计思路

将调用层与SQLiteDatabase进行隔离,在中间层完成相应的数据库框架设计
其简单来说就是调用层不关心数据的存入过程,只需要满足调用层将对象给与中间层,中间层完成存储的相应操作
通过映射关系使用HashMap得到数据库的key和value键值对
通过注解维护表明与数据库存值的关系

数据库框架实现

设计一个数据库操作接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface IBaseDao<T> {

//插入数据
long insert(T entity);

//更新数据
int update(T entity, T where);

//删除数据
int detele(T where);

//查询数据
List<T> query(T where);
List<T> query(T where, String orderBy, Integer startIndex, Integer limit);
}

添加注解类

1
2
3
4
5
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
String value();
}

1
2
3
4
5
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {
String value();
}

实现接口声明的方法,通过反射和注解得到对象的值,然后进行相应操作

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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
public abstract class BaseDao<T> implements IBaseDao<T> {

private static final String TAG = "BaseDao";
private SQLiteDatabase database;//持有数据库操作类的引用
private boolean isInit = false;//保证只实例化一次
private Class<T> entityClass;//持有数据库表所对应的java类型
private String tableName;
private HashMap<String, Field> cacheMap;//维护表名与成员变量的映射关系

//实例化
protected boolean init(Class<T> entity, SQLiteDatabase sqLiteDatabase) {
if (!isInit) {
database = sqLiteDatabase;
entityClass = entity;
cacheMap = new HashMap<>();
//拿到注解中的表名
if (entity.getAnnotation(DbTable.class) != null) {
tableName = entity.getAnnotation(DbTable.class).value();
} else {
tableName = entity.getClass().getSimpleName();
}
if (!database.isOpen()) {
return false;
}
if (!TextUtils.isEmpty(createTable())) {
database.execSQL(createTable());
}
initCacheMap();
isInit = true;
}
return isInit;
}

//创建表,在子类中完成
protected abstract String createTable();

//维护映射关系
private void initCacheMap() {
String sql = "select * from " + this.tableName + " limit 1 , 0";
Cursor cursor = null;
try {
cursor = database.rawQuery(sql, null);
String[] columnNames = cursor.getColumnNames();//表的列名数组
Field[] columnFields = entityClass.getFields();//拿到Filed数组
for (Field field : columnFields) {
field.setAccessible(true);
}
//查找对应关系
for (String columnName : columnNames) {
Field columnField = null;
for (Field field : columnFields) {
String fieldName = null;
if (field.getAnnotation(DbField.class) != null) {
fieldName = field.getAnnotation(DbField.class).value();
} else {
fieldName = field.getName();
}
if (columnName.equals(fieldName)) {
columnField = field;
break;
}
}
//找到对应关系,存入关系表
if (columnField != null) {
cacheMap.put(columnName, columnField);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
}

}

@Override
public long insert(T entity) {
Map<String, String> map = getValues(entity);
ContentValues values = getContentValues(map);
long result = database.insert(tableName, null, values);
Log.d(TAG, "insert: result = " + result);
return result;
}

@Override
public int update(T entity, T where) {
int result = -1;
Map values = getValues(entity);
ContentValues contentValues = getContentValues(values);
Map whereClause = getValues(where);
Condition condition = new Condition(whereClause);
result = database.update(tableName, contentValues, condition.getWhereClause(), condition.getWhereArgs());
return result;
}

@Override
public int detele(T where) {
int result = -1;
Map values = getValues(where);
Condition condition = new Condition(values);
result = database.delete(tableName, condition.getWhereClause(), condition.getWhereArgs());
return result;
}

@Override
public List<T> query(T where) {
return query(where, null, null, null);
}

@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
Map values = getValues(where);
String limitString = null;
if (startIndex != null && limit != null) {
limitString = startIndex + " , " + limit;
}
Condition condition = new Condition(values);
Cursor cursor = database.query(tableName, null, condition.getWhereClause(),
condition.getWhereArgs(), null, null, orderBy, limitString);
List<T> result = getResult(cursor, where);
return result;
}

//通过Map构建ContentValues
private ContentValues getContentValues(Map<String, String> map) {
ContentValues contentValues = new ContentValues();
Set keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = map.get(key);
if (value != null) {
contentValues.put(key, value);
}
}
return contentValues;
}

//获取value值,存入Map
private Map<String, String> getValues(T entity) {
HashMap<String, String> result = new HashMap<>();
Iterator<Field> filedsIterator = cacheMap.values().iterator();
//循环遍历映射map的Filed
while (filedsIterator.hasNext()) {
Field colmunToField = filedsIterator.next();
String cacheKey = null;
String cacheValue = null;
if (colmunToField.getAnnotation(DbField.class) != null) {
cacheKey = colmunToField.getAnnotation(DbField.class).value();
} else {
cacheKey = colmunToField.getName();
}
try {
if (colmunToField.get(entity) == null) {
continue;
}
cacheValue = colmunToField.get(entity).toString();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
result.put(cacheKey, cacheValue);
}
return result;
}

//构建查询条件
class Condition {
//查询条件
private String whereClause;
private String[] whereArgs;

public Condition(Map<String, String> whereClause) {
ArrayList arrayList = new ArrayList();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(" 1=1 ");
Set keys = whereClause.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = whereClause.get(key);
if (value != null) {
//拼接条件查询语句 1=1 and name=? and password=?
stringBuilder.append(" and " + key + " =?");
arrayList.add(value);
}
}
this.whereClause = stringBuilder.toString();
this.whereArgs = (String[]) arrayList.toArray(new String[arrayList.size()]);
}

public String getWhereClause() {
return whereClause;
}

public String[] getWhereArgs() {
return whereArgs;
}
}

//获取查询结果
private List<T> getResult(Cursor cursor, T where) {
List list = new ArrayList();
Object item;
while (cursor.moveToNext()) {
try {
item = where.getClass().newInstance();
Iterator iterator = cacheMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
String columnName = (String) entry.getKey(); //得到列名
Integer columnIndex = cursor.getColumnIndex(columnName); //拿到位置
Field field = (Field) entry.getValue();
Class type = field.getType();
if (columnIndex != -1) {
if (type == String.class) {
//反射方式赋值
field.set(item, cursor.getString(columnIndex));
} else if (type == Integer.class) {
field.set(item, cursor.getInt(columnIndex));
} else if (type == Double.class) {
field.set(item, cursor.getDouble(columnIndex));
} else if (type == Long.class) {
field.set(item, cursor.getLong(columnIndex));
} else if (type == byte[].class) {
field.set(item, cursor.getBlob(columnIndex));
} else {
continue;
}
}

}
list.add(item);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return list;
}
}

添加工厂模式

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
public class BaseDaoFactory {
private String sqliteDatabasePath;
private SQLiteDatabase sqLiteDatabase;
private static BaseDaoFactory instance = new BaseDaoFactory();

public static BaseDaoFactory getInstance(){
return instance;
}

public BaseDaoFactory() {
sqliteDatabasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "user.db";
openDatabase();
}

public synchronized <T extends BaseDao<M>, M> T getDataHelper(Class<T> clazz, Class<M> entityClass) {
BaseDao baseDao = null;
//反射得到对象类型
try {
baseDao = clazz.newInstance();
baseDao.init(entityClass, sqLiteDatabase);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (T) baseDao;
}

private void openDatabase() {
//打开数据库,如果不存在则创建
this.sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath,null);
}
}

编写测试类

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
@DbTable("tb_user")
public class User {

@DbField("name")
public String name;

@DbField("password")
public String password;

public User() {
}

public User(String name, String password) {
this.name = name;
this.password = password;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "name: " + name + ",password: " + password;
}
}

测试类的数据操作,继承BaseDao,完成数据库的创建

1
2
3
4
5
6
public class UserDao extends BaseDao{
@Override
protected String createTable() {
return "create table if not exists tb_user(name varchar(20),password varchar(10))";
}
}

在主活动中测试

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
public class MainActivity extends AppCompatActivity {

private static final String TAG = "cj5785";
IBaseDao<User> baseDao;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
baseDao = BaseDaoFactory.getInstance().getDataHelper(UserDao.class, User.class);
}

public void onSave(View view) {
User user = new User("jack", "123456");
baseDao.insert(user);
}

public void onUpdate(View view) {
User where = new User();
where.setName("jack");
User user = new User("json", "654321");
baseDao.update(user, where);
}

public void onDelete(View view) {
User user = new User();
user.setName("json");
baseDao.detele(user);
}

public void onQuery(View view) {
User user = new User();
user.setName("jack");
List<User> list = baseDao.query(user);
Log.d(TAG, "查询到数据条目:" + list.size());
for (User u : list) {
Log.d(TAG, u.toString());
}
}
}

测试结果
添加五条”jack”数据

更新”jack”数据为”json”

再添加五条”jack”数据

查询”jack”数据

删除”json”数据

对比OrimLite框架

OrimLite框架插入1000条数据,在我测试机上,测试十次,平均用时:5110ms
自定义框架插入1000条数据十次平均用时:6430ms

Donate comment here