如何将Android数据库操作通用化(三)

技术分享

概述

一个不小心都写了三篇了,也不知道大家还看得懂不?如果看不懂最好给我留个言,我好下一次改正。

接着上次的说,准备工作都已经做好了,现在咱们就要开始着手解决阻挡Android数据库操作通用化的五个问题了。

我们先回顾一下问题:
问题1:表名的获取
问题2:如何将实体中的数据,按照对应关系导入到数据库中
问题3:明确实体中主键是谁?获取到主键中封装的值
问题4:如何将数据库中表的列的数据,按照对应关系,封装到实体中
问题5:实体的对象创建

悠悠绿水傍林侵,日落观山四望回。

问题当然是一个一个解决,这样才有效率,那么第一个问题来了:表名如何获取

通常情况下,解决一个问题时,都会以一个方法或者函数开始,这次也不例外,我们先给一个壳子:

/** 问题一:表名的获取 **/
public String getTableName() {
    return null;
}

一切充满了希望,也显得那么自然。现在我们就要开始思考一些关于如何获取表名的问题了。

在实际开发中,每一个数据库表,都会对应着一个具体的实体。就好像news表对应着News类。看看它们,是不是名字好相似?只不过一个是首字母大写,另一个是全小写。于是我们心底这么想,如果我们能够通过“实体”拿到表名就好了

既然有了路走走看,就知道走的通还是走不通了。

我们可以提出两个方案:
① 如果能够获取到实体,那么我们就能够获取到实体的简单名称,然后只需要将首字母小写就是表名了。但是,这样的缺点也很明显,这要求数据库定义表的名称和实体的名称基本一致,这样就会导致我们定义的实体名称收到限制。例如:定义了news表,那么对应的实体就只能是News了。
② 利用注解,这样就可以让实体的名称和数据库表的名称之间的关系脱离,两者互不干涉。缺点也是显而易见,编码难度增加,(嘿嘿),不过谁叫咱们是有经验的开发人员,就选这种了。

如果有同学注解遗忘掉了,或者还不是很清晰,可以参看我的这篇文章:http://blog.csdn.net/biezhihua/article/details/43783165

幽林古寺孤明月,冷井寒泉碧映台。

明确了道路,一路向前就可以了。

接下来就要使用到注解和反射什么的了!!是不是有点小激动~哈哈~

接下来的任务,就是给News实体类增加一个注解了,增加注解的目的则是,将数据库表和对应实体之间的关系确定下来。请看代码:

// 注解的作用:将数据库表和对应的实体确定下来。
@TableName(DBHelper.TABLE_NEWS_NAME)
public class News {
    private int id;
    private String title;
    private String summary;
}

看起来像模像样的,但是@TableName(DBHelper.TABLE_NEWS_NAME)这句是什么? 谁能告诉我?

这句就是我们写的注解了,但是,此时我们还没有创建出来,但是不要紧,如果你使用的是Eclipse,请选中这行Ctrl+1,就可以自动创建了,这么高大上,是不是被震到了!↖(^ω^)↗。请看具体的TableName代码:

/**
 * 制定了实体和数据库中表的对应关系
 */
@Target(ElementType.TYPE) // 指定放置的位置
@Retention(RetentionPolicy.RUNTIME) // 指定存活时间
public @interface TableName {
    /**
     * 数据库中的表名,此处可以存放值
     */
    String value();
}

一切都是这么简单明了,让人心旷神怡。现在,注解已经创建好,实体上的注解也已经添加完毕,并且传入了表名。接下来在getTableName方法中,继续填写代码就好了。

获取表名依旧分为两步:
① 获取到对象的实体 - 这个是问题五
② 获取实体头上的注解,依据value的设置值,确定操作的表。

在伪代码的第一步是获取到对象的实体,经过查看,这个恰好是第五个问题,我们先放着,写一个空方法来代表。

/**
 * 问题五:实体对象的创建
 */
private M getInstance() {
    return null;
}

具体的getTableName()方法代码如下所示:

public String getTableName() {

    // 伪代码:
    // ① 问题五:获取到对象的实体
    M m = getInstance();
    // ② 获取实体头上的注解,依据value的设置值,确定操作的数据库表
    // 需要注意的,想要在“运行时”获取到注解的信息,给注解设置存活时间。
    TableName tableName = m.getClass().getAnnotation(TableName.class); // annotationType 注解的类型
    // 为了安全起见,判断注解的合法性;合法则返回value值
    if (tableName != null) {
        return tableName.value();
    }
    return null;
}

有了这个方法,BaseDaoSupport中的delete方法也就可以写出来了,依旧很简单,请看代码:

@Override
public int delete(Serializable id) {
    return db.delete(getTableName(), DBHelper.TABLE_ID + "=?", new String[] { id.toString() });
}

小结:其实呢,在表名的获取中,我们就是利用注解去解决了一个事,表和实体是一一对应的,它们之间的对应关系是什么。(TableName)

鸥飞满浦渔舟泛,鹤伴闲亭仙客来。

是时候表演真正的技术了!!(╰_╯)#

接下来该解决第二个问题了,如何将实体中的数据,按照对应关系导入到数据库中。

回想一下第一个问题的解决,其实就是利用注解解决了,表和实体之间的对应关系。以此类推,第二个问题也是要求将实体中的数据,按照对应关系导入到数据库中。一样的思路,继续往下延续,可以再为实体的字段一个注解,来体现和数据库表中列的对应关系。请看代码:

/**
 * 制定了实体的子对岸和数据库表中列的对应关系
 */
@Target(ElementType.FIELD) // 指定放置的位置
@Retention(RetentionPolicy.RUNTIME) // 指定存活时间
public @interface Column {
    String value();
}

只要字段上有这个注解的,肯定是和数据库表中的列有对应关系,接下来为实体的字段添加相应的注解,并传入列名。请看代码:

// 注解的作用:将数据库表和对应的实体确定下来。
@TableName(DBHelper.TABLE_NEWS_NAME)
public class News {

    // 指定了实体和数据库中表的对应关系
    @Column(DBHelper.TABLE_ID)
    private int id;

    @Column(DBHelper.TABLE_NEWS_TITLE)
    private String title;

    @Column(DBHelper.TABLE_NEWS_SUMMARY)
    private String summary;
}

准备工作已经做好了,接下来就要填写insert中的代码了,此处新建了一个fillColumn(M m, ContentValues values)方法,把实体M字段值和字段上Column(XXX)注解中传入的列名,用values.put(key,value)方法填入到valeus中,以便insert中填写数据。代码如下:

@Override
public long insert(M m) {
    ContentValues values = new ContentValues();

    // m代表数据源,vlaues是数据导入的目标
    fillColumn(m, values);

    return db.insert(getTableName(), null, values);
}

代码很简单,关键在于fillColumn(m, values);的数据填充。通过使用反射技术,获取到M实例的所有字段集合,再依次拿到每个字段的值和每个字段注解的值,并填充到values中,请看代码:

/**
 * 问题二:如何将实体中的数据,按照对应关系导入到数据库中
 * 
 * @param m 数据源
 * @param values 是数据导入的目标
 */
public void fillColumn(M m, ContentValues values) {

    // 获取m上所有的字段
    Field[] fields = m.getClass().getDeclaredFields();

    for (Field field : fields) {
        // 设置访问权限
        field.setAccessible(true);
        // 获取字段头上的注解
        Column column = field.getAnnotation(Column.class);
        if (column != null) {
            try {
                String key = column.value(); // 获取注解中,指定的列名
                String value = field.get(m).toString(); // 获取字段值
                // 填写数据
                values.put(key, value);
            } catch (IllegalArgumentException e) {
                throw new RuntimeException("字段不属于m实例");
            } catch (IllegalAccessException e) {
                throw new RuntimeException("没有访问字段域的权限");
            }
        }
    }
}

总结:只要掌握了思路和方法,整洁、清晰的代码,写起来也不是那么困难。

游径踏花烟上走,流溪远棹一篷开。

人生不相见,动如参与商.
今夕复何夕,共此灯烛光.
少壮能几时,鬓发各已苍.
访旧半为鬼,惊呼热中肠.
焉知二十载,重上君子堂.
昔别君未婚,儿女忽成行……

技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。