如何在Java中高效地重构SQLite访问?

如何在Java中高效地重构SQLite访问?,java,android,sqlite,refactoring,Java,Android,Sqlite,Refactoring,我正在用Java编写一个Android应用程序,它使用一个包含数十个表的SQLite数据库。我设置了几个数据源类,从这些表中提取数据,并将它们转换为各自的对象。我的问题是,我不知道用Java构造访问数据库的代码的最有效方法 数据源类变得非常重复,需要很长时间来编写。我希望将重复重构为一个父类,该父类将抽象掉访问数据库和创建对象的大部分工作 问题是,我是一个PHP(松散类型)程序员,我很难用严格类型的方式解决这个问题 用PHP思考,我会这样做: public abstract class Data

我正在用Java编写一个Android应用程序,它使用一个包含数十个表的SQLite数据库。我设置了几个数据源类,从这些表中提取数据,并将它们转换为各自的对象。我的问题是,我不知道用Java构造访问数据库的代码的最有效方法

数据源类变得非常重复,需要很长时间来编写。我希望将重复重构为一个父类,该父类将抽象掉访问数据库和创建对象的大部分工作

问题是,我是一个PHP(松散类型)程序员,我很难用严格类型的方式解决这个问题

用PHP思考,我会这样做:

public abstract class Datasource {

    protected String table_name;
    protected String entity_class_name;

    public function get_all () {

        // pseudo code -- assume db is a connection to our database, please.
        Cursor cursor  =  db.query( "select * from {this.table_name}");

        class_name  =  this.entity_class_name;
        entity  =  new $class_name;

        // loops through data in columns and populates the corresponding fields on each entity -- also dynamic
        entity  =  this.populate_entity_with_db_hash( entity, cursor );

        return entity;
    }
}

public class ColonyDatasource extends Datasource {

    public function ColonyDataSource( ) {
        this.table_name  =  'colony';
        this.entity_class_name  =  'Colony';
    }
}
然后,
new ColonyDatasource.get_all()
将获取表colony中的所有行,并返回一组colony对象,为每个表创建数据源就像创建一个只包含表信息到类信息的映射的类一样简单

当然,这种方法的问题是我必须声明我的返回类型,并且不能在Java中使用变量类名。所以现在我被卡住了

我们应该怎么做呢


(我知道我可以使用第三方ORM,但我的问题是,有人可以解决这个问题。)< /P> < P>如果您的查询实际上是相同的,除了某些参数,请考虑使用准备好的语句和绑定


首先,您不想在Java代码中执行以下操作:

class_name  =  this.entity_class_name;
entity  =  new $class_name;
可以按照您的建议进行操作,在Java等语言中称之为反射。

在这种情况下(以及许多情况下),出于许多原因,使用反射来做您想要做的事情是一个坏主意

列出一些:

  • 它很贵
  • 您希望编译器捕获任何错误,尽可能多地消除运行时错误
  • Java的设计并不是为了像鸭子一样嘎嘎叫:
您的代码应该以不同的方式构造,以避免这种类型的方法

遗憾的是,我确实认为,因为它是严格类型化的,所以您无法自动化代码的这一部分:

// loops through data in columns and populates the corresponding fields on each entity -- also dynamic
        entity  =  this.populate_entity_with_db_hash( entity, cursor );
除非你是通过思考来做的。或者完全采用shift方法并开始序列化对象(不推荐,只是说这是一个选项!)。或者做一些类似于Gson的事情。即,将db哈希转换为json表示,然后使用gson将其转换为对象

您可以做的是,自动化抽象类中对象的“get_all”部分,因为这几乎在每个实例中都是重复的,但是使用一个实现,这样您就可以确保抽象函数可以调用其扩展对象的方法。这将为您实现“自动化”方法提供大部分途径,减少您必须重新键入的代码量

要做到这一点,我们必须考虑java有:
  • 泛型()
  • 函数重载
  • Java中的每个对象总是从对象类扩展而来。
    • 非常像利斯科夫
  • 包装范围:
