我从 SQLiteOpenHelper 派生的类变得越来越大

Posted

技术标签:

【中文标题】我从 SQLiteOpenHelper 派生的类变得越来越大【英文标题】:My class derived from SQLiteOpenHelper is getting huge 【发布时间】:2014-01-09 05:53:26 【问题描述】:

我从SQLiteOpenHelper 派生的类随着时间的推移变得越来越大。在说话的时候,它是超过 1500 行的代码,这并不被认为是很酷的。可能有一些优雅的解决方案可以防止它增长,即通过和平分开。不知道这些和平是什么。有人说从上述类继承是一种不好的做法,因为它会导致数据库创建/升级过程的不当行为。有什么提示吗?非常感谢!

【问题讨论】:

你应该看看官方日历提供者或联系人提供者的 SQLiteOpenHelpers :-) 仅ContactsDatabaseHelperonUpdate() 方法就大约有 900 行长 - 同时将相关版本更改委托给其他方法.但它们的版本号为 800!整个课程大约有 5000 行代码。所以你不应该感到太抱歉 :-) 更认真:恕我直言只有 onUpgrade() 应该随着时间的推移而增长。没有别的! 【参考方案1】:

是的,开发中显示的示例。该站点确实鼓励创建一个怪物类,您可以在其中硬编码每个表和列的名称。这是一团糟。您可能想要创建一个类来处理每个表。这些类可以充当每个表的Data Access Objects。

这是我用的:

    public interface DataAccessObject<E> 

        public void onCreate(SQLiteDatabase db);

        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);

        public void create(E... params);

        public E[] retrieve(E... params);

        public void update(E... params);

        public void delete(E... params);

    

然后我为每个表编写一个实现。通用的tipe E通常是pojos。请注意,我不会将仅用于保存数据的类 (pojos) 与负责持久检索数据的类 (DAO) 混合在一起。例如,一个 pojo 可以是 Car,带有它的变量(颜色、年份等)。然后我会编写一个扩展 DataAccessObject&lt;Car&gt; 的 CarDAO。而这个 DAO 类负责将 pojo 的变量映射到 DB 列,查询表并写入。

最后,您可以拥有一个注入 DAO 的 SQLiteOpenHelper,并将每个表的内容委托给它们。这些 DAO 实现具有表名和列名常量。如果需要一些复杂的查询,他们可以互相交谈。 (请注意,这也是这种方法的缺点之一:当您需要涉及许多表和列的查询时,产生一个简洁的设计并不简单)。

【讨论】:

完全同意(几乎)。我不确定我是否有基于表格的分离度。我将它基于域对象(实际上可能与表相同,取决​​于)。对此答案 +1。【参考方案2】:

您应该尽可能避免将所有特定于模型的代码放在帮助程序中。将其视为您可以在下一个项目中尽可能多地重用的东西。您可以遵循您将看到的一些常见的数据抽象模式和原则。

例如,您可以考虑Active Record,您的业务对象及其字段和方法,以及与持久性相关的所有方法(从数据库读取和写入)。 此外,您可以考虑一个轻量级对象,并通过提供映射功能的其他对象(例如实体管理器,就像某些 ORM 所做的那样)将实例保存到数据库中并从数据库中检索它们。 您还可以查看Zend TableGateway,它是一种将数据库表表示为对象的酷方法,您可以迁移到 android 和 sqlite。 您可以使用基于 hydrators 的简单而强大的解决方案,如下所述

就个人而言,我更喜欢使用Hydrators

这是一个在一些 ORM 中广泛使用的概念,也在原生 Zend 框架中,以及在其他系统中提供数据持久性,即帮助对象甚至 Web 表单以易于理解和易于理解的方式映射到数据库记录。保持方式。

