-
-
- 概述
- 悠悠绿水傍林侵日落观山四望回
- 幽林古寺孤明月冷井寒泉碧映台
- 鸥飞满浦渔舟泛鹤伴闲亭仙客来
- 游径踏花烟上走流溪远棹一篷开
-
概述
一个不小心都写了三篇了,也不知道大家还看得懂不?如果看不懂最好给我留个言,我好下一次改正。
接着上次的说,准备工作都已经做好了,现在咱们就要开始着手解决阻挡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("没有访问字段域的权限");
}
}
}
}
总结:只要掌握了思路和方法,整洁、清晰的代码,写起来也不是那么困难。
游径踏花烟上走,流溪远棹一篷开。
人生不相见,动如参与商.
今夕复何夕,共此灯烛光.
少壮能几时,鬓发各已苍.
访旧半为鬼,惊呼热中肠.
焉知二十载,重上君子堂.
昔别君未婚,儿女忽成行……