如何使数据访问对象成为非阻塞的?
How to make Data Access Objects non-blocking?
我正在学习数据访问对象模式,它提供对数据源(例如数据库)的访问。 This answer给另一个问题提供了如下例子:
interface EmployeeDAO {
List<Employee> findAll();
List<Employee> findById();
List<Employee> findByName();
boolean insertEmployee(Employee employee);
boolean updateEmployee(Employee employee);
boolean deleteEmployee(Employee employee);
}
我在互联网上的其他答案和文章中看到了类似的例子。让我感到困惑的是,读取和写入数据库通常需要一段时间,在这种情况下,据我所知,这些示例(尤其是 find...()
的示例)并不是很实用。也就是说,在 find...()
调用期间阻塞可能不是期望的行为。
我认为使用 void EmployeeFound(Employee employee)
等方法创建一个 Listener 接口 (EmployeeDAO.Listener
) 可能是有意义的,但令我惊讶的是我之前在 DAO 示例中没有看到这个.我想知道我是否只是不理解数据访问对象 and/or 如果我错过了更明显的方法。
您在上述接口中实现的方法将是简单的 sql 查询。
- 查找全部将是 "select * from table"
- 按 id 查找将是 "select * from table where id = :id"(这是 table 的主键 - 并已编入索引)。
我开发的应用程序每天执行数百万条插入语句,我们不用担心阻塞。如果您在 java 中工作并且使用 Spring 框架,则可以使用一些库来为您处理所有这一切。查看 java.persistence.
中的 EntityManager 和 TransactionManager
有许多不同的选项/方法经常被采用:
阻塞 就像您展示的 API 一样。这是一个使用起来非常简单的API,并且仍然可以通过在多线程应用程序中调用API来实现并行性。
Receiving/registering 异步操作中的处理程序。这有时与阻塞 API 结合提供(事实上,可以根据阻塞 API 简单地通过生成后台线程然后在最后调用处理程序来实现)。
返回一个 Future 或 ListenableFuture 对象,这使得界面更加地道 Java-esque(通过 returning 数据在 return 类型的位置),但表示最终结果,而不是立即可用的结果。然后可以使用 Future 来阻塞或非阻塞。
就个人而言,我的建议是:
interface EmployeeDatabase {
interface StringWhereClause {
ListQueryBuilder is(String value);
ListQueryBuilder contains(String value);
ListQueryBUilder matchesRegex(String regex);
}
interface IntWhereClause {
ListQueryBuilder is(int value);
ListQueryBuilder isInRange(int min, int max);
ListQueryBuilder isGreaterThan(int value);
ListQueryBUilder isLessThan(int value);
ListQueryBuilder isGreaterThanOrEqualTo(int value);
ListQueryBUilder isLessThanOrEqualTo(int value);
}
// ... matchers for other types of properties ...
interface ListQueryBuilder {
// Generic restrict methods
StringWhereClause whereStringProperty(String propertyName);
IntWhereClause whereIntProperty(String propertyName);
// ...
// Named restrict methods
StringWhereClause whereName();
StringWhereClause whereJobTitle();
IntWhereClause whereEmployeeNumber();
// ...
ListQueryBuilder limit(long maximumSize);
ListQueryBuilder offset(long index);
ResultSet<Employee> fetch();
}
ListQueryBuilder list();
ListenableFuture<Employee> getById(Key key);
ListenableFuture<KeyOrError> add(Employee employee);
ListenableFuture<Status> update(Key key, Employee employee);
ListenableFuture<Status> delete(Key key);
}
有:
interface ResultSet<T> {
Iterable<T> asIterable();
// ... other methods ...
}
interface KeyOrError {
boolean isError();
boolean isKey();
Key getKey();
Throwable getError();
}
interface Status {
boolean isOk();
boolean isError();
Throwable getError();
void verifyOk();
}
基本上,想法是向数据库中插入 returns 一个 Key 对象(如果不成功则报错)。此键可用于检索、删除或更新数据库中的条目。这些操作(添加、更新、删除和 getById)都有一个结果,在这种情况下使用 ListenableFuture<T>
而不是类型 T
;这个 future 对象允许您阻塞(通过在 future 对象上调用 .get()
)或异步检索对象(通过注册一个回调,以便在结果就绪时调用)。
对于 list-y 操作,有许多不同的方法可以对列表进行过滤、子选择、排序等。为了防止各种不同重载的组合爆炸,我们使用 builder pattern 来允许以多种组合应用这些不同的限制。简而言之,构建器接口提供了一种方法来添加零个或多个选项(排序、过滤器、限制等)以应用检索操作,然后调用 fetch()
导致执行列表查询和 returns ResultSet
。此操作 return 是 ResultSet
而不是 ListenableFuture
,因为结果并非全部 return 立即编辑(例如,它们可能以流方式从数据库返回) ; ResultSet
实际上是一个接口,具有与 ListenableFuture
类似的行为,但用于项目列表(项目可能在不同时间准备好)。为方便起见,重要的是有一种方法可以轻松地迭代 ResultSet 的内容(例如,通过为 ResultSet 提供 Iterable
适配器);但是,您可能还想添加其他方法,以允许您在 ResultSet 上执行其他类型的异步处理;例如,您可能希望 ListenableFuture<T> reduce(T initialValue, ReduceFunction<T> reducer)
聚合结果集中的元素并提供表示最终完成的未来对象。
我正在学习数据访问对象模式,它提供对数据源(例如数据库)的访问。 This answer给另一个问题提供了如下例子:
interface EmployeeDAO {
List<Employee> findAll();
List<Employee> findById();
List<Employee> findByName();
boolean insertEmployee(Employee employee);
boolean updateEmployee(Employee employee);
boolean deleteEmployee(Employee employee);
}
我在互联网上的其他答案和文章中看到了类似的例子。让我感到困惑的是,读取和写入数据库通常需要一段时间,在这种情况下,据我所知,这些示例(尤其是 find...()
的示例)并不是很实用。也就是说,在 find...()
调用期间阻塞可能不是期望的行为。
我认为使用 void EmployeeFound(Employee employee)
等方法创建一个 Listener 接口 (EmployeeDAO.Listener
) 可能是有意义的,但令我惊讶的是我之前在 DAO 示例中没有看到这个.我想知道我是否只是不理解数据访问对象 and/or 如果我错过了更明显的方法。
您在上述接口中实现的方法将是简单的 sql 查询。
- 查找全部将是 "select * from table"
- 按 id 查找将是 "select * from table where id = :id"(这是 table 的主键 - 并已编入索引)。
我开发的应用程序每天执行数百万条插入语句,我们不用担心阻塞。如果您在 java 中工作并且使用 Spring 框架,则可以使用一些库来为您处理所有这一切。查看 java.persistence.
中的 EntityManager 和 TransactionManager有许多不同的选项/方法经常被采用:
阻塞 就像您展示的 API 一样。这是一个使用起来非常简单的API,并且仍然可以通过在多线程应用程序中调用API来实现并行性。
Receiving/registering 异步操作中的处理程序。这有时与阻塞 API 结合提供(事实上,可以根据阻塞 API 简单地通过生成后台线程然后在最后调用处理程序来实现)。
返回一个 Future 或 ListenableFuture 对象,这使得界面更加地道 Java-esque(通过 returning 数据在 return 类型的位置),但表示最终结果,而不是立即可用的结果。然后可以使用 Future 来阻塞或非阻塞。
就个人而言,我的建议是:
interface EmployeeDatabase {
interface StringWhereClause {
ListQueryBuilder is(String value);
ListQueryBuilder contains(String value);
ListQueryBUilder matchesRegex(String regex);
}
interface IntWhereClause {
ListQueryBuilder is(int value);
ListQueryBuilder isInRange(int min, int max);
ListQueryBuilder isGreaterThan(int value);
ListQueryBUilder isLessThan(int value);
ListQueryBuilder isGreaterThanOrEqualTo(int value);
ListQueryBUilder isLessThanOrEqualTo(int value);
}
// ... matchers for other types of properties ...
interface ListQueryBuilder {
// Generic restrict methods
StringWhereClause whereStringProperty(String propertyName);
IntWhereClause whereIntProperty(String propertyName);
// ...
// Named restrict methods
StringWhereClause whereName();
StringWhereClause whereJobTitle();
IntWhereClause whereEmployeeNumber();
// ...
ListQueryBuilder limit(long maximumSize);
ListQueryBuilder offset(long index);
ResultSet<Employee> fetch();
}
ListQueryBuilder list();
ListenableFuture<Employee> getById(Key key);
ListenableFuture<KeyOrError> add(Employee employee);
ListenableFuture<Status> update(Key key, Employee employee);
ListenableFuture<Status> delete(Key key);
}
有:
interface ResultSet<T> {
Iterable<T> asIterable();
// ... other methods ...
}
interface KeyOrError {
boolean isError();
boolean isKey();
Key getKey();
Throwable getError();
}
interface Status {
boolean isOk();
boolean isError();
Throwable getError();
void verifyOk();
}
基本上,想法是向数据库中插入 returns 一个 Key 对象(如果不成功则报错)。此键可用于检索、删除或更新数据库中的条目。这些操作(添加、更新、删除和 getById)都有一个结果,在这种情况下使用 ListenableFuture<T>
而不是类型 T
;这个 future 对象允许您阻塞(通过在 future 对象上调用 .get()
)或异步检索对象(通过注册一个回调,以便在结果就绪时调用)。
对于 list-y 操作,有许多不同的方法可以对列表进行过滤、子选择、排序等。为了防止各种不同重载的组合爆炸,我们使用 builder pattern 来允许以多种组合应用这些不同的限制。简而言之,构建器接口提供了一种方法来添加零个或多个选项(排序、过滤器、限制等)以应用检索操作,然后调用 fetch()
导致执行列表查询和 returns ResultSet
。此操作 return 是 ResultSet
而不是 ListenableFuture
,因为结果并非全部 return 立即编辑(例如,它们可能以流方式从数据库返回) ; ResultSet
实际上是一个接口,具有与 ListenableFuture
类似的行为,但用于项目列表(项目可能在不同时间准备好)。为方便起见,重要的是有一种方法可以轻松地迭代 ResultSet 的内容(例如,通过为 ResultSet 提供 Iterable
适配器);但是,您可能还想添加其他方法,以允许您在 ResultSet 上执行其他类型的异步处理;例如,您可能希望 ListenableFuture<T> reduce(T initialValue, ReduceFunction<T> reducer)
聚合结果集中的元素并提供表示最终完成的未来对象。