Hydrator 是一个对象,它将一侧的数据库字段名称映射到另一侧的实体属性。它不在内部存储这些信息,但提供了从数据库创建对象以及从对象中提取数据集以更新数据库的机制。 是否可以像具有列名数组的对象一样简单开始 -> 实体属性,当调用其YourClass hydrate() 方法时,会将相应信息从数据源传输到模型对象,当extract(YourClass yourObject)方法被调用,它将yourObject中包含的数据传输到相应的数据库记录中

我非常喜欢这种方法,因为它非常容易创建一个接口,以及针对常见用例的多个实现。 因此您可以在不影响主要对象或助手的情况下在数据库中执行更改。 此外,使用相同的界面,您可以创建映射器以将数据导出到 json、xml、rest 调用或您可以想象的任何其他内容

如果你可以考虑一个好的 hydrator 设计,然后创建一些类来继承,你可以拥有一个非常小的数据库助手,非常小的实体对象,一些做普通工作的抽象类,以及一些可以获取所有内容的具体 hydrator您可能需要的权重,但从来没有那么多,因为您可以为每个表或对象类型拥有一个,因此这些类显然更小,而且它们只包含与业务级别相关的代码。

【讨论】:

【参考方案3】:

你的助手不需要是那个大小。我只能假设您将所有操作数据的代码都放入了 Helper。

您应该将代码放在与其相关的类中,并且您可以在其中以面向对象的方式访问它。

例如,如果您有一个联系人类。您可以将保存联系人到数据库的代码放入其中。

See my post here

【讨论】:

【参考方案4】:

我按照给定的代码 sn-p:

SQLHelper 类:

    public class SQLHelper extends SQLiteOpenHelper 
        public SQLHelper(Context context, String DBName) 
        
            super(context, DBName, null, DATABASE_VERSION);
        

        public SQLiteDatabase getDBObject(int isWrtitable) 
        
            return (isWrtitable == 1) ? this.getWritableDatabase() : this.getReadableDatabase();
        

        @Override
        public void onOpen(SQLiteDatabase db) 
        
            super.onOpen(db);
            onCreate(db);
        

        @Override
        public void onCreate(SQLiteDatabase db) 
        

            db.execSQL(TABLE_1);
            db.execSQL(TABLE_2);
            ...
            db.execSQL(TABLE_N);
        
    

mysqlManager:负责大部分工作:

    public class MySQLManager 
    
        private SQLHelper sqlHelper;

        //Singlton class

        public void initMySQLManager(Context context, String DBName) 
        
            _context = context;
            sqlHelper = new DBHandler(context, DBName);
        


        public MyObject getMyObjectRecord() 
            MyObject myObj = new MyObject();
            Cursor cursor = null;

            try 
                cursor = dbObject.getWritableDatabase().rawQuery("select *  FROM " + SQLHelper.MYOBJECT_TABLE + ";", null);
                //fetch things
             catch (Exception e) 
                e.printStackTrace();
             finally 
                if (cursor != null && !cursor.isClosed()) 
                    cursor.close();
                
            
            return bookVo;
        

        //Similarly other methods.
    

【讨论】:

【参考方案5】:

您可以考虑使用单例类仅打开和关闭数据库连接(或像池一样捕获、释放),然后使用command pattern 中的命令类执行数据库操作

并且您可以使用facade pattern 将这些命令合并为执行顺序,以进行更复杂的数据库操作。这将使您的代码干净且易于控制。就像当您更改 InsertBookCommand 类时,所有相关操作都会因此而改变行为。

总结一下,我的解决方案是:

打开数据库连接并将数据库作为参数传递给命令。然后执行命令。

【讨论】:

以上是关于我从 SQLiteOpenHelper 派生的类变得越来越大的主要内容,如果未能解决你的问题,请参考以下文章

关于Java的LinkedHashMap中的类变方程式,如何才能使pson的值递增

将自定义委托与从 QTableView 派生的类一起使用

派生自std :: exception的类的赋值运算符

SQLiteOpenHelper

Android继承SQLiteOpenHelper实现

在两者中使用基类和派生类作为数组时的循环