用面向对象的方式来操作数据库

正常来说使用sql来操作数据库的话没啥问题,但是对于一些不太擅长写sql的同学来说,通过面向对象的方式来操作数据库,可以极大的避免写sql时不小心犯错误的情况,接下来让我们直接进入主题

首先是需要先定义接口规范,即数据库常用的增删改查操作,这用来让我们知道有哪些功能,当然还可以自己再继续扩展

public interface IBaseDao<T> {

    /**
     *  插入
     */
    long insert(T entity);

    /**
     * 更新
     */
    long update(T entity, T where);

    /**
     * 删除
     */
    int delete(T where);

    /**
     * 查询
     */
    List<T> query(T where);

    /**
     * 限制查询
     */
    List<T> query(T where, String orderBy, Integer startIndex, Integer limit);
}

使用注解的方式可以让我们来更好的定义表名以及列名,因为有时候对象既要对应后台下发的字段,又想适应数据库名的规范

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {

    String value();
}



@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {

    String value();
}

接着就是建库的操作,用单例方式来进行建立数据库操作

public class BaseDaoFactory {

    private static BaseDaoFactory sInstance;
    private SQLiteDatabase sqLiteDatabase;
    /**
     * 设计一个数据库的连接池
     */
    protected Map<String, BaseDao> map = Collections.synchronizedMap(new HashMap<String, BaseDao>());
    /**
     * 定义建数据库的路径
     */
    private String sqliteDatabasePath;

    public static BaseDaoFactory getInstance() {
        if (sInstance == null) {
            synchronized (BaseDaoFactory.class) {
                if (sInstance == null) {
                    sInstance = new BaseDaoFactory();
                }
            }
        }
        return sInstance;
    }

    public BaseDaoFactory() {
//        Environment.getExternalStorageDirectory() + File.separator;
        sqliteDatabasePath = "data/data/com.tgp.dbapp/test.db";
        //新建数据库
        sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);
    }

}

这里数据库的路径可以也写到sd卡中,这样即使App被卸载了,下次安装的时候还可以直接获取数据继续操作,当然这个具体根据需求情况来存储位置就可以

接着创建BaseDao去实现定义的接口,同时让我们在构造方法中对其进行一些初始化操作,

public void init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
        this.sqLiteDatabase = sqLiteDatabase;
        this.entityClass = entityClass;

        //*************自动建表*************
        //根据传入的entityClass来建表,只需要建一次
        if (!isInit) {
            //取得表名

            if (entityClass.getAnnotation(DbTable.class) == null) {
                //用类名做表名
                this.tableName = entityClass.getSimpleName();
            } else {
                //取得注解上的名字
                this.tableName = entityClass.getAnnotation(DbTable.class).value();
            }
            //执行建表操作
            String createSql = getCreateTableSql();
            Log.i(TAG, createSql);
            this.sqLiteDatabase.execSQL(createSql);
            cacheMap = new HashMap<>();
            initCacheMap();
            isInit = true;
        }

    }

用面向对象的方式来操作数据库,本质上来说还是使用sql,只不过对外展现的样子是面向对象的方式

所以执行建表操作的时候就需要我们根据对象去生成相应的sql语句,平时我们写的建表语句为

create table if not exists tb_user(id integer, name TEXT)

这种样子,上面的表名我们已经可以通过注解或者获取类名的方式获取到了,现在需要的是获取到有哪些列,即对象中是什么字段,这时候就用到了反射的操作

private String getCreateTableSql() {
        //create table if not exists tb_user(id integer, name TEXT)
        StringBuilder sb = new StringBuilder();
        sb.append("create table if not exists ");
        sb.append(tableName).append("(");
        //反射得到所有的成员变量
        Field[] declaredFields = entityClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //拿到成员类型
            Class<?> type = field.getType();
            if (field.getAnnotation(DbField.class) != null) {
                if (type == String.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" TEXT,");
                } else if (type == Integer.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" Integer,");
                } else if (type == Long.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" BIGINT,");
                } else if (type == Double.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" DOUBLE,");
                } else if (type == byte[].class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" BLOB,");
                } else {
                    //不支持的类型
                    continue;
                }
            } else {
                if (type == String.class) {
                    sb.append(field.getName()).append(" TEXT,");
                } else if (type == Integer.class) {
                    sb.append(field.getName()).append(" Integer,");
                } else if (type == Long.class) {
                    sb.append(field.getName()).append(" BIGINT,");
                } else if (type == Double.class) {
                    sb.append(field.getName()).append(" DOUBLE,");
                } else if (type == byte[].class) {
                    sb.append(field.getName()).append(" BLOB,");
                } else {
                    //不支持的类型
                    continue;
                }
            }
        }
        //去掉最后的逗号
        if (',' == (sb.charAt(sb.length() - 1))) {
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append(")");
        return sb.toString();
    }

