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

概述

在开始考虑Android的数据库操作之前,我们先回想一下Web方面的数据库操作。如果我们只是停留在JDBC的简单使用和封装上(比如纯JDCB,或者DBUtils),即使我们对数据库的增删改查操作进行了接口的抽取,代码依旧会和业务有很强的耦合性。

经过我们分析,解除耦合性的关键在于如何解决自动映射“实体类 与 数据库表”之间的对应关系。如果能够做到这一步,那么我们就能够更好的解耦了,也能降低我们的代码重复率。

如果我们再跨前一步,使用更为优秀的框架(比如:Hibernate),这一切都会变得简单和方便。Hibernate为我们提供了两种建立“实体类(bean) - 数据库表”之间关系的方式。

一种是使用配置文件,在配置文件中对表名、字段、实体间的关系进行配置,Hibernate就能够为我们做好一切,下面提供一段配置文件示例:

<hibernate-mapping package="com.bzh.gt.bean">
    <class name="Apartment">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" length="10" not-null="true"/>
        <property name="rank" not-null="true"/>
        <property name="sex" not-null="true"/>
        <property name="topFloor" not-null="true"/>
        <property name="roomNumber" not-null="true"/>
        <property name="bedNumber" not-null="true"/>

        <!-- dormitories属性,本类与Dormitory的一对多关系 -->
        <set name="dormitories" inverse="true" lazy="extra" fetch="select" cascade="all-delete-orphan">
            <key column="apartmentId"></key>
            <one-to-many class="Dormitory"/>
        </set>
    </class>
</hibernate-mapping>

另一种,则是使用注解的方式。Hibernate为我们提供了@Entity@Table@Id@Column@GeneratedValue诸如此类很多的注解。我们只需要在合适的地方加上这些注解,为我们的类、字段配置好关系,Hibernate也能够为我们做好一切,下面提供一段配置文件示例:

@Entity(name="brand") /* 实体bean */
@Table(name="brand_t") /* 指定生成表的名字*/
public class Brand implements Serializable {

    /** @Fields code : ID使用UUID生成 **/
    private String code;
    /** @Fields name : 品牌名称 **/
    private String name;
    /** @Fields visible : 是否可见 **/
    private Boolean visible = true;
    /** @Fields logopath : logo路径 **/
    private String logopath;

    public Brand(String name, String logopath) {
        this.name = name;
        this.logopath = logopath;
    }
    @Id
    @Column(name="code", length=36) /* UUID是36 */
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    @Column(length=40, nullable=false)
    public String getName() {
        return name;
    }
    @Column(nullable=false)
    public Boolean getVisible() {
        return visible;
    }
    /*/images/brand/2012/12/8/9/sss.gif */
    @Column(length=80,nullable=true)
    public String getLogopath() {
        return logopath;
    }

}

番外话说完,回到我们热爱的Android中,我们应该如何在Android写出一个通用的数据库增删改查操作,可能也需要借鉴Hibernate的优秀思想。

不过,世间万物都是一步一步进行演化的,我们的代码同样是如此,从不通用到通用,我会通过几篇博客一一向大家展示,但是由于本人经验和学识有限,无法构建出一个完整的开源项目,望大家海涵。有兴趣的可以联系我大家一起尝试做一下(每当你在感叹,如果有这样一个东西就好了的时候,请注意,其实这是你的机会)。

小小说明

整个例子会以一条新闻实体作为基本元素,并贯穿整个例子。新闻实体很简单,我们给它起个名字叫做News,字段由idtitlesummary构成。以下是示例代码:


public class News {

    private int id;

    private String title;

    private String summary;

    // 此处省略get、set方法
}

另外,在SQLite数据库中,存东西绝大多数是String类型的,只有涉及到主键、自增的才会使用int。

最后,想章节名太痛苦,就在网上找了一些有趣的诗词作为章节了,大家按顺序看哦。

一别之后,二地相悬。

