一个不小心都写了三篇了,也不知道大家还看得懂不?如果看不懂最好给我留个言,我好下一次改正。
接着上次的说,准备工作都已经做好了,现在咱们就要开始着手解决阻挡Android数据库操作通用化的五个问题了。
问题当然是一个一个解决,这样才有效率,那么第一个问题来了:表名如何获取。
通常情况下,解决一个问题时,都会以一个方法或者函数开始,这次也不例外,我们先给一个壳子:
/** 问题一:表名的获取 **/
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
方法中,继续填写代码就好了。
在伪代码的第一步是获取到对象的实体,经过查看,这个恰好是第五个问题,我们先放着,写一个空方法来代表。
/** * 问题五:实体对象的创建 */
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("没有访问字段域的权限");
}
}
}
}
总结:只要掌握了思路和方法,整洁、清晰的代码,写起来也不是那么困难。
人生不相见,动如参与商.
今夕复何夕,共此灯烛光.
少壮能几时,鬓发各已苍.
访旧半为鬼,惊呼热中肠.
焉知二十载,重上君子堂.
昔别君未婚,儿女忽成行……