如何使数据访问对象无阻塞?

时间:2016-03-04 04:52:12

标签: java design-patterns dao

我正在学习数据访问对象模式,该模式提供对数据源(如数据库)的访问。 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...()调用期间阻塞可能不是期望的行为。

我认为使用EmployeeDAO.Listener等方法创建一个Listener界面(void EmployeeFound(Employee employee))是有意义的,但我很惊讶我还没有看到这个之前在DAO示例中。我想知道我是不是不理解数据访问对象和/或我是否错过了一个更明显的方法。

2 个答案:

答案 0 :(得分:2)

通常会采取许多不同的选择/方法:

  1. 阻止,就像您展示的API一样。这是一个非常简单的API,并且仍然可以通过在多线程应用程序中调用API来实现并行性。

  2. 在异步操作中接收/注册处理程序。这有时与阻塞API一起提供(实际上,可以通过生成后台线程,然后在最后调用处理程序来实现阻塞API)。

  3. 返回未来ListenableFuture对象,这使得界面更加惯用Java(通过返回返回类型位置的数据),但代表最终结果,而不是立即可用的结果。然后可以使用Future来阻止或阻止。

  4. 就个人而言,我的推荐是:

    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();
     }
    

    基本上,想法是插入数据库会返回一个Key对象(如果不成功则返回错误)。此密钥可用于检索,删除或更新数据库中的条目。这些操作(添加,更新,删除和getById)都只有一个结果,在这种情况下使用ListenableFuture<T>而不是类型T;此future对象允许您阻止(通过在将来的对象上调用.get())或异步检索对象(通过注册在结果准备好时调用的回调)。

    对于list-y操作,可以通过许多不同的方式对列表进行过滤,子选择,排序等。为了防止各种不同重载的组合爆炸,我们使用builder pattern来允许这些在多种组合中应用的不同限制。简而言之,构建器接口提供了一种方法来处理零个或多个选项(排序,过滤器,限制等)以应用检索操作,在调用fetch()之前导致列表查询被执行并返回ResultSet。此操作返回ResultSet而不是ListenableFuture,因为结果不是一次全部返回(例如,它们可能以流方式从数据库返回); ResultSet实际上是一个与ListenableFuture具有类似行为的界面,但是对于项目列表(项目可能在不同时间准备好)。为方便起见,有一种方法可以轻松地迭代ResultSet的内容(例如,通过向ResultSet提供Iterable适配器);但是,您可能还希望添加其他方法,以允许您在ResultSet上执行其他类型的异步处理;例如,您可能希望ListenableFuture<T> reduce(T initialValue, ReduceFunction<T> reducer)聚合结果集中的元素,并提供表示最终完成的未来对象。

答案 1 :(得分:0)

您在上面的接口中实现的方法将是简单的SQL查询。

  • 查找所有内容将是“select * from table”
  • 按id查找将是“select * from table where id =:id”(这是表的主键 - 并已编入索引)。

我在一个应用程序中工作,我们每天执行数百万个插入语句,我们不担心阻塞。如果您正在使用Java并且使用Spring框架,那么可以使用可以为您处理所有这些内容的库。在java.persistence中查看EntityManager和TransactionManager。