Android 架構之數據框架搭建【上】

前言

你還在苦惱的寫SQL麼?你還在爲數據庫升級而煩惱麼?你還在因查詢數據而寫繁瑣不可用的代碼麼? 在這,這些都將不復存在!在本篇中,將會讓你一點一滴從無到有建立一個再也不爲數據庫而煩惱的框架。java

在開始以前咱們先欣賞一下本章實現的最終效果 效果展現git

如圖所示github

  • 對應的model,可直接成爲表結構,再也不寫對應的 Create table xxx對應的SQL了
  • 對應model的Dao層,裏面封裝了數據表的基本操做(增刪改查)
  • 對應的增刪改查操做,不再用SQL了,全用對象處理

接下來開始實戰了sql

一、先建立對應相關操做的註解

@DbTable 標識表

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
	//表名
    String value();
}

複製代碼

@DbPrimaryKey 標識主鍵

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbPrimaryKey {
    
    //表列名
    String value();

    //是否爲自動增加
    boolean isAuto() default false;
}
複製代碼

@DbFiled 標識成員屬性

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbFiled {
	
	//表列名
    String value();
    /* 這裏能夠像主鍵同樣,添加其餘屬性,好比是否惟一約束,是否非空等 甚至能夠將主鍵的約束放在這裏來,只是代表能夠這樣作,具體怎樣擴展,徹底能夠按大家想法來 */
}
複製代碼

二、建立對應表操做類Dao層

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》建立 已實現的基層 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並建立對應表,以及基礎的增刪改查操做。markdown

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層。app

三、建立數據庫工廠

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的初始化。惟一值得注意的就是初始化的時候用了倆個泛型,具體什麼意思,可按照代碼註釋理解。

四、建立對應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 類,用到了對應註解,相信經過註解可以清楚知道對應表結構是怎樣的。

五、最終使用

MainActivity

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();
    }
}
複製代碼

Demo地址:點我下載

下一章,將會講解數據庫如何快速升級

相關文章
相關標籤/搜索