上述代码中,同样的有注解的用注解来做列名,没有的话使用字段名来做列名,并根据数据库的列的字段类型来匹配

this.sqLiteDatabase.execSQL(createSql);获取到创建表的语句后我们就可以执行建表操作了

我们这里需要缓存下列名和字段的map,以此来方便后面的增删改查等操作

private void initCacheMap() {
        //取得所有字段名
        // 查询空表
        String sql = "select * from " + tableName + " limit 1, 0";
        Cursor cursor = sqLiteDatabase.rawQuery(sql, null);
        //获取列名
        String[] columnNames = cursor.getColumnNames();
        //取所有的成员变量
        Field[] declaredFields = entityClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
        }
        //字段跟对象的成员变量一一对应
        for (String columName : 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 (columName.equals(fieldName)) {
                    columnField = field;
                    break;
                }
            }
            if (columnField != null) {
                cacheMap.put(columName, columnField);
            }
        }
    }

到这里我们就可以进行增删改查的操作了,首先来看下增的操作

正常的sql语句写法为    sqLiteDatabase.insert(tableName, null, contentValues);

表名我们已经获取到了,接着就是去组装数据的操作,首先让我们来获取一个Map,key是列名,value就是字段对应的值,上面我们缓存的对象的 列名-字段,这里我们拿到传进来的对象,通过反射的操作就可以拿到对应的值

