在抽象访问时访问数据库中数据的最佳实践

Posted

技术标签:

【中文标题】在抽象访问时访问数据库中数据的最佳实践【英文标题】:best practice for accessing data in a database while abstracting the accessing 【发布时间】:2012-05-12 13:32:01 【问题描述】:

我正在尝试学习以下场景的最佳实践。

我有一组已定义的数据对象,它们会随着更新而改变。其中一些对象中包含其他对象的数组。

使用sqlite 我使用以下模式设置了数据库:每个对象都在表中。如果一个对象中有一个子对象,它的子表就有一个fk

表名和对象可能会发生变化。因此,为了便于更改这些,我需要能够隐藏数据库调用。我还需要在插入数据库之前验证数据的能力。此验证也因对象而异,并且每个对象可以有不同类型的验证。

目前我正在尝试为每个对象设置一个DAO。并为每个使用ContentProviders 授予数据库访问权限的对象设置一个DatabaseHelper。然后开发人员使用DAO 来完成他们的工作。

它似乎变得非常丑陋和复杂。似乎还有一种更简单的方法......也许ContentProviders有一些技巧。

那么,有没有办法更好地将ContentProvider 集成到DAO 模式中?或者有没有更好的方法来实现这个目标?

非常感谢任何建议。

【问题讨论】:

无论你做什么,这都会让人头疼:) 【参考方案1】:

我最近在 .net 中创建了一个数据访问层。我创建了一个 BusinessObjectBase 类以及一个 BusinessObjectsBase(复数)类。将通用功能转移到这些类中比人们最初想象的更具挑战性。这里有一些提示。

1) 由于 .Net 是一种类型化语言(Java 也是),因此我需要获取有关基类虚函数所操作的派生类的类型信息。为了做到这一点,我使用了 Curiously Recurring Template Pattern (虽然我实际上没有听说过它,直到我自己意识到它的用处):http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern

基本上,它是一个将自己的类型作为泛型参数的泛型类。

2) 我非常依赖反思。不过,我不知道 java 必须提供多少反射方式,或者它是否足够快以对数据访问层有用。我不得不使用我在网上找到的一个免费的 fastflect 库,因为原生反射方法太慢了。

3) 我使用 PostSharp(Java 有 Spring 做同样的事情)来处理更改跟踪,这样它只会在对象实际发生更改时更新它们。

4) 好的,这是最重要的部分:保持简单。尽管我使用了一种奇怪的通用模式、反射和面向方面的编程来完全实现我想要的,但我的 dll 的核心实际上比你想象的要简单。我做了很多研究来寻找完美的orm工具,但发现最终只写一些函数来动态生成我自己的sql语句并不难。这就是反射派上用场的地方,因为我将属性放在表示数据库中表的类上,以及表示表中字段的属性上。这样,您只需在表或字段名称更改时更改属性并且...

5) 我创建了一个简短的应用程序(几乎适合单个页面)来读取数据库表/字段并为每个表动态生成包含类的代码文件。

好的,所以您可能不想创建那么复杂的东西,但我想我会根据自己的经验提出一些想法,也许您会发现其中一个或多个有用:)

(作为旁注,我知道很多人会想:为什么你要经历所有这些麻烦而不是仅仅使用现有的 ORM。我发现现有的 ORM 使用起来过于麻烦,而我的实现是比我研究过的任何 ORM 都更轻量和更快)

【讨论】:

【参考方案2】:

我总是有单独的数据库包名称。我编写了一个单独的数据库类,我在每个项目中都使用它。我只更改数据库名、表名、列名。以下是示例类:

package com.mobisys.android.contactwidget.database;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class ContactDatabase 
    public static final String DATABASE_NAME = "contact.db";
    public static final int DATABASE_VERSION = 1;
    public static final String CONTACT_TABLE_NAME = "contact";

    public static final String KEY_ID = "_id";
    public static final String KEY_ROW = "row";
    public static final String KEY_COL = "col";
    public static final String KEY_APP_WIDGET_ID = "app_widget_id";
    public static final String KEY_CONTACT_IMAGE = "image";
    public static final String KEY_CONTACT_NAME = "name";
    public static final String KEY_CONTACT_NUMBER = "number";
    public static final String KEY_CONTACT_EMAIL = "email";

    private final OpenHelper contactHelper;

    public ContactDatabase(Context context)
        contactHelper=new OpenHelper(context);
    

    public long insert(String table, ContentValues values)
        return contactHelper.getWritableDatabase().insert(table, null, values);
    

    public long delete(String table, String where, String[] whereArgs)
        return contactHelper.getWritableDatabase().delete(table, where, whereArgs);
       

    public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
        return contactHelper.getWritableDatabase().update(table, values, whereClause, whereArgs);
    

    public long countRows(String query)
        return DatabaseUtils.longForQuery(contactHelper.getReadableDatabase(), query, null);
    

    public Cursor query(String table,String[] columns, String selection,String[] selectionArgs,String groupBy,String having,String orderBy)
        return contactHelper.getReadableDatabase().query(table, columns, selection, selectionArgs, groupBy, having, orderBy);
    

    public void close()
        contactHelper.close();
    

    private static class OpenHelper extends SQLiteOpenHelper 

        OpenHelper(Context context) 
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        

        @Override
        public void onCreate(SQLiteDatabase db) 
            db.execSQL("CREATE TABLE "+
                CONTACT_TABLE_NAME+
                " ("+ KEY_ID+" INTEGER PRIMARY KEY AUTOINCREMENT, "+
                KEY_ROW+" INT, "+
                KEY_COL+" INT, "+
                KEY_APP_WIDGET_ID+" INT, "+
                KEY_CONTACT_IMAGE+" BLOB, "+
                KEY_CONTACT_NAME+" TEXT, "+
                KEY_CONTACT_NUMBER+" TEXT, "+
                KEY_CONTACT_EMAIL+" TEXT"+")");
        

        @Override
        public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) 
            String alter_query1="alter table "+CONTACT_TABLE_NAME+" RENAME TO temp1;";
            db.execSQL(alter_query1);

            onCreate(db);

            String insert_query1="insert into "+CONTACT_TABLE_NAME+" select * from temp1;";
            db.execSQL(insert_query1);

            String delete_query1="DROP TABLE temp1;";
            db.execSQL(delete_query1);
        

    

