该模式属于对象关系元数据映射模式目录,此目录属于企业应用程序体系结构的模式。
目的
表示数据库查询的对象。
实例
传统的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); } }
|