android sqlite orm

要过年了,最近比较闲整理下以前的代码。

14年写的sqlite orm库,当时为了尽快熟悉android数据库操作,所以自己动手写了这个库。然后用这个库开发了几个项目发现用的还是比较顺手的,于是就写篇博客记录下吧。

我理解的orm的核心思想就是以对象为单位进行增删改查。

app开发给字段命名是很麻烦的一件事,特别是当页面有很多控件的时候,要兼顾接口开发人员命名风格和页面快速辨认需求,如果orm再要求字段名还要考虑到数据表字段命名的问题就太麻烦了,于是我使用了反射来实现模型字段和数据表字段的对应。

public class SQLiteUtil {
    private SQLiteDatabase db;
    private Context context;

    /**
     * @param dbName
     * @param context
     */
    public SQLiteUtil(String dbName, Context context) {
        this.context = context;
        //打开或创建数据库
        db = this.context.openOrCreateDatabase(dbName, Context.MODE_PRIVATE, null);
    }

    /**
     * 建表语句,如果表不存在则建表
     *
     * @param table 表结构,示例 tableName(field1 text primary key, field2 integer)
     */
    public void createTable(String table) {
        String cts = "create table if not exists " + table + "; ";//建表语句
        db.execSQL(cts);
    }

    /**
     * 删除表
     *
     * @param table 表名
     */
    public void deleteTable(String table) {
        String sql = "DROP TABLE IF EXISTS " + table;
        db.execSQL(sql);
    }

    /**
     * 执行sql命令
     *
     * @param sql
     */
    public void execSql(String sql) {
        db.execSQL(sql);
    }

