Android 架构之数据库框架搭建

作者:hqk 时间:2021-09-28 23:26:06 

目录
  • 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、最终使用

        前言:

        你还在苦恼的写SQL么?你还在为数据库升级而烦恼么?你还在因查询数据而写繁琐不可用的代码么? 在这,这些都将不复存在!在本篇中,将会让你一点一滴从无到有创建一个不再为数据库而烦恼的框架。

        在开始之前我们先欣赏一下本章实现的最终效果 效果展示 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<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远远不够。所以这里还是选择创建不同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 <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();
           }
        }

        来源:https://juejin.cn/post/6985052371420184612

        标签:Android,架构,数据库,框架
        0
        投稿

        猜你喜欢

      • Spring Boot实现文件上传下载

        2021-11-22 21:19:36
      • springboot整合security和vue的实践

        2021-09-17 20:39:28
      • java使用lambda表达式对List集合进行操作技巧(JDK1.8)

        2021-05-30 06:41:06
      • Mybatis-Plus查询中如何排除标识字段

        2023-11-23 20:38:46
      • Java环境配置图文教程(推荐)

        2023-09-17 11:27:42
      • 25个最好的免费Eclipse插件

        2021-09-21 10:56:24
      • C#模拟实现QQ窗体功能

        2021-07-17 02:49:11
      • SpringBoot项目部署到腾讯云的实现步骤

        2023-01-01 16:58:55
      • java算法实现预测双色球中奖号码

        2022-06-19 17:01:22
      • Java开发人员需知的十大戒律

        2023-09-17 07:33:50
      • Java通过What、Why、How了解弱引用

        2021-11-01 00:06:20
      • Scala小程序详解及实例代码

        2023-03-29 12:10:56
      • Java数据结构及算法实例:朴素字符匹配 Brute Force

        2022-01-10 15:03:40
      • C#使用NPOI上传excel

        2022-05-20 14:46:22
      • java中动态 代理的实现

        2023-11-17 16:16:25
      • Java抢红包的红包生成算法

        2023-06-23 20:52:04
      • 深入理解C#之继承

        2022-02-25 07:25:44
      • 手动实现将本地jar添加到Maven仓库

        2021-06-12 16:06:49
      • C#二进制读写BinaryReader、BinaryWriter、BinaryFormatter

        2022-03-07 23:01:28
      • Quarkus中ConfigSourceInterceptor的加密配置实现

        2021-10-08 10:47:14
      • asp之家 软件编程 m.aspxhome.com