尝试这样的代码(高度未经测试,并且很可能不会编译):

//注意默认范围
接口数据源接口{
//这是为了允许我们的GenericDataSource调用尚未定义的方法。
对象游标TOME(游标游标);
}
//注意我们在这里是如何实现的,但是没有实现函数声明!
公共抽象类GenericDataSource实现DataSourceInterface{
受保护的SQLITE数据库;
//在这里,我们看到泛型和对象是做我们想做的事情的朋友。
//这基本上是说?(通配符)将有一个随机事件列表
//但是我们知道这些随机的东西会从一个物体延伸出来

受保护列表所以我还没有完全探讨的另一个选项是Java持久化API,有一些项目实现了与此非常类似的注释。其中大多数都是以ORM的形式提供数据访问对象()

一个名为“Hibernate”的开源项目似乎是Java中ORM的一个可行解决方案,但我也听说这是一个非常繁重的解决方案,尤其是当你开始考虑移动应用时

一个特定于android的ORM解决方案称为OrmLite(),它基于Hibernate,但是非常精简,没有太多的依赖项,用于将其安装在android手机上


我已经读到,使用其中一个的人会很好地过渡到另一个。

你能展示一些重复写起来很麻烦的查询的例子吗?哪一部分实际上是重复的?我很好奇Android中的惯用方法是什么。肯定有安卓应用程序在本地存储大量数据并显示在多个活动中以相同的方式使用不同的表数据。我还不太熟悉,不知道这种方式是什么,但我有兴趣看到它。TL;DR:我没有以正确的方式考虑这个问题。我怀疑这一点,但我不知道如何在一个更严格类型的世界中思考它。谢谢!
// Notice default scoping
interface DataSourceInterface {
    //This is to allow our GenericDataSource to call a method that isn't defined yet.
    Object cursorToMe(Cursor cursor);
}

//Notice how we implement here?, but no implemented function declarations!
public abstract class GenericDataSource implements DataSourceInterface {
    protected SQLiteDatabase database;

    // and here we see Generics and Objects being friends to do what we want.
    // This basically says ? (wildcard) will have a list of random things
    // But we do know that these random things will extend from an Object
    protected List<? extends Object> getAll(String table, String[] columns){
        List<Object> items = new ArrayList<Object>();

        Cursor cursor = database.query(table, columns, null, null, null, null,null);
        cursor.moveToFirst();
        while (!cursor.isAfterLast()) {
            // And see how we can call "cursorToMe" without error!
            // depending on the extending class, cursorToMe will return 
            //   all sorts of different objects, but it will be an Object nonetheless!
            Object object = this.cursorToMe(cursor);
            items.add(object);
            cursor.moveToNext();
        }
        // Make sure to close the cursor
        cursor.close();
        return items;
    }
}

//Here we extend the abstract, which also has the implements.
// Therefore we must implement the function "cursorToMe"
public class ColonyDataSource extends GenericDataSource {
    protected String[] allColumns = {
        ColonyOpenHelper.COLONY_COLUMN_ID, 
        ColonyOpenHelper.COLONY_COLUMN_TITLE, 
        ColonyOpenHelper.COLONY_COLUMN_URL
    };

    // Notice our function overloading!
    //    This getAll is also changing the access modifier to allow more access
    public List<Colony> getAll(){
        //See how we are casting to the proper list type?
        // Since we know that our getAll from super will return a list of Colonies.
        return  (List<Colony>)super.getAll(ColonyOpenHelper.COLONY_TABLE_NAME, allColumns);
    }


    //Notice, here we actually implement our db hash to object
    // This is the part that would only be able to be done through reflection or what/not
    // So it is better to just have your DataSource object do what it knows how to do.
    public Colony cursorToMe(Cursor cursor) {
        Colony colony = new Colony();
        colony.setId(cursor.getLong(0));
        colony.setTitle(cursor.getString(1));
        colony.setUrl(cursor.getString(2));
        return colony;
    }
}