实体对象我们已经有了,那么现在就差数据库表了。现在回想一下Android中,数据库表的操作步骤,继承SQLiteOpenHelper,修改一些构造函数,再在onCreate中使用db执行一下建表语句,好像就完成了(数据库版本升级后续再讲)。请看示例代码:

public class DBHelper extends SQLiteOpenHelper {

    private static final int VERSION = 1;
    private static final String NAME = "bzh.db";

    public DBHelper(Context context) {
        super(context, NAME, null, VERSION);
    }

    // 新闻表:主键+标题+摘要
    public static final String TABLE_ID = "_id";
    public static final String TABLE_NEWS_NAME = "news";
    public static final String TABLE_NEWS_TITLE = "title";
    public static final String TABLE_NEWS_SUMMARY = "summary";

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TABLE_NEWS_NAME + "( " + //
                TABLE_ID + " integer primary key autoincrement , " + //
                TABLE_NEWS_TITLE + " varchar(50) ," + //
                TABLE_NEWS_SUMMARY + " varhcar(200))"//
        );

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

}

仔细看一下代码,其中我们把各个字段和表名都抽取成为了常量字段,这样可以更方便我们写数据库增删改查操作的时候使用。

都说是三四月,谁又知五六年。

现在,实体和表都有了,是时候写增删改查的操作了。既然是增删改查,那么肯定有对应的insertdeleteupdatefindAll等操作咯。

而我们作为有经验的开发人员(偷笑),肯定是要面向接口编程咯,那就先写一个NewsDao吧,整体上很简单,就不写注释了。代码如下:

public interface NewsDao {
    long insert(News news);
    int delete(int id);
    int update(News news);
    List<News> findAll();
}

看起来一切都很顺利,下面是时候写Dao的实现类了,我们就叫做NewsDaoImpl吧,并实现NewsDao接口。代码如下:

public class NewsDaoImpl implements NewsDao {

    private Context context;
    private DBHelper helper;
    private SQLiteDatabase db;

    public NewsDaoImpl(Context context) {
        super();
        this.context = context;
        this.helper = new DBHelper(context);
        this.db = helper.getWritableDatabase();
    }

    @Override
    public long insert(News news) {
        return 0;
    }

    @Override
    public int delete(int id) {
        return 0;
    }

    @Override
    public int update(News news) {
        return 0;
    }

    @Override
    public List<News> findAll() {
        return null;
    }
}

貌似架子就是这个样子了,为了执行增删改查等语句,我们需要一个之前写的DBHelper实例,而DBHelper又要求传入Context对象。然后DBHelper又给了我们一个操作数据库SQLiteDatabase实例。

一切很顺利,不过增删改查方法都是空着的,看来接下来只需要填空就好了。

七弦琴无心弹,八行书不可传。

既然是填空,那么我们就只能一个一个来了,太麻烦了(怪不得程序员喜欢偷懒,喵喵!)。

就先从insert开始吧。

@Override
public long insert(News news) {
    ContentValues values = new ContentValues();
    values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
    // ... 此处省略N行代码
    return db.insert(DBHelper.TABLE_NEWS_NAME, null, values);
}

当然,我这些只是示例性的代码,你看省略的好多呢,整个insert的内容一点也不复杂。首先,把字段名和实体字段的对应关系存一个一个入ContentValues中,然后再执行一下db.insert,并填入要插入的表名和数据,搜一的一下,就成功了。

接下来是delete,这么貌似更容易了。在delete中指定表的名称、过滤条件、以及过滤条件对应的参数,就OK了。

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

下面是update方法。为更新准备一下数据,在update填入对应的表名、更新值、过滤条件、以及过滤条件对应的参数,这就完成了。当然,为了偷懒,还是省略了一下代码。我保证下个肯定写完整。

@Override
public int update(News news) {
    ContentValues values = new ContentValues();
    values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
    // ... 此处省略N行代码
    return db.update(DBHelper.TABLE_NEWS_NAME, values, DBHelper.TABLE_ID + "=?", new String[] { news.getId() + "" });
}