private Map<String, String> getValueMap(T entity) {
        HashMap<String, String> map = new HashMap<>();
        //返回的是所有的成员变量
        for (Field next : cacheMap.values()) {
            next.setAccessible(true);
            try {
                //获取对象的属性值
                Object o = next.get(entity);
                if (o == null) {
                    continue;
                }
                //张三, 3
                String value = o.toString();
                String key = "";
                if (next.getAnnotation(DbField.class) != null) {
                    key = next.getAnnotation(DbField.class).value();
                } else {
                    key = next.getName();
                }
                if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
                    map.put(key, value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

拿到这里列-字段内容的map后,我们就可以去组装contentValues了

 private ContentValues getContentValues(Map<String, String> map) {
        ContentValues contentValues = new ContentValues();
        for (String key : map.keySet()) {
            String value = map.get(key);
            if (value != null) {
                contentValues.put(key, value);
            }
        }
        return contentValues;
    }

相应的增加操作就完成了

@Override
    public long insert(T entity) {
   
        //准备好ContentValues中需要的数据
        Map<String, String> map = getValueMap(entity);
        //把数据转移到ContentValues中
        ContentValues contentValues = getContentValues(map);
        //数据库操作
        return sqLiteDatabase.insert(tableName, null, contentValues);
    }

update:

  sqLiteDatabase.update(tableName, contentvalue, "name = ?", new String[]{"tgp"});

更改的sql语句是这样写的,表名已经有了,然后就是要变更成什么以及变更的条件是什么的组合

@Override
    public long update(T entity, T where) {
        //        sqLiteDatabase.update(tableName, contentvalue, "name = ?", new String[]{"tgp"});
        int result = -1;
        Map<String, String> values = getValueMap(entity);
        ContentValues contentValues = getContentValues(values);
        //条件map
        Map<String, String> values1 = getValueMap(where);
        Condition condition = new Condition(values1);
        result = sqLiteDatabase.update(tableName, contentValues, condition.whereClause, condition.whereArgs);
        return result;
    }

    private class Condition {
        String whereClause;
        String[] whereArgs;

        Condition(Map<String, String> values) {
            ArrayList<String> list = new ArrayList<>();
            StringBuilder sb = new StringBuilder();
            sb.append("1=1");
            //取得所有成员名
            Set<String> keys = values.keySet();
            for (String key : keys) {
                String value = values.get(key);
                if (value != null) {
                    sb.append(" and ").append(key).append("=?");
                    list.add(value);
                }
            }
            this.whereClause = sb.toString();
            whereArgs = list.toArray(new String[list.size()]);
        }
    }

update有两个参数,第一个参数是要变成什么,第二个对象是用来组成判断条件,Condition只是用来做了一个封装

后面的查询和删除本质上差不多,这里把BaseDao的整个代码贴出来

package com.tgp.dbapp.db;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;

import com.tgp.dbapp.annotation.DbField;
import com.tgp.dbapp.annotation.DbTable;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 建表
 */
public class BaseDao<T> implements IBaseDao<T> {

    private static final String TAG = "BaseDao";

    /**
     * 持有数据库操作的引用
     */
    private SQLiteDatabase sqLiteDatabase;
    /**
     * 表名
     */
    private String tableName;

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

    /**
     * 标记用来表示是否做过初始化操作
     */
    private boolean isInit = false;
    /**
     * 定义一个缓存空间(key 字段名 value 成员变量)
     */
    private HashMap<String, Field> cacheMap;

    public void init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
        this.sqLiteDatabase = sqLiteDatabase;
        this.entityClass = entityClass;

        //*************自动建表*************
        //根据传入的entityClass来建表,只需要建一次
        if (!isInit) {
            //取得表名

            if (entityClass.getAnnotation(DbTable.class) == null) {
                //用类名做表名
                this.tableName = entityClass.getSimpleName();
            } else {
                //取得注解上的名字
                this.tableName = entityClass.getAnnotation(DbTable.class).value();
            }
            //执行建表操作
            String createSql = getCreateTableSql();
            Log.i(TAG, createSql);
            this.sqLiteDatabase.execSQL(createSql);
            cacheMap = new HashMap<>();
            initCacheMap();
            isInit = true;
        }

    }

    private void initCacheMap() {
        //取得所有字段名
        // 查询空表
        String sql = "select * from " + tableName + " limit 1, 0";
        Cursor cursor = sqLiteDatabase.rawQuery(sql, null);
        //获取列名
        String[] columnNames = cursor.getColumnNames();
        //取所有的成员变量
        Field[] declaredFields = entityClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
        }
        //字段跟对象的成员变量一一对应
        for (String columName : 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 (columName.equals(fieldName)) {
                    columnField = field;
                    break;
                }
            }
            if (columnField != null) {
                cacheMap.put(columName, columnField);
            }
        }
    }

    /**
     * 拼接SQL
     */
    private String getCreateTableSql() {
        //create table if not exists tb_user(id integer, name TEXT)
        StringBuilder sb = new StringBuilder();
        sb.append("create table if not exists ");
        sb.append(tableName).append("(");
        //反射得到所有的成员变量
        Field[] declaredFields = entityClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //拿到成员类型
            Class<?> type = field.getType();
            if (field.getAnnotation(DbField.class) != null) {
                if (type == String.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" TEXT,");
                } else if (type == Integer.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" Integer,");
                } else if (type == Long.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" BIGINT,");
                } else if (type == Double.class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" DOUBLE,");
                } else if (type == byte[].class) {
                    sb.append(field.getAnnotation(DbField.class).value()).append(" BLOB,");
                } else {
                    //不支持的类型
                    continue;
                }
            } else {
                if (type == String.class) {
                    sb.append(field.getName()).append(" TEXT,");
                } else if (type == Integer.class) {
                    sb.append(field.getName()).append(" Integer,");
                } else if (type == Long.class) {
                    sb.append(field.getName()).append(" BIGINT,");
                } else if (type == Double.class) {
                    sb.append(field.getName()).append(" DOUBLE,");
                } else if (type == byte[].class) {
                    sb.append(field.getName()).append(" BLOB,");
                } else {
                    //不支持的类型
                    continue;
                }
            }
        }
        //去掉最后的逗号
        if (',' == (sb.charAt(sb.length() - 1))) {
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append(")");
        return sb.toString();
    }

    private ContentValues getContentValues(Map<String, String> map) {
        ContentValues contentValues = new ContentValues();
        for (String key : map.keySet()) {
            String value = map.get(key);
            if (value != null) {
                contentValues.put(key, value);
            }
        }
        return contentValues;
    }

    /**
     * key(字段) value(成员变量)
     */

    private Map<String, String> getValueMap(T entity) {
        HashMap<String, String> map = new HashMap<>();
        //返回的是所有的成员变量
        for (Field next : cacheMap.values()) {
            next.setAccessible(true);
            try {
                //获取对象的属性值
                Object o = next.get(entity);
                if (o == null) {
                    continue;
                }
                //张三, 3
                String value = o.toString();
                String key = "";
                if (next.getAnnotation(DbField.class) != null) {
                    key = next.getAnnotation(DbField.class).value();
                } else {
                    key = next.getName();
                }
                if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
                    map.put(key, value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    @Override
    public long insert(T entity) {
        //        ContentValues contentValues = new ContentValues();
        //        contentValues.put
        //        sqLiteDatabase.insert()
        //准备好ContentValues中需要的数据
        Map<String, String> map = getValueMap(entity);
        //把数据转移到ContentValues中
        ContentValues contentValues = getContentValues(map);
        //数据库操作
        return sqLiteDatabase.insert(tableName, null, contentValues);
    }

    @Override
    public long update(T entity, T where) {
        //        sqLiteDatabase.update(tableName, contentvalue, "name = ?", new String[]{"tgp"});
        int result = -1;
        Map<String, String> values = getValueMap(entity);
        ContentValues contentValues = getContentValues(values);
        //条件map
        Map<String, String> values1 = getValueMap(where);
        Condition condition = new Condition(values1);
        result = sqLiteDatabase.update(tableName, contentValues, condition.whereClause, condition.whereArgs);
        return result;
    }

    private class Condition {
        String whereClause;
        String[] whereArgs;

        Condition(Map<String, String> values) {
            ArrayList<String> list = new ArrayList<>();
            StringBuilder sb = new StringBuilder();
            sb.append("1=1");
            //取得所有成员名
            Set<String> keys = values.keySet();
            for (String key : keys) {
                String value = values.get(key);
                if (value != null) {
                    sb.append(" and ").append(key).append("=?");
                    list.add(value);
                }
            }
            this.whereClause = sb.toString();
            whereArgs = list.toArray(new String[list.size()]);
        }
    }

    @Override
    public int delete(T where) {
//        sqLiteDatabase.delete(tableName, "name = ?", new String[]{"tgp"});
        Map<String, String> values = getValueMap(where);
        Condition condition = new Condition(values);
        int delete = sqLiteDatabase.delete(tableName, condition.whereClause, condition.whereArgs);
        return delete;
    }

    @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, "od = ?", new String[]{}, null, null, "1,5");
        Map<String, String> values = getValueMap(where);
        String limitString = "";
        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> objects = getResult(query, where);
        return objects;
    }

    private List<T> getResult(Cursor query, T where) {
        ArrayList objects = new ArrayList<>();
        Object item = null;
        while (query.moveToNext()) {
            try {
                item = where.getClass().newInstance();
                for (Map.Entry<String, Field> stringFieldEntry : cacheMap.entrySet()) {
                    //获取列名
                    String columnName = stringFieldEntry.getKey();
                    //用列名来拿到列名在游标中的位置
                    int columnIndex = query.getColumnIndex(columnName);
                    Field value = stringFieldEntry.getValue();
                    Class<?> type = value.getType();
                    if (columnIndex != -1) {
                        if (type == String.class) {
                            value.set(item, query.getString(columnIndex));
                        } 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;
                        }
                    }
                }
                objects.add(item);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        query.close();
        return objects;
    }
}

这里说一下,查询结束后返回的集合,就是根据查询的结果又使用反射的方式重新赋值了下

 

 

当然,获取basedao的地方还是在basedao工厂中,这里是BaseDaoFactory的完整代码

package com.tgp.dbapp.db;

import android.database.sqlite.SQLiteDatabase;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 工厂
 */
public class BaseDaoFactory {

    private static BaseDaoFactory sInstance;
    private SQLiteDatabase sqLiteDatabase;
    /**
     * 设计一个数据库的连接池
     */
    protected Map<String, BaseDao> map = Collections.synchronizedMap(new HashMap<String, BaseDao>());
    /**
     * 定义建数据库的路径
     */
    private String sqliteDatabasePath;

    public static BaseDaoFactory getInstance() {
        if (sInstance == null) {
            synchronized (BaseDaoFactory.class) {
                if (sInstance == null) {
                    sInstance = new BaseDaoFactory();
                }
            }
        }
        return sInstance;
    }

    public BaseDaoFactory() {
//        Environment.getExternalStorageDirectory() + File.separator;
        sqliteDatabasePath = "data/data/com.tgp.dbapp/test.db";
        //新建数据库
        sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);
    }

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

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

使用方式也很简单,我们建一个对应的model

@DbTable("tb_person")
public class Person {

    @DbField("_id")
    private Integer id;
    private String name;
    private String password;

    public Person() {

    }

    public Person(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<Person> person = BaseDaoFactory.getInstance().getBaseDao(Person.class);
        person.insert(new Person(1, "tgpperson", "123person"));

通过工厂获取到对象,直接进行增删改查的操作即可

 

假设我们想扩展一下,因为有的数据库操作比较复杂,牵扯到多表联动之类的,我们可以自行扩展

public class BaseDaoImpl<T> extends BaseDao<T> {

    public List<T> query(String sql) {
        return null;
    }
}

我们通过继续BaseDao,来增加方法即可

这里我们工厂中也提供了对应的方法,直接获取到对象

BaseDaoNewImpl baseDao = BaseDaoFactory.getInstance().getBaseDao(BaseDaoNewImpl.class, Person.class);
        Person person = new Person();
        person.setName("tgp update");
        Person where = new Person();
        where.setId(1);
        long update = baseDao.update(person, where);

 

 

用面向对象的方式来操作数据库本质上来说只是进行了一层封装,其实还是进行的sql操作,只是对写代码来说更友好些

 

至于像数据库升级,字段扩展,我们可以通过xml的方式来进行操作,通过从网络获取到最新的xml并进行解析,和当前的版本做比较,因为有可能用户长时间不用这时候你的数据库版本可能已经从1-2-3-4这样,所以xml中对此都需要写出来该怎么进行操作

 

好了,以上文章就是对数据库的封装来达到面向对象方式的使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章