我还创建了一个 HelperDatabase 类,其中包含所有与数据库相关的静态方法。示例类:

package com.mobisys.android.contactwidget.database;

import android.content.ContentValues;
import android.database.Cursor;

import com.mobisys.android.contactwidget.data.CONTACT;

public class HelperDatabase 

    public static long inserContact(CONTACT contact, ContactDatabase database)
        ContentValues values=new ContentValues();
        values.put(ContactDatabase.KEY_APP_WIDGET_ID, contact.app_widget_id);
        values.put(ContactDatabase.KEY_ROW, contact.row);
        values.put(ContactDatabase.KEY_COL, contact.col);
        values.put(ContactDatabase.KEY_CONTACT_NAME, contact.name);
        values.put(ContactDatabase.KEY_CONTACT_NUMBER, contact.cotact_number);
        values.put(ContactDatabase.KEY_CONTACT_EMAIL, contact.email);
        values.put(ContactDatabase.KEY_CONTACT_IMAGE, contact.image);

        long id=database.insert(ContactDatabase.CONTACT_TABLE_NAME, values);
        return id;
    

    public static void updateMyContactInfo(ContactDatabase contactdb, int _id, String number)
        ContentValues values=new ContentValues();
        values.put(ContactDatabase.KEY_CONTACT_NUMBER, number);
        contactdb.update(ContactDatabase.CONTACT_TABLE_NAME, values, "_id"+"="+_id, null);
    

    public static Cursor getContacts(ContactDatabase contactdb, int sort)
        if(sort==1)
            return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, ContactDatabase.KEY_CONTACT_NAME);
        else if(sort==2)
            return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, ContactDatabase.KEY_CONTACT_EMAIL);
        else if(sort==3)
            return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, ContactDatabase.KEY_CONTACT_NUMBER);

        return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, null);
    

    public static boolean isContactExist(ContactDatabase contactdb, String number)
        return contactdb.countRows("SELECT COUNT(*) FROM "+ContactDatabase.CONTACT_TABLE_NAME+" WHERE"+ ContactDatabase.KEY_CONTACT_NUMBER + "='"+number+"'")>0;
       

所以,基本上,我可以为项目中的每个数据库设置一个类和一个 HelperDatabase 类,它执行所有插入、更新、检索和删除功能。

如果我的项目严重依赖数据库,那么最好为您的数据库类设置一个静态对象,该对象将在您的主要活动开始时打开,在您的主要活动将销毁时关闭。

以下是代码示例:

public class HomeActivity extends Activity implements View.OnClickListener
    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        HelperDatabase.contactdb=new ContactDatabase(this);
        startApplication();
    

    @Override
    public void onDestroy()
        HelperDatabase.contactdb.close();
        super.onDestroy();
    

希望对你有所帮助。

【讨论】:

这不是使用ContentProvider。我正在寻找一种将 ContentProvider 集成到 DAO 模式中的方法。这似乎是实现这一目标的一种方式,但我真的很想使用ContentProvider 来实际进行我的数据库调用,并且仍然有一个类似于DAO 模式的抽象层。看来这将是真正实现我在 OP 中提到的目标的唯一方法。

以上是关于在抽象访问时访问数据库中数据的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

将类数据存储在随机访问文件中的最佳实践

DAO(数据访问对象)最佳实践 - 我看到的示例同时使用 DAO 和服务对象,这里的最佳实践是啥?

属性和实例变量的最佳实践

在 Ruby/Rails 中隔离数据访问层的最佳实践

在 iOS 中保存访问令牌的最佳实践

在 Rails 数据库中存储和稍后访问 JSON 对象的最佳实践