    /**
     * 增加一条新的数据
     */
    public <T> void insert(String tableName, T data, Class<T> clazz) {
        String sql = "insert into " + tableName + " ";
        String fields = "( ";
        String value = " VALUES( ";

        for (Field f : clazz.getDeclaredFields()) {
            f.setAccessible(true);
            if (null != f.getAnnotation(SqlField.class)) {
                SqlField ta = f.getAnnotation(SqlField.class);
                fields += ta.field() + ",";
                try {
                    if (f.getType().getSimpleName().equals("int")) {
                        value += f.getInt(data) + ",";
                    } else if (f.getType().getSimpleName().equals("String")) {
                        value += "'" + f.get(data).toString() + "',";
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        fields = fields.substring(0, fields.length() - 1) + ") ";
        value = value.substring(0, value.length() - 1) + ") ";

        sql = sql + fields + value;
        StaticMethod.debugEMSG(sql);
        db.execSQL(sql);
    }

    /**
     * 修改数据
     */
    public <T> void update(String tableName, String where, T data, Class<T> clazz) {
        String sql = "UPDATE " + tableName + " SET ";
        String set = "";

        for (Field f : clazz.getDeclaredFields()) {
            f.setAccessible(true);
            if (null != f.getAnnotation(SqlField.class)) {
                SqlField ta = f.getAnnotation(SqlField.class);
                try {
                    if (f.getType().getSimpleName().equals("int")) {
                        set += " " + ta.field() + " = " + f.getInt(data) + ",";
                    } else if (f.getType().getSimpleName().equals("String")) {
                        if (f.get(data).toString() != null)
                            set += " " + ta.field() + " = '" + f.get(data).toString() + "',";
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        set = set.substring(0, set.length() - 1);

        sql = sql + set + where;
        db.execSQL(sql);
    }

    /**
     * 查询方法,返回对应的数组
     *
     * @param sql
     * @param <T> 泛型对象,必须提供一个空白构造函数
     * @return
     */
    public <T> List<T> query(String sql, Class<T> clazz) {
        List<T> result = new ArrayList<T>();
        Cursor c = db.rawQuery(sql, null);
        try {
            while (c.moveToNext()) {
                T temp = (T) Class.forName(clazz.getName()).newInstance();
                for (Field f : clazz.getDeclaredFields()) {
                    f.setAccessible(true);
                    if (null != f.getAnnotation(SqlField.class)) {
                        SqlField ta = f.getAnnotation(SqlField.class);
                        if (f.getType().getSimpleName().equals("int")) {
                            f.set(temp, c.getInt(c.getColumnIndex(ta.field())));
                        } else if (f.getType().getSimpleName().equals("String")) {
                            f.set(temp, c.getString(c.getColumnIndex(ta.field())));
                        }
                    }
                }
                result.add(temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            c.close();
        }
        return result;
    }

    /**
     * 查
     *
     * @param sql       查询语句
     * @param fieldName 要返回的字段名称
     * @return 返回字符串内容
     */
    public List<String> query(String sql, String fieldName) {
        Cursor c = db.rawQuery(sql, null);
        List<String> result = new ArrayList<String>();
        while (c.moveToNext()) {
            result.add(c.getString(c.getColumnIndex(fieldName)));
        }
        c.close();
        return result;
    }
}

  没有删除方法,实际开发中删除操作都是业务比较复杂的,我都是在封装好sql语句后直接调用execSql(sqlString)来实现删除。

  查询有1个重载,是因为在开发时有比较多的业务场景只需要储存和读取一张表中的某个字段,为了这个字段实现一个model太麻烦,所以有了重载方法。

  增查改中都使用了反射,sql语句也没有优化。因为从实际开发角度来看,以app的数据库里的数据量级,做不做优化效果区别不大,所以可以放心食用。

  贴一下Annotation的代码

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

  annotation使用例子

   @SqlField( field = "field1")
    public String key;
    @SqlField( field = "field2")
    public String json;

  例如,执行查询的时候,orm会从数据表中取出field1的值,并赋值给key字段。取出field2字段的值并赋值给json字段。

  最后再来说一下我的使用经验,一般我会根据业务需求对orm再次封装。例如做缓存时,内存缓存,SharedPreferences缓存和sqlite缓存以及文件缓存,都要根据业务来配置,其中sqlite缓存就可以通过二次封装这个orm来实现。

     下面是我的sqlite缓存实现代码

public class SqliteCache {

    public static void clean(String key) {
        String sql = "delete from " + StaticConstant.SQLCACHE + " where  key='" + key + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }

    public static void save(CacheModel cache) {
        String sql = "select key from " + StaticConstant.SQLCACHE + " where key='" + cache.key + "'";
        if (StaticVariable.mSqLiteOper.query(sql, "key").size() > 0) {
            StaticVariable.mSqLiteOper.update(StaticConstant.SQLCACHE,
                    " where key='" + cache.key + "'",
                    cache,
                    CacheModel.class);
        } else {
            StaticVariable.mSqLiteOper.insert(StaticConstant.SQLCACHE,
                    cache,
                    CacheModel.class);
        }
    }

    public static CacheModel read(String key) {
        String sql = "select * from " + StaticConstant.SQLCACHE + " where key='" + key + "'";
        List<CacheModel> temp = StaticVariable.mSqLiteOper.query(sql, CacheModel.class);
        if (temp.size() > 0)
            return temp.get(0);
        else
            return null;
    }
}

  sqlite缓存类都是根据业务来写的,不同app有不同业务,业务也在随时变化,所以这个类仅供参考。

  既然都写到缓存了,顺便就把我另一个例子也存档一下吧。

  推送是大部分app必备的功能,而一个体验良好的app,推送到达app端后是持久存在的,直到被用户消费掉,例如微信的红点未读提醒。

  这就需要实现推送消息的缓存,而当推送消息类型很多时,缓存管理就十分麻烦,这时就必须使用数据库来管理推送缓存。

  

public class PushMsgCache {

    /**
     * 首页已读(任务通知里面)
     */
    public static void read1(String uid) {
        String sql = "UPDATE " + StaticConstant.NOTICECACHE + " SET state = 2 WHERE state=1 and " +
                " type!='" + StaticConstant.微信对话 + "' and " +
                " type!='" + StaticConstant.系统通知 + "' and " +
                " type!='" + StaticConstant.销售新用户 + "' and userid = '" + uid + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }

    /**
     * 特定通知1级已读设置
     */
    public static void read1(String uid, String msgType) {
        String sql = "UPDATE " + StaticConstant.NOTICECACHE +
                " SET state = 2 WHERE state in(1,2) and userid = '" + uid + "' and type='" + msgType + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }

    /**
     * 2级通知页面已读
     */
    public static void read2(String uid, String msgType) {
        String sql = "UPDATE " + StaticConstant.NOTICECACHE +
                " SET state = 3 WHERE state in(1,2) and userid = '" + uid + "' and type='" + msgType + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }

    /**
     * 详情已读
     */
    public static void read3(String uid, String msgType) {
        String sql = "UPDATE " + StaticConstant.NOTICECACHE +
                " SET state = 4 WHERE state in(1,2,3) and userid = '" + uid + "' and type='" + msgType + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }


    /**
     * 微信列表已读
     */
    public static void readwechatlist(String uid) {
        String sql = "UPDATE " + StaticConstant.NOTICECACHE + " SET state = 2 WHERE state=1 and " +
                " type='" + StaticConstant.微信对话 + "' and userid = '" + uid + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }

    /**
     * 微信消息已读
     */
    public static void readwechat(String uid, String cid) {
        String sql = "UPDATE " + StaticConstant.NOTICECACHE +
                " SET state = 3 WHERE state in(1,2) and userid = '" + uid + "' and targetid='" +
                cid + "' and type='" + StaticConstant.微信对话 + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }

    /**
     * 跟进详情回复已读
     */
    public static void readgjxqhf2(String uid, String cid) {
        String sql = "UPDATE " + StaticConstant.NOTICECACHE +
                " SET state = 3 WHERE state in(1,2) and userid = '" + uid + "' and targetid='" +
                cid + "' and type='" + StaticConstant.销售新用户 + "'";
        StaticVariable.mSqLiteOper.execSql(sql);
    }

    /**
     * 推送消息入库
     *
     * @param json 推送过来的消息对象的json
     */
    public static void save(String json) {
        Gson gson = new Gson();
        NoticeCacheModel model = new NoticeCacheModel();
        PushReceiverModel push = gson.fromJson(json, PushReceiverModel.class);
        model.setType(push.getMessageType());
        model.setMsg(gson.toJson(push));
        model.setTime(StaticMethod.getNowTimeStamp() + "");
        model.setTargetid(push.getUid() + "");
        if (push.getAdvid() != 0) {
            model.setUserid(push.getAdvid() + "");
        } else {
            model.setUserid(push.getAdvisorid() + "");
        }
        model.setState(1);
        StaticVariable.mSqLiteOper.insert(StaticConstant.NOTICECACHE, model, NoticeCacheModel.class);
    }

    /**
     * 读取推送消息
     *
     * @param pageName 取数据的页面名称
     */
    public static List<PushReceiverModel> query(String pageName) {
        String sql = "";
        switch (pageName) {
            case "xsgw_main"://查找所有未读的推送信息
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state=1 and" +
                        " type!='" + StaticConstant.微信对话 + "' and " +
                        " type!='" + StaticConstant.抢客户 + "' and " +
                        " type!='" + StaticConstant.系统通知 + "' and " +
                        "type!='" + StaticConstant.销售新用户 + "'" +
                        "order by time desc";
                break;
            case "xsgw_msgbox":
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state in(1,2) order by time desc";
                break;
            case "xsjl_main":
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state=1 and" +
                        " type!='" + StaticConstant.微信对话 + "' and " +
                        " type!='" + StaticConstant.抢客户 + "' and " +
                        " type!='" + StaticConstant.系统通知 + "' and " +
                        "type!='" + StaticConstant.销售新用户 + "'" +
                        "order by time desc";
                break;
            case "xsjl_msgbox":
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state in(1,2)  order by time desc";
                break;
            case "wechat":
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state in(1,2) and type='" + StaticConstant.微信对话 + "' order by time desc";
                break;
            case "xsgw_main_activity":
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state=1 order by time desc";
                break;
            case "newnotice":
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state in(1,2,3) and type='" + StaticConstant.经理通知 + "'";
                break;
            case "system"://未读系统通知
                sql = "select * from " + StaticConstant.NOTICECACHE + " where userid='" +
                        StaticVariable.uid + "' and state in(1,2) and type='" + StaticConstant.系统通知 + "'";
                break;
            default:
                return null;
        }
        List<NoticeCacheModel> temp = StaticVariable.mSqLiteOper.query(sql, NoticeCacheModel.class);
        if (temp.size() > 0) {
            Gson gson = new Gson();
            List<PushReceiverModel> result = new ArrayList<>();
            for (NoticeCacheModel item : temp) {
                result.add(gson.fromJson(item.getMsg(), PushReceiverModel.class));
            }
            return result;
        } else
            return null;
    }

}

  对推送消息的处理,我采用json来存放到数据库中,使用gson来完成数据的序列化和反序列化。

  这个类里面的核心方法是save(消息入库)和query(未读消息查询),readxxx系列方法则是根据不同业务产生的消息被消费方法,作用是把消息置为已消费。

  因为我目前开发都是小团队模式,1-5人之内,对代码的抽象需求不大,所以写类的时候我更多的是考虑代码易读和修改便捷,觉得水平太差的请轻喷.

原文地址:https://www.cnblogs.com/sun-yang-/p/5148910.html