适用于 Android 的 Ormlite:设计帮助等

发布于 2024-12-28 16:35:14 字数 6965 浏览 1 评论 0原文

我开始开发Android应用程序已经几个月了。我已经使用 Spring 框架和 Hibernate 工作了大约两年。因此,在寻找适用于 Android 的 ORM 工具时,我找到了 ormlite 项目。我发现它非常有趣,我决定在我的应用程序中使用它。

一切似乎都工作正常,但我会向专家询问一些关于使用 ormlite 以高效且(如果可能)优雅的方式开发 Android 应用程序的技巧!

让我们从域类开始。

所有域类都实现一个假的 DomainObject 接口:

@DatabaseTable(tableName = "job")
public final class  Job implements DomainObject{

    @DatabaseField(generatedId = true, columnName="_id")
    public Integer id;

    @DatabaseField(index=true)
    public Integer remoteDbid;

    @DatabaseField
    public Date downloadDate;

    @DatabaseField
    public Date assignedDate;
     .....
}

然后我有一个通用的 Dao 接口,

public interface GenericDao <T extends DomainObject> {
    T queryForId(Integer id);
    List<T> queryForAll();
    void save(T object);
    void merge(T object);   
    void delete(T object);
}

其实现方式是:

public class GenericDaoORM <T extends DomainObject> extends OrmLiteSqliteOpenHelper implements GenericDao<T>{

    private static final String LOG_TAG = GenericDaoORM.class.getSimpleName();

    private final static int DATABASE_VERSION = 7;

    private final Class<T> type;
    protected Dao<T, Integer> dao = null;

    protected GenericDaoORM(final Context context, final Class<T> type) {
        super(context, FileConfig.DB_PATH, null, DATABASE_VERSION);
        this.type = type;
        if (dao == null) {
            try {
                dao = getDao(type);
            } catch (SQLException e) {
                if (LogConfig.ENABLE_ACRA){
                    ErrorReporter.getInstance().handleException(e);
                }
                if (LogConfig.ERROR_LOGS_ENABLED){
                    Log.e(LOG_TAG, "Can't get DAO for "+type.getName(), e);
                }
            }
                }
    }

    @Override
    public T queryForId(final Integer id) {
        try {
            return dao.queryForId(id);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't get element with id "+id, e);
            }
            return null;
        }
    }

    @Override
    public List<T> queryForAll() {
        try {
            return dao.queryForAll();
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't get "+ type.getName() +" list", e);
            }
            return null;
        }
    }

    @Override
    public void save(final T object) {
        try {
            dao.create(object);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't save "+ object.getClass().getName(), e);
            }
        }
    }

    @Override
    public void merge(final T object) {
        try {
            dao.update(object);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't update "+ object.getClass().getName(), e);
            }
        }
    }

    @Override
    public void delete(final T object) {
        try {
            dao.delete(object);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't delete "+ object.getClass().getName(), e);
            }
        }
    }

    @Override
    public void onCreate(final SQLiteDatabase database, final ConnectionSource connectionSource) {
        try {
            TableUtils.createTableIfNotExists(connectionSource, Job.class);                 
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Cannot create Tables", e);
            }
        }
    }

    @Override
    public void onUpgrade(final SQLiteDatabase database, final ConnectionSource connectionSource, final int oldVersion, final int newVersion) {
        onCreate(database, connectionSource);       
    }
}

每个域对象都有自己的 Dao 接口,定义额外的方法(以防万一需要 GenericDao 中定义的方法之外的其他方法):

public interface JobDao extends GenericDao<Job>{
    Cursor getReceivedJobs();
        ... 
}

以及相对实现:

public final class JobDaoORM extends GenericDaoORM<Job> implements JobDao {
    private static final String LOG_TAG = JobDaoORM.class.getSimpleName();

    public JobDaoORM(final Context context) {
            super(context, Job.class);
        }

    public Cursor getReceivedJobs() {
        try{
            final QueryBuilder<Job, Integer> queryBuilder = dao.queryBuilder();
            queryBuilder.orderBy("reportDate", false);
            queryBuilder.selectColumns(new String[]{"...", "...", ...});
            final Where<Job, Integer> where = queryBuilder.where();
            where.eq("executed", true);
            final PreparedQuery<Job> preparedQuery = queryBuilder.prepare();
            final AndroidCompiledStatement compiledStatement =
                    (AndroidCompiledStatement)preparedQuery.compile(connectionSource.getReadOnlyConnection(),StatementType.SELECT);
            return compiledStatement.getCursor();
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "getReceivedJobs()", e);
            }
        }
        return null;
    }
}

所以我的问题是...

1)这种域/dao 的实现是否高性能,或者我是否引入了冗余且不必要的代码?

2)这是使用ormlite的好方法吗?

3)将每个 dao 的实例保留在扩展 Application 的类中是一个很好的做法,还是在每个活动上保留一个 dao 实例或保留在其他地方更好?

我也尝试过处理事务,但没有成功。我得到一个异常,说表被锁定。我在 GenericDao 中创建了一个方法,返回 TransactionManager 的新实例:

public interface GenericDao <T extends DomainObject> {
    ....
    TransactionManager getTransactionManager();
}

public class GenericDaoORM <T extends DomainObject> extends OrmLiteSqliteOpenHelper implements GenericDao<T>{
...
    @Override
    public TransactionManager getTransactionManager(){
        return new TransactionManager(getConnectionSource());
    }
}

但是当我在事务更新数据库中执行代码时我有一个例外...

