使用 SQLite 开发 AIR 应用程序的最佳实践
我将开始做几个 Adobe AIR 项目,这些项目将使用 AIR
提供的 SQLite
功能.由于这是我第一次尝试这样做,我将不胜感激一些关于开发的指示、技巧和最佳实践.
I am going to start doing a couple of Adobe AIR projects that will be using SQLite
feature provided by AIR
. Since this is the first time I am attempting to do this, I would appreciate some pointers, tips and Best Practices for development.
由于此应用程序将访问本地数据库,我想我可以在应用程序启动时打开与数据库的连接,并保持打开状态直到应用程序关闭.这是在这里使用的正确方法吗?
Since this application will be accessing a local db, I think I can open a connection to the database at the start of the app and keep it open till the application closes. Is this a right approach to use here?
如果我使用 Mate
或 Cairngorm
等 MVC 框架,应该如何设计我的应用程序?
How should design my application if I am using an MVC framework like Mate
or Cairngorm
?
我是否应该创建一些包装类来执行数据库操作,以便我可以在其他项目中重用它?期待一些有价值的信息...
Should I create some wrapper class to perform the db operations so I can reuse this in some other project? Looking forward to some valuable information...
推荐答案
您必须做出的第一个也是迄今为止最重要的决定是您要同步还是异步访问该数据库.两者各有优缺点,看你的情况而定.
The first and by far most important decision you have to make, is whether you're going to access that database synchronously or asynchronously. They both have their pros and cons, so it will depend on your situation.
同步
- 更简单的架构
- 更容易阅读
- 更适合小型操作
- 在执行操作时冻结应用
适合小规模应用
异步
- 更复杂的架构
- 更难阅读
- 长时间操作不会冻结 UI
- 如果您必须与服务器数据库同步,您可以为本地和远程数据库服务创建一个接口(例如
LocalUserService
和RemoteUserService
类都实现了一个接口这迫使他们有一个方法saveUser()
)
- more complex architecture
- harder to read
- long operations will not freeze the UI
- if you have to sync with a server DB, you can create an interface for both local and remote DB services (e.g. the class
LocalUserService
andRemoteUserService
both implement an interface that forces them to have a methodsaveUser()
)
适用于更大规模的应用程序以及同步本地和远程数据.
Good for larger scale app and synchronizing local and remote data.
我几乎总是选择异步方法 - 因为它更灵活 - 并将复杂性抽象到......包装类中.这样就回答了您问题的那部分.
I tend to almost always opt for the asynchronous approach - since it's more flexible - and abstract the complexity away in ... a wrapper class. So that answers that part of your question.
架构
我不喜欢框架,所以我无法回答你关于 Mate
或 -shudder- Cairngorm
的问题.但这是我认为相当不错的方法:
I don't like frameworks, so I can't answer your question about Mate
or -shudder- Cairngorm
. But here's what I consider a fairly good approach:
- 为模型中的每个实体创建一个仅返回原始查询结果的数据访问对象 (DAO)(例如用于查询用户的
UserDAO
).该类应仅包含查询. - 匹配每个 DAO 创建一个生成器/工厂,可以获取这些查询结果并生成模型对象(例如 UserBuilder)
- 这对我来说通常已经足够了,但您也可以将它们两者放在一个服务层(例如 UserService)中.此服务层还可能有助于将现有远程服务层与您的本地服务相匹配.
- for each entity in your model, create a Data Acces Object (DAO) that will return only raw query results (e.g.
UserDAO
for quering Users). That class should contain only queries. - matching each DAO create a Builder/Factory that can take those query results and spawn model objects (e.g. UserBuilder)
- this is usually enough for me, but you could also bring the two of them together in a service layer (e.g. UserService). This service layer might also help matching an existing remote service layer to your local services.
至于保持连接打开.我一直这样做,从来没有遇到过任何问题.我不完全知道当应用程序崩溃并且连接没有正确关闭时会发生什么,但这不是我们正在谈论的 Oracle 或 SQL Server.我不认为 SQLite 会保留打开的指针或其他东西,因为它只是一个简单的文件,但我可能错了.
As for keeping the connection open. I've always done so and never had any issues with it. I don't exactly know what happens when an application crashes and the connection wasn't properly closed, but this is not Oracle or SQL Server we're talking about. I don't think SQLite will keep open pointers or something since it's just a simple file, but I might be wrong.
关于 DAO/工厂模式的更多细节(根据 OP 的要求).
some more details on the DAO / Factory pattern (as requested by OP).
具有一个功能的 UserDAO 示例:
An example of a UserDAO with one function:
public class PupilDAO extends AsynchronousDAO {
public function getUserById(id:int, handleResult:Function):Responder {
return getResults(
"SELECT * FROM user WHERE user_id = ?",
handleResult, [id]
);
}
}
如您所见,我已将复杂性抽象到 AsynchronousDAO
基类中,因此我们只能在 UserDAO 中看到必要的信息.handleResult
函数是一个回调,只要查询准备好就会调用它,将结果集作为参数传递.我们通常会将该结果集传递到工厂/构建器类中.
As you can see, I've abstracted out the complexity into the AsynchronousDAO
base class, so we can see only the necessary information in UserDAO. The handleResult
function is a callback that will be called whenever the query is ready, passing in the resultset as an argument. We'll usually pass that resultset into the factory/builder class.
UserBuilder 的示例:
An example of a UserBuilder:
public class UserBuilder {
public function buildUser(record:*):User {
var user:User = new User();
user.id = record.user_id;
user.firstname = record.firstname;
user.lastname = record.lastname;
return user;
}
}
这显然是一个简单的例子,但您可以在构建器中创建更复杂的数据结构.有关 Factory 模式和 Builder 模式之间差异的一些信息,我建议使用 Google.
This is obviously a simple example, but you can create more complex data structures in the builder. For some info on the difference between the Factory and Builder patterns I recommend Google.
现在让我们把它绑定到服务类中:
Now let's tie it together in the service class:
public class UserService {
private var dao:UsetDAO;
private var builder:UserBuilder;
public UserService(dao:UserDAO, builder:UserBuilder) {
this.dao = dao;
this.builder = builder;
}
public function getUserById(id:int, handleResult):void {
var handleResultSet:Function = function(resultSet:SQLResult):void {
var user:User = builder.buildUser(resultSet.data[0]);
if (handleResult!= null) handleResult(user);
}
dao.getUserById(id, handleResultSet);
}
}
最后,让我们使用三重奏:
Finally, let's put the trio to use:
var userService = new UserService(new UserDAO(), new UserBuilder());
userService.getUserById(1, handleUser);
function handleUser(user:User):void {
trace(user);
}
例如,我使用 new
构建了类,并将 dao 和构建器作为构造函数参数传递,但您可以使用注入或单例或任何您喜欢的框架来为您构建.
For the example I constructed the classes with new
and passed the dao and the builder as constructor arguments, but you could use injection or singletons or whatever framework you like to do the construction for you.
相关文章