Android|Android 架构之数据库框架搭建

目录

  • 1、先创建对应相关操作的注解
    • 1.1 bTable 标识表
    • 1.2 DbPrimaryKey 标识主键
    • 1.3 DbFiled 标识成员属性
  • 2、创建对应表操作类Dao层
    • 2.1 建 待实现的基层 IBaseDao
    • 2.2 建已实现的基层 BaseDao
    • 2.3 建对应model 的Dao层
  • 3、创建数据库工厂
    • 4、创建对应model
      • 5、最终使用
        前言:
        【Android|Android 架构之数据库框架搭建】你还在苦恼的写SQL么?你还在为数据库升级而烦恼么?你还在因查询数据而写繁琐不可用的代码么? 在这,这些都将不复存在!在本篇中,将会让你一点一滴从无到有创建一个不再为数据库而烦恼的框架。
        在开始之前我们先欣赏一下本章实现的最终效果 效果展示 Android|Android 架构之数据库框架搭建
        文章图片

        如图所示:
        • 对应的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 {Long insert(T entity); int update(T entity, T where); /*** 删除数据** @param where* @return*/int delete(T where); /*** 查询数据*/List query(T where); List query(T where, String groupBy, String orderBy, String having, Integer startIndex,Integer limit); }

        代码分析:
        这里创建了基类 IBaseDao ,拥有待实现的增删改查, T 代表对应的 数据表结构的 model

        2.2 建已实现的基层 BaseDao

        public class BaseDao implements IBaseDao {private static final String TAG = "hqk"; /*** 持有数据库操作类的引用*/private SQLiteDatabase database; /*** 持有操作数据库表所对应的java类型* User*/private Class entityClass; /*** 保证实例化一次*/private boolean isInit = false; private String tableName; //检查表private HashMap cacheMap; protected BaseDao() {}protected synchronized boolean init(Class 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 0String 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(); }@Overridepublic Long insert(T entity) {Map map = getValues(entity); ContentValues contentValues = getContentValues(map); return database.insert(tableName, null, contentValues); }/*** 获取对应 model 属性以及对应的注解值(表列名值)** @param entity 对应 表结构的model* @return 返回 key= 列名,value=https://www.it610.com/article/属性的值map集合*/private Map getValues(T entity) {HashMap map = new HashMap<>(); //获取对应缓存 model 里面的属性键Iterator 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 = https://www.it610.com/article/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 map) {ContentValues contentValues = new ContentValues(); Set keys = map.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) {String key = iterator.next(); String value = map.get(key); if (value != null) {contentValues.put(key, value); }}return contentValues; }@Overridepublic 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 whereClause) {boolean flag = false; if (true && flag) {}ArrayList list = new ArrayList(); StringBuilder stringBuilder = new StringBuilder(); // 这里之所以先添加 1=1 这个条件 是因为// SQLwhere后面需要给条件判断,而下面 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 = https://www.it610.com/article/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()]); }}@Overridepublic int delete(T where) {Map map = getValues(where); Condition condition = new Condition(map); return database.delete(tableName, condition.whereClause, condition.whereArgs); }@Overridepublic List query(T where) {return query(where, null, null, null, null, null); }//所有条件@Overridepublic List 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 result = getResult(cursor, where); cursor.close(); return result; }private List getResult(Cursor cursor, T where) {ArrayListlist=new ArrayList(); Object item; while (cursor.moveToNext()) {try {//cachmap---对象中的成员变量Filedannotion-- tb_name//cacheMapname---Filed1//tb_name---Filed2item=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//反射的成员 cursorField 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)); }elseif(type==Integer.class){field.set(item,cursor.getInt(colmunIndex)); }else if(type==Long.class){field.set(item,cursor.getLong(colmunIndex)); }elseif(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 extends BaseDao {@Overridepublic Long insert(User entity) {return super.insert(entity); }@Overridepublic List query(User where) {return super.query(where); }@Overridepublic int delete(User where) {return super.delete(where); }@Overridepublic int update(User entity, User where) {return super.update(entity, where); }@Overridepublic List 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 extends BaseDao {@Overridepublic Long insert(Photo entity) {return super.insert(entity); }@Overridepublic int update(Photo entity, Photo where) {return super.update(entity, where); }@Overridepublic List query(Photo where) {return super.query(where); }@Overridepublic int delete(Photo where) {return super.delete(where); }}

        代码分析:
        虽然 BaseDao 已经完成了几乎所有的操作,但是一旦遇到多表查询的时候,光是一个BaseDao远远不够。所以这里还是选择创建不同modelDao层,并继承与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 我们在这可以把它看成某一个对象,它继承与 BaseDao ,而里面的T 就是下面的那个空对象* @param 我们在这可以吧它看成某一个空对象 T* @return*/public synchronized , T> R createBaseDao(Class clazz, Class 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 = "https://www.it610.com/article/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")privateString time; @DbFiled("id")privateLong id; @DbFiled("path")privateString 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 userDao; PhotoDao photoDao; private ArrayList listUser = new ArrayList<>(); @Overrideprotected 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); }@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults); createTable(); }}

        到此这篇关于Android 架构之数据库框架搭建的文章就介绍到这了,更多相关Android 架构数据库框架搭建内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

          推荐阅读