轮到findAll方法了,这个比较重要,大家仔细听。我们通过db.query()查询数据,并获取到游标(不要忘记关闭哦),然后就开始移动游标,拿出数据并封装到News实体中,然后再一个一个加入到List中。好吧,我承认,这里又偷懒了,嘿嘿。

@Override
public List<News> findAll() {
    List<News> result = null; // List<M>
    Cursor cursor = db.query(DBHelper.TABLE_NEWS_NAME, null, null, null, null, null, null);
    if (cursor != null) {
        result = new ArrayList<News>();
        while (cursor.moveToNext()) {
            News news = new News(); // M m  = new M();
            int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_NAME);
            String title = cursor.getString(columnIndex);
            news.setTitle(title);
            // ... 此处省略N行代码

            result.add(news);
        }
    }

    // 关闭游标
    cursor.close();
    return result;
}

一步一步下来,貌似一个有关News实体的增删改查的Dao就出炉了,但是,作为一名程序员,写一个这样子的还是可以承受的,那么万一有更多的UserAAABBBCCC 等等出现怎么办?

难道还要写出来一大堆UserDaoUserDaoImplAAADaoAAADaoImpl等等的一大堆类和方法? 老天,你还是杀了我吧。

那么,问题来了,上述的代码需要解决什么问题,才能变成通用的?

九连环从中折断,十里长亭望眼欲穿!

接下来我们从insertdeleteupdatefindAll这些方法中依次分析,看看是哪些问题阻挡了我们通用化的脚步。

insert中,

ContentValues values = new ContentValues();
values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
    // ... 此处省略N行代码

如果这些代码能够,以一种“自动”的方式,那么我们的双手就算解放出来一步了,我们需要面对的问题是:如何将实体中的数据,按照对应关系导入到对应的表之中。

return db.insert(DBHelper.TABLE_NEWS_NAME, null, values);

再看看这句,好像在Java中,写死就等于耦合、等于不方便。而DBHelper.TABLE_NEWS_NAME好像也固定在语句之中了,看来这句代码也阻挡了我们解放双手的步伐,嘿嘿,又出来一个。我们需要面对的问题是:如何获取表名。

这个方法是没有了,接下来看看delete

return db.delete(DBHelper.TABLE_NEWS_NAME, DBHelper.TABLE_ID + "=?", new String[] { id + "" });

其中的DBHelper.TABLE_NEWS_NAME我们已经记录下来了,那么,后面的DBHelper.TABLE_ID又是怎么回事? 哦,原来是主键,也是固定写死的。看来这也是我们面对的问题:明确实体中的主键是谁,并获取到主键中封装的值。

再看看update方法,仔细瞅瞅~ 好像没有耶!那么就继续下一个findAll方法。

News news = new News(); // M m  = new M();
int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_NAME);
String title = cursor.getString(columnIndex);
news.setTitle(title);
// ... 此处省略N行代码

这里是将游标中的数据取出来,并放入到对应的实体属性中去,这里最容易出现错误、并且也是写死的。看来这里也是问题之一:将数据库表中列的数据,按照对应关系导入到实体之中。

分析完后,我们已经可以总结出一二三四五六七八条了。哈哈。(貌似没有这么多。)

披荆斩棘

需要解决什么问题,才能变成通用的?
① 表名的获取
② 如何将实体中的数据,按照对应关系导入到对应的表之中。
③ 明确实体中的主键是谁,并获取到主键中封装的值。
④ 搞定实体对象的创建
⑤ 将数据库表中列的数据,按照对应关系导入到实体之中。

以上就是全部问题所在了,如果能够搞定这些,在Android中的数据库通用操作也就应运而生了。

