Android 架構之數據庫框架搭建
前言:
你還在苦惱的寫SQL麼?你還在為數據庫升級而煩惱麼?你還在因查詢數據而寫繁瑣不可用的代碼麼? 在這,這些都將不復存在!在本篇中,將會讓你一點一滴從無到有創建一個不再為數據庫而煩惱的框架。
在開始之前我們先欣賞一下本章實現的最終效果 效果展示
如圖所示:
- 對應的
model
,可直接成為表結構,不再寫對應的Create table xxx
對應的SQL瞭 - 對應
model
的Dao層,裡面封裝瞭數據表的基本操作(增刪改查) - 對應的增刪改查操作,再也不用SQL瞭,全用對象處理
接下來開始實戰瞭
1、先創建對應相關操作的註解
1.1 bTable 標識表
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DbTable { //表名 String value(); }
1.2 DbPrimaryKey 標識主鍵
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface DbPrimaryKey { //表列名 String value(); //是否為自動增長 boolean isAuto() default false; }
1.3 DbFiled 標識成員屬性
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface DbFiled { //表列名 String value(); /* 這裡可以像主鍵一樣,添加其他屬性,比如是否唯一約束,是否非空等 甚至可以將主鍵的約束放在這裡來,隻是表明可以這樣做,具體怎樣擴展,完全可以按你們想法來 */ }
2、創建對應表操作類Dao層
2.1 建 待實現的基層 IBaseDao
public interface IBaseDao<T> { Long insert(T entity); int update(T entity, T where); /** * 刪除數據 * * @param where * @return */ int delete(T where); /** * 查詢數據 */ List<T> query(T where); List<T> query(T where, String groupBy, String orderBy, String having, Integer startIndex, Integer limit); }
代碼分析:
這裡創建瞭基類 IBaseDao ,擁有待實現的增刪改查, T 代表對應的 數據表結構的 model
。
2.2 建已實現的基層 BaseDao
public class BaseDao<T> implements IBaseDao<T> { private static final String TAG = "hqk"; /** * 持有數據庫操作類的引用 */ private SQLiteDatabase database; /** * 持有操作數據庫表所對應的java類型 * User */ private Class<T> entityClass; /** * 保證實例化一次 */ private boolean isInit = false; private String tableName; // 檢查表 private HashMap<String, Field> cacheMap; protected BaseDao() { } protected synchronized boolean init(Class<T> entity, SQLiteDatabase sqLiteDatabase) { if (!isInit) { //初始化完瞭 自動建表 entityClass = entity; database = sqLiteDatabase; if (entity.getAnnotation(DbTable.class) == null) { tableName = entity.getClass().getSimpleName(); } else { tableName = entity.getAnnotation(DbTable.class).value(); } if (!database.isOpen()) { return false; } String sql = createTable(); database.execSQL(sql); //建立好映射關系 initCacheMap(); isInit = true; } return true; } /** * 將真實表中的列名 + 成員變量進行 映射 * 緩存對應的 表 Model裡的屬性名以及對應表列名 */ private void initCacheMap() { cacheMap = new HashMap<>(); //這裡沒有必要查詢 對應表中的任何數據,隻想要對應表列名,所以 這 limit 0 String sql = "select * from " + tableName + " limit 0"; Cursor cursor = database.rawQuery(sql, null); String[] columnNames = cursor.getColumnNames(); Field[] columnFields = entityClass.getDeclaredFields(); //獲取對應表中的列名數組,以及對應表Model裡面的屬性數組 for (String columnName : columnNames) { Field resultField = null; for (Field field : columnFields) { //拿到對應屬性的註解值 String fieldAnnotationName = field.getAnnotation(DbFiled.class).value(); //如果對應的屬性註解值與數據庫表列名相同,則拿到對應屬性值 if (columnName.equals(fieldAnnotationName)) { resultField = field; break; } } if (resultField != null) { cacheMap.put(columnName, resultField); } } } /** * 組裝 創建表的SQL語句 * * @return */ private String createTable() { StringBuffer stringBuffer = new StringBuffer(); //開始組裝 SQL語句 stringBuffer.append("create table if not exists "); stringBuffer.append(tableName + " ("); Field[] fields = entityClass.getDeclaredFields(); for (Field field : fields) { Class type = field.getType(); String primaryKey = null; try { primaryKey = field.getAnnotation(DbPrimaryKey.class).value(); } catch (Exception e) { } Log.i(TAG, "createTable primaryKey " + primaryKey); Log.i(TAG, "createTable type " + type); if (type == String.class) { if (null == primaryKey) { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " TEXT,"); } else { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " TEXT PRIMARY KEY,"); } } else if (type == Double.class) { if (null == primaryKey) { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " DOUBLE,"); } else { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " DOUBLE PRIMARY KEY,"); } } else if (type == Integer.class) { if (null == primaryKey) { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " INTEGER,"); } else { boolean isAuto = field.getAnnotation(DbPrimaryKey.class).isAuto(); if (isAuto) { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " INTEGER PRIMARY KEY AUTOINCREMENT,"); } else { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " INTEGER PRIMARY KEY,"); } } } else if (type == Long.class) { if (null == primaryKey) { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " BIGINT,"); } else { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " BIGINT PRIMARY KEY,"); } } else if (type == byte[].class) { if (null == primaryKey) { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " BLOB,"); } else { stringBuffer.append(field.getAnnotation(DbFiled.class).value() + " BLOB PRIMARY KEY,"); } } else { /* 不支持的類型 */ continue; } } //循環完成後,最後一項會有 逗號 ,如果最後一個是逗號,則刪除最後一個字符 if (stringBuffer.charAt(stringBuffer.length() - 1) == ',') { stringBuffer.deleteCharAt(stringBuffer.length() - 1); } //SQL 語句 收尾 stringBuffer.append(")"); Log.i(TAG, "createTable: " + stringBuffer.toString()); return stringBuffer.toString(); } @Override public Long insert(T entity) { Map<String, String> map = getValues(entity); ContentValues contentValues = getContentValues(map); return database.insert(tableName, null, contentValues); } /** * 獲取對應 model 屬性以及對應的註解值(表列名值) * * @param entity 對應 表結構的model * @return 返回 key= 列名,value=屬性的值 map集合 */ private Map<String, String> getValues(T entity) { HashMap<String, String> map = new HashMap<>(); //獲取對應緩存 model 裡面的屬性鍵 Iterator<Field> fieldIterator = cacheMap.values().iterator(); while (fieldIterator.hasNext()) { Field field = fieldIterator.next(); field.setAccessible(true); try { Object object = field.get(entity); if (object == null) { continue; } String value = object.toString(); String key = field.getAnnotation(DbFiled.class).value(); //遍歷 取出對應 屬性的值 以及對應的 註解值,並添加至Map裡 if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) { map.put(key, value); } } catch (IllegalAccessException e) { e.printStackTrace(); } } return map; } /** * 數據庫數據結構的封裝 * * @param map 帶有 以表列名為鍵,的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 key = iterator.next(); String value = map.get(key); if (value != null) { contentValues.put(key, value); } } return contentValues; } @Override public int update(T entity, T where) { Map values = getValues(entity); ContentValues contentValues = getContentValues(values); //條件 Map whereMap = getValues(where); Condition condition = new Condition(whereMap); return database.update(tableName, contentValues, condition.whereClause, condition.whereArgs); } class Condition { String whereClause; String[] whereArgs; public Condition(Map<String, String> whereClause) { boolean flag = false; if (true && flag) { } ArrayList list = new ArrayList(); StringBuilder stringBuilder = new StringBuilder(); // 這裡之所以先添加 1=1 這個條件 是因為 // SQL where 後面需要給條件判斷,而下面 while 循環 直接添加瞭 and // SQL 語句就變成瞭 where and 這顯然不符合SQL語句 // 因此 加上 1=1 就變成瞭 where 1=1 and xx。起瞭一個呈上去下的作用 stringBuilder.append("1=1"); Set keys = whereClause.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { String key = (String) iterator.next(); String value = whereClause.get(key); if (value != null) { stringBuilder.append(" and " + key + " =?"); list.add(value); } } this.whereClause = stringBuilder.toString(); this.whereArgs = (String[]) list.toArray(new String[list.size()]); } } @Override public int delete(T where) { Map map = getValues(where); Condition condition = new Condition(map); return database.delete(tableName, condition.whereClause, condition.whereArgs); } @Override public List<T> query(T where) { return query(where, null, null, null, null, null ); } //所有 條件 @Override public List<T> query(T where, String groupBy, String orderBy, String having,Integer startIndex, Integer limit) { String limitString=null; if(startIndex!=null&&limit!=null) { limitString=startIndex+" , "+limit; } Map map=getValues(where); Condition condition=new Condition(map); Cursor cursor= database.query(tableName, null, condition.whereClause, condition.whereArgs, groupBy, having, orderBy, limitString ); // 封裝 --返回 List<T> result = getResult(cursor, where); cursor.close(); return result; } private List<T> getResult(Cursor cursor, T where) { ArrayList list=new ArrayList(); Object item; while (cursor.moveToNext()) { try { // cachmap ---對象中的成員變量 Filed annotion-- tb_name //cacheMap name ---Filed 1 // tb_name ---Filed 2 item=where.getClass().newInstance(); Iterator iterator=cacheMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry= (Map.Entry) iterator.next(); //tb_name /** * 得到列名 */ String colomunName= (String) entry.getKey(); // 通過列名查找到遊標的索性 Integer colmunIndex= cursor.getColumnIndex(colomunName); // Filed //反射的成員 cursor Field field= (Field) entry.getValue(); Class type=field.getType(); if(colmunIndex!=-1) { // if (type == String.class) { field.set(item, cursor.getString(colmunIndex)); }else if(type==Double.class) { field.set(item,cursor.getDouble(colmunIndex)); }else if(type==Integer.class) { field.set(item,cursor.getInt(colmunIndex)); }else if(type==Long.class) { field.set(item,cursor.getLong(colmunIndex)); }else if(type==byte[].class) { field.set(item,cursor.getBlob(colmunIndex)); /* 不支持的類型 */ }else { continue; } } } list.add(item); } catch ( Exception e) { e.printStackTrace(); } } return list; } }
代碼分析:
在這個BaseDao
裡面,幾乎分擔瞭數據表大部分的臟活累活,根據model
結構自動生成對應SQL
並創建對應表,以及基礎的增刪改查操作。
2.3 建對應model 的Dao層
1.UserDao
public class UserDao<User> extends BaseDao<User> { @Override public Long insert(User entity) { return super.insert(entity); } @Override public List<User> query(User where) { return super.query(where); } @Override public int delete(User where) { return super.delete(where); } @Override public int update(User entity, User where) { return super.update(entity, where); } @Override public List<User> query(User where, String groupBy, String orderBy, String having, Integer startIndex, Integer limit) { return super.query(where, groupBy, orderBy, having, startIndex, limit); } }
2.PhotoDao
public class PhotoDao<Photo> extends BaseDao<Photo> { @Override public Long insert(Photo entity) { return super.insert(entity); } @Override public int update(Photo entity, Photo where) { return super.update(entity, where); } @Override public List<Photo> query(Photo where) { return super.query(where); } @Override public int delete(Photo where) { return super.delete(where); } }
代碼分析:
雖然 BaseDao
已經完成瞭幾乎所有的操作,但是一旦遇到多表查詢的時候,光是一個BaseDao
遠遠不夠。所以這裡還是選擇創建不同model
的Dao
層,並繼承與BaseDao
。也就是說,有多少表,最好就創建對應多少個Dao層。
3、創建數據庫工廠
public class BaseDaoFactory { private final String TAG = "hqk"; private SQLiteDatabase sqLiteDatabase; private String sqliteDatabasePath; private static BaseDaoFactory instance = new BaseDaoFactory(); //餓漢單例模式 public static BaseDaoFactory getInstance() { return instance; } public BaseDaoFactory() { //讀者可隨意更改路徑以及對應數據庫名,這裡演示暫時放在根目錄 sqliteDatabasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/hqk.db"; sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null); Log.i(TAG, "sqliteDatabasePath : " + sqliteDatabasePath); Log.i(TAG, "sqLiteDatabase : " + sqLiteDatabase.getPath()); } /** * @param clazz * @param entityClass * @param <R> 我們在這可以把它看成某一個對象,它繼承與 BaseDao<T> ,而裡面的T 就是下面的那個空對象 * @param <T> 我們在這可以吧它看成某一個空對象 T * @return */ public synchronized <R extends BaseDao<T>, T> R createBaseDao(Class<R> clazz, Class<T> entityClass) { BaseDao baseDao = null; try { baseDao = clazz.newInstance(); baseDao.init(entityClass, sqLiteDatabase); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } return (R) baseDao; } }
代碼分析:
這裡也沒啥好分析的,就一個數據庫創建,以及對應model
的初始化。唯一值得註意的就是初始化的時候用瞭倆個泛型,具體什麼意思,可按照代碼註釋理解。
4、創建對應model
1.User
@DbTable("tb_user") public class User { @DbPrimaryKey(value = "tb_id", isAuto = true) @DbFiled("tb_id") public Integer id; @DbFiled("tb_name") public String name;// @DbFiled("tb_age") public Integer age; public User(String name, Integer age) { this.name = name; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public User() { } }
2.Photo
@DbTable("tb_photo") public class Photo { @DbFiled("time") private String time; @DbFiled("id") private Long id; @DbFiled("path") private String path; public Photo( ) { } public Photo(String time, Long id, String path) { this.time = time; this.id = id; this.path = path; } public void setTime(String time) { this.time = time; } public void setId(Long id) { this.id = id; } public void setPath(String path) { this.path = path; } }
代碼分析:
這倆類就是對應表結構model
類,用到瞭對應註解,相信通過註解能夠清楚知道對應表結構是怎樣的。
5、最終使用
ainActivity
public class MainActivity extends AppCompatActivity { UserDao<User> userDao; PhotoDao<Photo> photoDao; private ArrayList<User> listUser = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestPermission(this); } public void save(View view) { User user = new User("hqk", 18); long size = userDao.insert(user); Photo photo = new Photo("time", System.currentTimeMillis(), "path"); long photoSize = photoDao.insert(photo); Toast.makeText(this, "save line : " + size, Toast.LENGTH_LONG).show(); } public void update(View view) { User where = new User(); where.setAge(18); int size = userDao.update(new User("TOM", 99), where); Toast.makeText(this, "update Size : " + size, Toast.LENGTH_LONG).show(); } public void delete(View view) { User where = new User(); where.setAge(18); int size = userDao.delete(where); Toast.makeText(this, "delete Size : " + size, Toast.LENGTH_LONG).show(); } public void queryList(View view) { listUser.clear(); listUser.addAll(userDao.query(new User())); Toast.makeText(this, "查詢條數為:" + listUser.size(), Toast.LENGTH_LONG).show(); } public void requestPermission( Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(activity, new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, 1); return; } createTable(); } private void createTable() { userDao = BaseDaoFactory.getInstance().createBaseDao(UserDao.class, User.class); photoDao = BaseDaoFactory.getInstance().createBaseDao(PhotoDao.class, Photo.class); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); createTable(); } }
到此這篇關於Android
架構之數據庫框架搭建的文章就介紹到這瞭,更多相關Android
架構數據庫框架搭建內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Spring JPA學習之delete方法示例詳解
- Java開發反射機制的實戰經驗總結
- Android傳感器數據獲取的方法
- Java效率提升神器之Guava-Joiner
- Java設計模式之java迭代器模式詳解