查询对象模式(Query Object Pattern)

19-03-20 jdon
         

该模式属于对象关系元数据映射模式目录,此目录属于企业应用程序体系结构的模式。

目的

表示数据库查询的对象。

实例

传统的Hibernate标准API和现代的JPA2标准API是这个模式的优秀示例,它将它与Builder Pattern结合在一起。

关键点

1. 通过引用类和字段而不是表和列来创建此查询。

2. 使得查询独立于数据库架构,并且对架构的更改可以在单个位置进行本地化。将来,如果要更改架构,则可以在单个位置本地化时更改QueryObject类。

3. 查询对象模式实际上使用元数据映射模式来生成实际的数据库查询。

如何运作

查询对象是解释器模式的应用程序,用于表示SQL查询。它的主要作用是允许客户端形成各种类型的查询,并将这些对象结构转换为适当的SQL字符串。

查询对象的一个​​共同特征是它可以用内存中对象的语言而不是数据库模式表示查询。这意味着,您可以使用对象和字段名称,而不是使用表名和列名。如果您的对象和数据库具有相同的结构,这并不重要,但如果您在两者之间获得变化,它将非常有用。为了执行这种视图更改,查询对象需要知道数据库结构如何映射到对象结构,这是一种真正需要元数据映射的功能。

如果您使用多个数据库,则可以设计查询对象,使其根据运行查询的数据库生成不同的SQL。

优点

查询对象的优点来自更复杂的需求:保持数据库模式的封装,支持多个数据库,支持多个模式,以及通过优化来避免多个查询。

代码示例

让我们创建一个简单的查询对象、条件和映射器类来演示这种模式。

class QueryObject {
    private Class klass;
    private List criteria = new ArrayList(); 
    
    public Set execute(UnitOfWork uow) {
        this.uow = uow;
        return uow.getMapper(klass).findObjectsWhere(generateWhereClause());
    }

private String generateWhereClause() {
    StringBuffer result = new StringBuffer();
        for (Iterator it = criteria.iterator(); it.hasNext();) {
            Criteria c = (Criteria)it.next();
            if (result.length()!= 0)
            result.append("AND");
             result.append(c.generateSql(uow.getMapper(klass).getDataMap));
    }
    return result.toString();
    }
    
}

class Criteria {
    private String sqlOperator;
    protected String field;
    protected Object value; 
    
    public static Criteria greaterThan(String fieldName, int value) {
        return Criteria.greaterThan(fieldName, new Integer(value));
    }
    public static Criteria greaterThan(String fieldName, Object value) {
        return new Criteria(" > ", fieldName, value);
    }
    private Criteria(String sql, String field, Object value) {
        this.sqlOperator = sql;
        this.field = field;
        this.value = value;
    }

    public String generateSql(DataMap dataMap) {
        return dataMap.getColumnForField(field) + sqlOperator + value;
    }

    public static Criteria matches(String fieldName, String pattern){
        return new MatchCriteria(fieldName, pattern);
    }

}

public class Person{
    private String lastName;
    private String firstName;
    private int numberOfDependents
    
}

class MatchCriteria extends Criteria{
    public String generateSql(DataMap dataMap) {
    return "UPPER(" + dataMap.getColumnForField(field) + ") LIKE UPPER('" + value + "')";
    }
}

class Mapper {

   public Set findObjectsWhere (String whereClause) {
      String sql = "SELECT" + dataMap.columnList() + " FROM " + dataMap.getTableName() + " WHERE "
         + whereClause;
      PreparedStatement stmt = null;
      ResultSet rs = null;
      Set result = new HashSet();
      try {
         stmt = DB.prepare(sql);
         rs = stmt.executeQuery();
         result = loadAll(rs);
      } catch (Exception e) {
           throw new ApplicationException (e);
      } finally {DB.cleanUp(stmt, rs);
      }
    return result;
   }
}


class DataMap {

  public String getColumnForField (String fieldName) {
      for (Iterator it = getColumns(); it.hasNext();) {
         ColumnMap columnMap = (ColumnMap)it.next();
         if (columnMap.getFieldName().equals(fieldName))
            return columnMap.getColumnName();
      }
      throw new ApplicationException ("Unable to find column for " + fieldName);
  }
}

public class Client{

     public static void main(String []args){
          QueryObject query = new QueryObject(Person.class);
            query.addCriteria(Criteria.greaterThan("numberOfDependents", 0));
            
        UnitOfWork uow = new UnitOfWork();
        query.execute(uow);
     }
}