时间是怎样一种东西,它能改变一切、带走一切、更可留下一切。昨天仿佛还在眼前,今天却悄悄过去,我们的生命总是那么有限,那些属于你我的年轻亦是如此缥缈,拥有时并无察觉,待往事已成过眼云烟,方才了解自己荒废了那大好的时光,岁月仍在,流星划过的刹那,却忘记在心中默默许下那卑微的心愿。

时间: 2024-10-29 00:25:29

如何将Android数据库操作通用化(一)的相关文章

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

概述 悠悠绿水傍林侵日落观山四望回 幽林古寺孤明月冷井寒泉碧映台 鸥飞满浦渔舟泛鹤伴闲亭仙客来 游径踏花烟上走流溪远棹一篷开 概述 一个不小心都写了三篇了,也不知道大家还看得懂不?如果看不懂最好给我留个言,我好下一次改正. 接着上次的说,准备工作都已经做好了,现在咱们就要开始着手解决阻挡Android数据库操作通用化的五个问题了. 我们先回顾一下问题: 问题1:表名的获取 问题2:如何将实体中的数据,按照对应关系导入到数据库中 问题3:明确实体中主键是谁?获取到主键中封装的值 问题4:如何将数据

数据库操作通用类

DataBase.java 说明: 1. 此类包含对数据库的查询,删除,更新操作.     2. 可以实现对大文本对象的增删改.     3. 利用自建的数据库连接池类, 得到数据库连接.     4. 可以利用Tomcat 自带的连接池, 得到数据库连接 变量:     1. 数据库连接 变量名     : conn 应用范围   : protect 变量类型   : Connection 数据库连接 初始值     : null 是否Static :  否     2. 声明语句 变量名  

Android数据库操作_表格显示

Android数据库操作_表格显示 显示表格布局 完成后效果如下: 首先需要一个主布局文件main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation=&quo

android菜鸟学习笔记20----Android数据存储(四))Android数据库操作

Android内置了一个名为SQLite的关系型数据库,这是一款轻量型的数据库,操作十分简便.SQLite与别的数据库不同的是,它没有数据类型.可以保存任何类型的数据到你所想要保存的任何表的任何列中.但它又支持常见的类型比如: NULL, VARCHAR, TEXT, INTEGER, BLOB, CLOB...等. 唯一的例外是:integer primary key 此字段只能存储64位整数. 在JAVA项目中,要使用JDBC操作数据库需要加载数据库驱动,连接数据库等操作.Android简化

C#数据库操作通用方法类

平时使用的数据库操作类整理更新后备份,记录下来以供以后使用,并顺便分享给大家一起交流.直接上源码: 整个底层操作方法分为4个类,基础方法类:SqlBase 基础工具类:SqlTool 日志类:DbLog  和MSSQL操作类:MsSqlHelper. 由于平时工作未用到其他类型数据库,因此未整理其他数据库的操作类,以后用到的话会进行更新. 首先是通用的数据库底层操作方法整理: /// <summary> /// 数据库操作基础方法类 /// </summary> public cl

android数据库操作之直接读取db文件

在对数据库操作时,常用的有两种方法: 1.在代码中建库.建表: 2.直接将相关库.表建立好,将db文件拷贝至assets目录下: 现在来看看第二种方法: private String GetDataBasePath(Context context) { String packageName = context.getPackageName(); //Log.i("PackName", packageName); // String DB_PATH = String.format(&qu

android数据库操作

package cn.hackcoder.beautyreader.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; /** * Created by hackcoder on 15-1-25. */ public class Data

android 数据库操作详解

请看郭大神的八篇专栏,包含sql语句  android封装的databasehelper 和郭大神自己的LitePal  三种使用详解 http://blog.csdn.net/column/details/android-database-pro.html

数据库操作通用方法类

public class DbMethods { /// <summary> /// 获取数据库 /// </summary>zlf 2014-12-11 /// <param name="dbCode">用户:u 项目:p 捐赠:d 公共:c 活动:a 日志:l</param> public static Database GetDb(string dbCode) { Database db = null; switch (dbCode