Android架構設計01-資料庫框架
1 使用場景
涉及到資料庫的操作,一般的都是使用第三方的框架,如GreeoDao,OrmLitem等。或者是直接利用SQLiteOpenHelper來完成資料庫的增刪改查以及資料庫升級。那麼對於這種寫法,一般都比較繁瑣。操作如下:
DBHelper dbHelper = new DBHelper(this);
SQLiteDatabase sqLiteDatabase = dbHelper.getWritableDatabase();
sqLiteDatabase.execSQL("create table if not exists tb_weichat(name varchar(20),password varchar(10))" );
ContentValues contentValues = new ContentValues();
contentValues.put("user_name","張三");
contentValues.put("password","123456");
sqLiteDatabase.beginTransaction();
int scheduleID = -1;
dbHelper.insert("schedule",null,values);
dbHelper.setTransctionSuccessful();
使用框架實現
User user = new User("張三" , "20122425");
baseDao.insert(user);
可以看出使用框架和之前相比省了很多行程式碼。操作更簡單。
2 用到的知識點
泛型
註解
反射
資料庫語句拼接
用到的設計的模式
單例模式
簡單工廠模式
模板方法模式
3 類圖
4 具體實現
IBaseDao
public interface IBaseDao<T> {
/**
* 資料庫插入操作
* @param entity
* @return
*/
Long insert(T entity);
/**
* 資料庫刪除
* @param where
* @return
*/
int delete(T where);
/**
* 資料庫更改操作
* @param entity
* @param where
* @return
*/
int updata(T entity,T where);
/**
* 資料庫查詢操作
* @param where
* @return
*/
List<T> query(T where);
}
這個藉口約束了資料庫行為,資料庫中有增刪改查功能。
BaseDao
public abstract class BaseDao<T> implements IBaseDao<T> {
//只能初始化一次
private boolean isInit = false;
//被插入資料庫的實體
private Class<T> mEntityClass;
private SQLiteDatabase mSqLiteDatabase;
//表名
private String tableName;
/**
* 用來維護表與實體的對應關係
* key: 表的列名
* value: entity的Field名
*/
private HashMap<String, Field> cacheMap;
/**
* @param entityClass 要出入資料庫的實體類
* @param sqLiteDatabase 資料庫管理的物件
*/
public synchronized boolean init(Class<T> entityClass, SQLiteDatabase sqLiteDatabase) {
if (!isInit) {
mEntityClass = entityClass;
mSqLiteDatabase = sqLiteDatabase;
if (mEntityClass.getAnnotation(DbTable.class) != null) {
//註解DbTable的值為表名
tableName = entityClass.getAnnotation(DbTable.class).value();
} else {
//如果沒有註解,對應的類名為表名
tableName = entityClass.getSimpleName();
}
//建立表
if (!TextUtils.isEmpty(createTable())) {
sqLiteDatabase.execSQL(createTable());
}
cacheMap = new HashMap<>();
initCacheMap();
isInit = true;
}
return isInit;
}
/**
* 初始化cacheMap,
* 將表的列明和對應的entity的成員變數名字賦值給該cacheMap
*/
private void initCacheMap() {
String sql = "select * from " + this.tableName + " limit 1,0";
Cursor cursor = null;
try {
cursor = mSqLiteDatabase.rawQuery(sql, null);
//表的列名陣列
String[] columnsName = cursor.getColumnNames();
//entitiy的Field陣列
Field[] fields = mEntityClass.getFields();
//防止私有屬性無法訪問
for (Field field : fields) {
field.setAccessible(true);
}
//遍歷列名
for (String columnName : columnsName) {
//遍歷成員變數
for (Field field : fields) {
String fieldName = null;
if (field.getAnnotation(DbField.class) != null) {
fieldName = field.getAnnotation(DbField.class).value();
} else {
fieldName = field.getName();
}
if (field != null && columnName.equals(fieldName)) {
cacheMap.put(columnName, field);
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != cursor) {
cursor.close();
}
}
}
//=============================================插入
@Override
public Long insert(T entity) {
Map<String, String> map = getValue(entity);
ContentValues contentValues = getContentValues(map);
Long reslt = mSqLiteDatabase.insert(tableName, null, contentValues);
return reslt;
}
//=============================================刪除
@Override
public int delete(T where) {
int result = -1;
Map whereClause = getValue(where);
Condition condition = new Condition(whereClause);
result = mSqLiteDatabase.delete(tableName, condition.getWhereClause(), condition.getWhereArgs());
return result;
}
//=============================================更新
@Override
public int updata(T entity, T where) {
int result = -1;
Map whereClause = getValue(where);
Map values = getValue(entity);
Condition condition = new Condition(whereClause);
ContentValues contentValues = getContentValues(values);
result = mSqLiteDatabase.update(tableName, contentValues, condition.getWhereClause(), condition.getWhereArgs());
return result;
}
//=============================================更新
@Override
public List<T> query(T where) {
return query(where, null, null, null);
}
private List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
Map map = getValue(where);
String limitString = null;
if (startIndex != null && limit != null) {
limitString = startIndex + " , " + limit;
}
Condition condition = new Condition(map);
Cursor cursor = mSqLiteDatabase.query(tableName, null, condition.getWhereClause(), condition.getWhereArgs(), null, null, orderBy, limitString);
List<T> result = getResult(where, cursor);
cursor.close();
return result;
}
/**
* 將查詢的結果返回
* @param where 表中每條資料對應的實體類
* @param cursor 查詢結果返回的cursor物件
* @return 返回所有的查詢結果
*/
protected List<T> getResult(T where, Cursor cursor) {
ArrayList 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();
//獲取Field的型別
Class type = field.getType();
//判斷Field的型別
if (columnIndex != -1) {
if (type == String.class) {
//根據反射進行賦值
field.set(item, cursor.getString(columnIndex));
} else if (type == Double.class) {
field.set(item, cursor.getDouble(columnIndex));
} else if (type == Integer.class) {
field.set(item, cursor.getInt(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;
}
;
/**
* 把map轉成contentValues
* key:列名 value:Field值
*
* @param map
* @return
*/
private ContentValues getContentValues(Map<String, String> map) {
ContentValues contentValues = new ContentValues();
Set keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String columnName = iterator.next();
String value = map.get(columnName);
if (!TextUtils.isEmpty(columnName) && !TextUtils.isEmpty(value)) {
contentValues.put(columnName, value);
}
}
return contentValues;
}
;
/**
* 用map 來儲存entity的屬性值 key為對應屬性的註解值
* key:"user_name" vlaue:"張三"
*
* @param entity
* @return
*/
private Map<String, String> getValue(T entity) {
Map<String, String> map = new HashMap<>();
Iterator<Field> fieldIterator = cacheMap.values().iterator();
while (fieldIterator.hasNext()) {
Field field = fieldIterator.next();
String key = null; //列名
String value = null; //field值
if (field.getAnnotation(DbField.class) != null) {
key = field.getAnnotation(DbField.class).value();
} else {
key = field.getName();
}
try {
if (field.get(entity) == null) {
continue;
}
value = field.get(entity).toString();
if (value != null) {
map.put(key, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
class Condition {
/**
* 條件語句
* name=?&&password=?
*/
private String whereClause;
private String[] whereArgs;
public Condition(Map<String, String> whereClause) {
ArrayList list = new ArrayList();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(" 1=1 ");
Set keySet = whereClause.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = whereClause.get(key);
if (key != null) {
//拼接條件語句 1=1 and name =? and password =?
stringBuilder.append(" and " + key + " =?");
list.add(value);
}
}
this.whereClause = stringBuilder.toString();
this.whereArgs = (String[]) list.toArray(new String[list.size()]);
}
public String getWhereClause() {
return whereClause;
}
public String[] getWhereArgs() {
return whereArgs;
}
}
/**
* 每個Dao的建的表各不相同,所以此方法留給子類實現
*
* @return 建表的字串語句
*/
public abstract String createTable();
}
這個類也是這個框架的核心內容。首先看這個方法
init(Class entityClass, SQLiteDatabase sqLiteDatabase)
這個方法做了這幾件事情:
初始化每一條資料對應的實體類。
mEntityClass = entityClass;
初始化SQLiteDataBase物件
mSqLiteDatabase = sqLiteDatabase;
初始化表名
if (mEntityClass.getAnnotation(DbTable.class) != null) {
//註解DbTable的值為表名
tableName = entityClass.getAnnotation(DbTable.class).value();
} else {
//如果沒有註解,對應的類名為表名
tableName = entityClass.getSimpleName();
}
建立表 這裡的createTalbe方法是抽象方法,也是BaseDao子類唯一需要重寫的方法。也就是說BaseDao的子類除了建表語句不同之外,其他的方法都是一樣的。
if (!TextUtils.isEmpty(createTable())) {
sqLiteDatabase.execSQL(createTable());
}
初始化cacheMap
cacheMap = new HashMap<>();
initCacheMap();
cacheMap用來維護表的資料和實體類的對應關係。
key:表的列名。如user_name
value: entity的Field。如User類中的String name的name變數。
initCacheMap()
這個方法是用來給cacheMap賦值的。
首先用cursor來獲取一個表的列名陣列
cursor = mSqLiteDatabase.rawQuery(sql, null);
String[] columnsName = cursor.getColumnNames();
獲取entity中的成員變數陣列
Field[] fields = mEntityClass.getFields();
在把根據列名找到對應的實體類中的成員變數
for (String columnName : columnsName) {
//遍歷成員變數
for (Field field : fields) {
String fieldName = null;
if (field.getAnnotation(DbField.class) != null) {
fieldName = field.getAnnotation(DbField.class).value();
} else {
fieldName = field.getName();
}
if (field != null && columnName.equals(fieldName)) {
//沒找到一個對應 就新增到cacheMap中
cacheMap.put(columnName, field);
break;
}
}
}
getValue(T entity)
這個方法用來把一個實體類的中所有的成員變數封裝到一個map中。
map的key:entity的Field註解名,也就是列名;
map的value:entity的Field的value;
用cacheMap去迭代表的所有列的資料。當列名對應的Field的value不是null的時候,將此列名和value作為一個entry新增到map中。
getContentValues(Map< String, String> map)
這個方法用來把map(key:列名,value:Field的值)轉化為contentValue。
內部類Condition
這個內部類是用來拼接條件語句。當在構造方法中傳入一個entity後,把這個entity的Field的名作為拼接條件的查詢語句,Field的value作為條件語句的值。它有兩個成員變數
whereClause:用來拼接條件語句。如:and name =?
whereArgs:條件語句的值的陣列。如:”張三“
getResult(T where, Cursor cursor)
這個方法是用來根據需要
查詢的條件
和表中的所有資料 來返回所有符合條件的物件集合。
UserDao
具體的資料庫訪問類。
public class UserDao extends BaseDao {
@Override
public String createTable() {
return "create table if not exists tb_weichat(name varchar(20),password varchar(10))";
}
}
這個類只是重寫了建表語句的方法。也就是每當需要建一張表就要寫一個此類。
註解:DbField
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {
String value();
}
修飾成員變數。value對應表的列名。
註解:DbTable
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
String value();
}
修飾類。對應表名。
User
@DbTable("tb_weichat")
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 void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
}
這個類是表的每一條資料對應的實體類。也就是說每個User對應一個UserDao。每個UserDao對應一張表。而每個User對應一條表的資料。
BaseDaoFactory
public class BaseDaoFactory {
//單例
private static BaseDaoFactory instance = new BaseDaoFactory();
//資料庫的儲存路徑
private String sqliteDataPath;
private SQLiteDatabase sqLiteDatabase;
private BaseDaoFactory() {
//隨便起了個名字,比如叫微信的資料庫
sqliteDataPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/weichat.db";
openSqliteDataBase();
}
public static BaseDaoFactory getInstance() {
return instance;
}
private void openSqliteDataBase() {
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDataPath,null);
}
/**
* 構建一個BaseDao的子類
* @param entity 插入資料庫的實體類
* @param dao BaseDao的子類
* @param <T>
* @param <M>
* @return
*/
public synchronized <T,M extends BaseDao<T>> BaseDao<T> constructDao(Class<T> entity,Class<M> dao){
BaseDao baseDao = null;
try {
baseDao = dao.newInstance();
baseDao.init(entity,sqLiteDatabase);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return baseDao;
}
}
這個類用來構建一個具體的BaseDao物件。這個類是單例的。全域性只有一個此類的物件。構建了BaseDao物件並隱藏了具體的實現細節。
客戶端類MainActivity
public class MainActivity extends AppCompatActivity {
private IBaseDao baseDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
baseDao = BaseDaoFactory.getInstance().constructDao(User.class, UserDao.class);
}
public void insert(View view) {
User user = new User("張三", "20122425");
baseDao.insert(user);
}
public void delete(View view){
User where = new User();
where.setName("李四");
baseDao.delete(where
);
}
public void update(View view){
User user = new User("李四","1352284");
User where = new User();
where.setName("張三");
baseDao.updata(user,where);
}
public void query(View view){
User user = new User();
user.setName("李四");
List<User> list = baseDao.query(user);
Log.i("Main","查詢到"+list.size()+"條資料");
}
}