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

时间:2013-12-20 11:15:04

标签: android database design-patterns

SQLiteOpenHelper派生的我的课程在一段时间内变得越来越大。在发言时间,超过1500行代码不被认为是酷的。可能有一些优雅的解决方案来阻止它的成长,即通过和平分离。不确定那些和平是什么。有些人说继承上述类是一种不好的做法,因为它会导致数据库创建/升级过程的不当行为。任何提示?非常感谢!

5 个答案:

答案 0 :(得分:11)

是的,开发中显示的示例。 site确实鼓励创建一个怪物类,你可以对每个表和列的名称进行硬编码。这是一团糟。您可能想要创建一个类来处理每个表。这些类可以作为每个表的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);

    }

然后我为每个表编写一个实现。通用条纹E通常是pojos。请注意,我并没有将用于保存数据(pojos)的类与负责持久检索数据(DAO)的类混合在一起。例如,pojo可以是Car,其变量(颜色,年份等)。然后我编写一个扩展DataAccessObject<Car>的CarDAO代码。这个DAO类负责将pojo的变量映射到DB列,查询表并写入它。

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

答案 1 :(得分:8)

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

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

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

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

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

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

如果你能想到一个好的保水器设计,然后创建一些继承的类,你可以拥有一个非常小的数据库帮助器,非常小的实体对象,一些抽象类做一些普通的工作,以及一些可以得到所有的具体的水合器您可能需要的权重,但从来没有那么多,因为您可以为每个表或对象类型使用一个,因此类很小,而且,它们只包含在业务级别相关的代码。

答案 2 :(得分:6)

你的助手不应该那么大。我只能假设您将所有操作数据的代码放入Helper中。

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

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

See my post here

答案 3 :(得分:6)

我按照给定的代码段进行操作:

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.
    }

答案 4 :(得分:2)

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

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

总而言之,我的解决方案是:

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