谢谢您的回复!

马可

It's a few months that I started developing android application. I have worked for about two years with Spring framework and Hibernate. So, searching for an ORM tool for android I found the ormlite project. I found it very interesting and I decided to used it in my application.

All seems to work fine but I would ask to the experts some tips on developing android apps using ormlite in efficient and (if possible) elegant manner!

Lets start from domain classes.

All domain classes implements a fake DomainObject interface:

@DatabaseTable(tableName = "job")
public final class  Job implements DomainObject{

    @DatabaseField(generatedId = true, columnName="_id")
    public Integer id;

    @DatabaseField(index=true)
    public Integer remoteDbid;

    @DatabaseField
    public Date downloadDate;

    @DatabaseField
    public Date assignedDate;
     .....
}

Then I have a generic Dao Interface

public interface GenericDao <T extends DomainObject> {
    T queryForId(Integer id);
    List<T> queryForAll();
    void save(T object);
    void merge(T object);   
    void delete(T object);
}

implemented by:

public class GenericDaoORM <T extends DomainObject> extends OrmLiteSqliteOpenHelper implements GenericDao<T>{

    private static final String LOG_TAG = GenericDaoORM.class.getSimpleName();

    private final static int DATABASE_VERSION = 7;

    private final Class<T> type;
    protected Dao<T, Integer> dao = null;

    protected GenericDaoORM(final Context context, final Class<T> type) {
        super(context, FileConfig.DB_PATH, null, DATABASE_VERSION);
        this.type = type;
        if (dao == null) {
            try {
                dao = getDao(type);
            } catch (SQLException e) {
                if (LogConfig.ENABLE_ACRA){
                    ErrorReporter.getInstance().handleException(e);
                }
                if (LogConfig.ERROR_LOGS_ENABLED){
                    Log.e(LOG_TAG, "Can't get DAO for "+type.getName(), e);
                }
            }
                }
    }

    @Override
    public T queryForId(final Integer id) {
        try {
            return dao.queryForId(id);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't get element with id "+id, e);
            }
            return null;
        }
    }

    @Override
    public List<T> queryForAll() {
        try {
            return dao.queryForAll();
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't get "+ type.getName() +" list", e);
            }
            return null;
        }
    }

    @Override
    public void save(final T object) {
        try {
            dao.create(object);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't save "+ object.getClass().getName(), e);
            }
        }
    }

    @Override
    public void merge(final T object) {
        try {
            dao.update(object);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't update "+ object.getClass().getName(), e);
            }
        }
    }

    @Override
    public void delete(final T object) {
        try {
            dao.delete(object);
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Can't delete "+ object.getClass().getName(), e);
            }
        }
    }

    @Override
    public void onCreate(final SQLiteDatabase database, final ConnectionSource connectionSource) {
        try {
            TableUtils.createTableIfNotExists(connectionSource, Job.class);                 
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "Cannot create Tables", e);
            }
        }
    }

    @Override
    public void onUpgrade(final SQLiteDatabase database, final ConnectionSource connectionSource, final int oldVersion, final int newVersion) {
        onCreate(database, connectionSource);       
    }
}

Each domain object has it's own Dao Interface defining extra methods (just in case other methods are needed than those defined in GenericDao):

public interface JobDao extends GenericDao<Job>{
    Cursor getReceivedJobs();
        ... 
}

and the relative implementation:

public final class JobDaoORM extends GenericDaoORM<Job> implements JobDao {
    private static final String LOG_TAG = JobDaoORM.class.getSimpleName();

    public JobDaoORM(final Context context) {
            super(context, Job.class);
        }

    public Cursor getReceivedJobs() {
        try{
            final QueryBuilder<Job, Integer> queryBuilder = dao.queryBuilder();
            queryBuilder.orderBy("reportDate", false);
            queryBuilder.selectColumns(new String[]{"...", "...", ...});
            final Where<Job, Integer> where = queryBuilder.where();
            where.eq("executed", true);
            final PreparedQuery<Job> preparedQuery = queryBuilder.prepare();
            final AndroidCompiledStatement compiledStatement =
                    (AndroidCompiledStatement)preparedQuery.compile(connectionSource.getReadOnlyConnection(),StatementType.SELECT);
            return compiledStatement.getCursor();
        } catch (SQLException e) {
            if (LogConfig.ENABLE_ACRA){
                ErrorReporter.getInstance().handleException(e);
            }
            if (LogConfig.ERROR_LOGS_ENABLED){
                Log.e(LOG_TAG, "getReceivedJobs()", e);
            }
        }
        return null;
    }
}

So my questions are...

1)Are this kind of implementation of domain/dao performant or have I introduced redundant and not necessary code?

2)Is this a good way to work with ormlite?

3)It a good practice to keep instance of each dao in a class that extends Application, or is better to keep an an instance of the dao on each activity or keep elsewhere?

I have also tryed to handle transaction but with no success.. I got an exception says that the table is locked.. I've create a method inside GenericDao returning a new instance of TransactionManager:

public interface GenericDao <T extends DomainObject> {
    ....
    TransactionManager getTransactionManager();
}

public class GenericDaoORM <T extends DomainObject> extends OrmLiteSqliteOpenHelper implements GenericDao<T>{
...
    @Override
    public TransactionManager getTransactionManager(){
        return new TransactionManager(getConnectionSource());
    }
}

But when I execute the code in transaction updating DB i got an exception...

Thank you for your response!

Marco

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文