Android 面向对象式数据库架构设计1

greenDao的流程图:
在这里插入图片描述

下面代码展示了数据库的使用:

 BaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(User.class);//1
 baseDao.insert(new User(1, "hongxue", "123"));//2

注释1:使用BaseDaoFactory工厂得到BaseDao。
注释2:执行插入数据操作。

1 BaseDaoFactory

BaseDaoFactory用于创建数据库,BaseDaoFactory的getBaseDao方法用于获取BaseDao。

public class BaseDaoFactory {
    private static final BaseDaoFactory ourInstance = new BaseDaoFactory();

    private SQLiteDatabase sqLiteDatabase;

    //定义建数据库的路劲
    //建议写到sd卡中,好处,app 删除了,下次再安装的时候,数据还在
    private String sqliteDatabasePath;

    public static BaseDaoFactory getOurInstance(){
        return ourInstance;
    }

    public BaseDaoFactory() {
        //新建数据库
        sqliteDatabasePath = "data/data/com.hongx.database/hongxue.db";
        sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);
    }

    public <T> BaseDao<T> getBaseDao(Class<T> entityClass){
        BaseDao baseDao = null;
        try {
            baseDao = BaseDao.class.newInstance();
            baseDao.init(sqLiteDatabase, entityClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return baseDao;
    }
}

2 IBaseDao

创建一个接口IBaseDao,包含了增删改查操作。

public interface IBaseDao<T> {
    //插入
    long insert(T entity);
    //更新
    long updata(T entity, T where); //new User(1, "alan0", "123")
    //删除
    int delete(T where);
    //查询
    List<T> query(T where);
    List<T> query(T where, String orderBy, Integer startIndex, Integer limit);
}

接下来创建BaseDao继承IBaseDao,在实现BaseDao之前有必要先从javabean说起,比如User.java

public class User {

    private Integer id;
    private String name;
    private String password;

}

我们可以将类名作为表名,参数名作为表的属性名

如果想使用自定义的表名和属性名,可以通过注解+反射来操作。

3 两个注解

添加两个注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
    String value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {
    String value();
}

4 javabean-User

在User中使用注解:

@DbTable("tb_user")
public class User {
    @DbField("_id")
    private Integer id;
    private String name;
    private String password;
    public User() {
    }
    public User(Integer id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
    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 String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

接下来创建BaseDao,如果javabean中添加了注解,就反射注解获取表名或者属性名,如果没有添加注解就直接反射获取类名或者参数名


5 BaseDao

5.1 实现IBaseDao接口

public class BaseDao<T> implements IBaseDao<T> {
    @Override
    public long insert(T entity) {
        return 0;
    }
    @Override
    public long update(T entity, T where) {
        return 0;
    }
    @Override
    public int delete(T where) {
        return 0;
    }
    @Override
    public List<T> query(T where) {
        return null;
    }
    @Override
    public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
        return null;
    }
}

5.2 创建表

我们再看看BaseDaoFactory的getBaseDao方法及其调用:

 public <T> BaseDao<T> getBaseDao(Class<T> entityClass){
        BaseDao baseDao = null;
        try {
            baseDao = BaseDao.class.newInstance();//1
            baseDao.init(sqLiteDatabase, entityClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return baseDao;
    }
 BaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(User.class);

注释1:通过newInstance获取BaseDao实例,而不是直接new一个对象的目的是,不想将BaseDao对外提供,而是要通过BaseDaoFactory。


从上面看出,BaseDao中需要:
1 持有数据库操作的引用sqLiteDatabase
2 持有操作数据库所对应的java类型entityClass

public class BaseDao<T> implements IBaseDao<T>{

    //持有数据库操作的引用
    private SQLiteDatabase sqLiteDatabase;

    //表名
    private String tableName;

    //持有操作数据库所对应的java类型
    private Class<T> entityClass;

    //标记:用来表示是否做过初始化操作
    private boolean isInit = false;

    //定义一个缓存空间(key-字段名   value-成员变量)
    private HashMap<String, Field> cacheMap;

    //架构内部的逻辑,最后不要提供构造方法给调用层使用
    protected void init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
        this.sqLiteDatabase = sqLiteDatabase;//1
        this.entityClass = entityClass;//2

        //可以根据传入的entityClass类型来建表,只需要建一次
        if(!isInit){//3
            //自动建表
            //取得表明
            if(entityClass.getAnnotation(DbTable.class) == null){//4
                //反射到类名
                this.tableName =  entityClass.getSimpleName();
            } else {//5
                //取得注解上的名字
                this.tableName =  entityClass.getAnnotation(DbTable.class).value();
            }

            //执行建表操作
            //create table if not exists
            // tb_user(_id integer, name varchar(20), password varchar(20))
            //使用getCreateTableSql() 生成sql语句
            String createTableSql = getCreateTableSql();//6
            Log.i(TAG, createTableSql);
            this.sqLiteDatabase.execSQL(createTableSql);//7
            cacheMap  = new HashMap<>();
            initCacheMap();//8
            isInit = true;
        }
    }
	...
}

注释1:获取SQLiteDatabase
注释2:获取Class对象
注释3:为了防止一个表创建多次,添加一个标记。
注释4:没有获取到DbTable注解,直接反射类名作为表名
注释5:获取到DbTable注解,反射注解得到表名
注释6:生成创建表的sql语句
注释7:执行sql语句创建表
注释8:初始化一个缓存空间(key-字段名 value-成员变量)

生成创建表的sql语句

    private String getCreateTableSql() {
        //create table if not exists
        // tb_user(_id integer, name varchar(20), password varchar(20),)
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("create table if not exists ");
        stringBuffer.append(tableName + "(");
        //反射得到所有的成员变量
        Field[] declaredFields = entityClass.getDeclaredFields();//1
        for (Field field : declaredFields) {//2
            Class type = field.getType();// 拿到成员的类型

            if(field.getAnnotation(DbField.class) != null){
                //通过注解获取
                if(type == String.class){
                    stringBuffer.append(field.getAnnotation(DbField.class).value() + " TEXT,");
                } else  if(type == Integer.class){
                    stringBuffer.append(field.getAnnotation(DbField.class).value() + " INTEGER,");
                }else  if(type == Long.class){
                    stringBuffer.append(field.getAnnotation(DbField.class).value() + " BIGINT,");
                }else  if(type == Double.class){
                    stringBuffer.append(field.getAnnotation(DbField.class).value() + " DOUBLE,");
                }else  if(type == byte[].class){
                    stringBuffer.append(field.getAnnotation(DbField.class).value() + " BLOB,");
                }else {
                    //不支持的类型
                    continue;
                }
            } else {
                //通过反射获取
                if(type == String.class){
                    stringBuffer.append(field.getName() + " TEXT,");
                } else  if(type == Integer.class){
                    stringBuffer.append(field.getName() + " INTEGER,");
                }else  if(type == Long.class){
                    stringBuffer.append(field.getName() + " BIGINT,");
                }else  if(type == Double.class){
                    stringBuffer.append(field.getName() + " DOUBLE,");
                }else  if(type == byte[].class){
                    stringBuffer.append(field.getName() + " BLOB,");
                }else {
                    //不支持的类型
                    continue;
                }
            }
        }

        if(stringBuffer.charAt(stringBuffer.length()  - 1) == ','){
            stringBuffer.deleteCharAt(stringBuffer.length()  - 1);
        }
        stringBuffer.append(")");
        return stringBuffer.toString();
    }

注释1:反射得到所有的成员变量。
注释2:for循环拿到成员的类型,同样的,如果存在注解就通过注解获取,不存在就通过反射直接获取。

initCacheMap

initCacheMap方法作用是将所有成员变量名保存到缓存中,这样在操作增删改查的时候,不需要每次都去获取成员变量名。

    private void initCacheMap() {
        //1、取得所有字段名
        String sql = "select * from " + tableName + " limit 1, 0";  //空表
        Cursor cursor = sqLiteDatabase.rawQuery(sql, null);
        String[] columnNames = cursor.getColumnNames();
        //2、取所有的成员变量
        Field[] declaredFields = entityClass.getDeclaredFields();
        //把所有字段的访问权限打开
        for (Field f: declaredFields) {
            f.setAccessible(true);
        }
        //字段 跟  成员变量一一对应
        for(String columNage : columnNames){
            Field columnField = null;
            for(Field field : declaredFields){
                String fieldName = "";  //对象中的成员变量名字
                if(field.getAnnotation(DbField.class) != null){
                    fieldName = field.getAnnotation(DbField.class).value();
                } else {
                    fieldName = field.getName();
                }
                if(columNage.equals(fieldName)){  //匹配
                    columnField = field;
                    break;
                }
            }
            if(columnField != null){
                cacheMap.put(columNage, columnField);
            }
        }
    }

5.3 实现插入

我们常规的插入操作如下:

    @Override
    public long insert(T entity) {
        ContentValues contentValues = new ContentValues();
        contentValues.put("_id", 1);
        contentValues.put("name", "hongxue");
        contentValues.put("password", "123456");
        sqLiteDatabase.insert(tableName, null, contentValues);
     }

我们要实现的是通过对象拿到属性和值,而不是手动去设置。

	@Override
    public long insert(T entity) {
        //1、准本好ContentValues中需要的数据
        Map<String, String> map = getValues(entity);

        //2、把数据转移到ContentValues
        ContentValues values = getContentValues(map);
        //3、开始插入
        sqLiteDatabase.insert(tableName, null, values);
        return 0;
    }

    //key(字段)--- value(成员变量)
    private Map<String, String> getValues(T entity) {
        HashMap<String, String> map = new HashMap<>();
        //返回的是所有的成员变量
        Iterator<Field> iterator = cacheMap.values().iterator();//1
        while (iterator.hasNext()){
            Field field = iterator.next();
            field.setAccessible(true);
            try {
                //获取对象的属性值
                Object obj = field.get(entity);//2
                if(obj == null){
                    continue;
                }
                String value = obj.toString();
                String key  = "";
                if(field.getAnnotation(DbField.class) != null){
                    key = field.getAnnotation(DbField.class).value();
                } else {
                    key = field.getName();
                }
                if(!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)){
                    map.put(key, value);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return map;
    }

注释1:在initCacheMap中已经将成员变量名保存在了cacheMap中。
注释2:通过变量名反射,获取对象中变量名对应的属性值。


把数据转移到ContentValues,代码如下:

    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;
    }

5.4 更新

BaseDao的update方法:

 @Override
    public long update(T entity, T where) {
        /**
         * whereClause 可选的where语句
         * whereArgs whereClause语句中表达式的?占位参数列表
         */
//        sqLiteDatabase.update(tableName, contentValue, "id = ?", new String[]{"1"});
        int result = -1;
        //1、准本好ContentValues中需要的数据
        Map<String, String> values = getValues(entity);
        ContentValues contentValues = getContentValues(values);

        //条件map
        Map<String, String> values1 = getValues(where);

        Condition condition = new Condition(values1);

        result = sqLiteDatabase.update(tableName, contentValues, condition.whereClause, condition.whereArgs);
        return result;
    }

Condition中完成了update的sql语句,如下:

    private class Condition {
        private String whereClause;  //1=1 and password = ?  --- >password = ?
        private String[] whereArgs;

        /**
         * new Person(1, "alan", "123")
         * @param whereCasue
         */
        public Condition(Map<String, String> whereCasue ) {
            ArrayList list = new ArrayList();  //whereArgs里面的内容存入的list
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("1=1");
            //取得所有成员变量的名字
            Set<String> keys = whereCasue.keySet();
            Iterator<String> iterator = keys.iterator();
            while (iterator.hasNext()){
                String key = iterator.next();
                String value = whereCasue.get(key);
                if(value != null){
                    stringBuffer.append(" and " + key + "=?");
                    list.add(value);
                }
            }

            this.whereClause = stringBuffer.toString();
            this.whereArgs = (String[]) list.toArray(new String[list.size()]);
        }
    }

5.5 删除

BaseDao的delete方法:

    @Override
    public int delete(T where) {
//        sqLiteDatabase.delete(tableName,"id = ?", new String[]{"1"} );
        int result = -1;
        //1、准本好ContentValues中需要的数据
        Map<String, String> values = getValues(where);
        Condition condition = new Condition(values);
        result = sqLiteDatabase.delete(tableName, condition.whereClause, condition.whereArgs);
        return result;
    }

5.5 查询

 @Override
    public List<T> query(T where) {
        return query(where, null, null, null);
    }

    @Override
    public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
//        sqLiteDatabase.query(tableName, null, "id = ?",
//                new String[], null. null, orderBy, "1, 5");
        //1、准本好ContentValues中需要的数据
        Map<String, String> values = getValues(where);

        String limitString = "";   //"2,6"
        if(startIndex != null && limit != null){
            limitString = startIndex + " , " + limit;
        }

        Condition condition = new Condition(values);

        Cursor query = sqLiteDatabase.query(tableName, null, condition.whereClause,
                condition.whereArgs, null, orderBy, limitString);

        List<T> result = getResult(query, where);  //游标  --- 》 javabean  --- list<javabaen>
        return result;
    }

    private List<T> getResult(Cursor query, T where) {
        ArrayList list = new ArrayList();
        Object item = null;
        while (query.moveToNext()){
            try {
                item = where.getClass().newInstance();  //因为不知道 new  ? , 所以通过反射方式
                //cacheMap  (字段---成员变量的名字)
                Iterator<Map.Entry<String, Field>> iterator = cacheMap.entrySet().iterator();
                while (iterator.hasNext()){
                    Map.Entry<String, Field> entry = iterator.next();
                    //取列名
                    String columnName = entry.getKey();

                    //以列名拿到列名在游标中的位置
                    int columnIndex = query.getColumnIndex(columnName);

                    Field value = entry.getValue();   //id
                    Class<?> type = value.getType();  //Integer
                    if(columnIndex != -1){  //columnName = "age"
                        if(type==String.class){
                            value.set(item,query.getString(columnIndex));//setid(1)
                        }else if(type==Double.class){
                            value.set(item,query.getDouble(columnIndex));
                        }else if(type==Integer.class){
                            value.set(item,query.getInt(columnIndex));
                        }else if(type==Long.class){
                            value.set(item,query.getLong(columnIndex));
                        }else if(type==byte[].class){
                            value.set(item,query.getBlob(columnIndex));
                        }else{
                            continue;
                        }
                    }
                }
                list.add(item);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        query.close();
        return list;
    }

对应SQLiteDatabase中的query方法如下:

  public Cursor query(String table, String[] columns, String selection,
            String[] selectionArgs, String groupBy, String having,
            String orderBy) {

        return query(false, table, columns, selection, selectionArgs, groupBy,
                having, orderBy, null /* limit */);
    }

6 BaseDao的扩展

创建BaseDaoNewImpl继承BaseDao:

public class BaseDaoNewImpl<T> extends BaseDao<T>{
    public List<T> query(String sql){
        return  null;
    };
}

在BaseDaoFactory中添加getBaseDao方法:

public <T extends BaseDao<M>, M>T getBaseDao(Class<T> daoClass, Class<M> entityClass){
        BaseDao baseDao = null;
        try {
            baseDao = daoClass.newInstance();
            baseDao.init(sqLiteDatabase, entityClass);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return (T)baseDao;
    }

使用:

  public void clickUpdate(View view) {
        BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(
                BaseDaoNewImpl.class, Person.class);
        Person person = new Person();
        person.setName("jett");
        Person where = new Person();
        where.setId(1);
        long update = baseDao.update(person, where);
        Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();
    }

    public void clickDelete(View view) {
        BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(
                BaseDaoNewImpl.class, Person.class);
        Person where = new Person();
        where.setName("jett");
        where.setId(1);
        int delete = baseDao.delete(where);
        Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();
    }

    public void clickSelect(View view) {
        BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(
                BaseDaoNewImpl.class, Person.class);
        Person where = new Person();
        where.setId(1);
        List<Person> query = baseDao.query(where);
        Log.i("dn_alan", query.size() + "");
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值