适用于 Android 的 Ormlite:设计帮助等
我开始开发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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论