The View pattern allows specification of multiple queriable aspects of a single persistence-capable class. Each view defines a subset of the available persistence-capable fields that can be used as criteria in an IQL query. Views are implemented as field-name classes that contain Fields for only the view defined subset of field-name references.
The benefits are two-fold. First, as a convenience. A view can define only those field-name references that are allowed or expected to be used as criteria in an IQL query.
Second, when combined with an appropriate persistence-class based inheritance strategy, the view pattern can effectively limit the scope of information both queriable and returnable in response to an IQL query. In practice, the view/inheritance pattern can be used as a security mechanism for controlling the availability and distribution of sensitive data in different modules of a project.
public class Employee {
// field-name references
public static final String SALUTATION = "salutation");
public static final String FIRSTNAME = "firstname");
public static final String LASTNAME = "lastname");
public static final String TITLE = "title");
public static final String EMPLOYER = "employer");
public static final String EMPLOYEENUMBER = "employeeNumber");
public static final String ADDRESS1 = "address1");
public static final String ADDRESS2 = "address2");
public static final String CITY = "city");
public static final String STATE = "state");
public static final String ZIP = "zip");
// the persistence-capable fields
private String salutation;
private String firstname;
private String lastname;
private String title;
private String employer;
private Integer employeeNumber;
private String address1;
private String address2;
private String city;
private String state;
private String zip;
// constructors/getters/setters
...
}
public class EmplAddr extends AbstractFieldClass {
public Class record = Employee.class;
public Field employeeNum = new Field(record, Employee.EMPLOYEENUMBER);
public Field addr1 = new Field(record, Employee.ADDRESS1);
public Field addr2 = new Field(record, Employee.ADDRESS2);
public Field city = new Field(record, Employee.CITY);
public Field state = new Field(record, Employee.STATE);
public Field zip = new Field(record, Employee.ZIP);
public Field qbe = new Field(record);
public Field[] fields = { employeeNum, addr1, addr2,
city, state, zip, qbe };
}
public class EmplName extends AbstractFieldClass {
public Class record = Employee.class;
public Field title = new Field(record, Employee.TITLE);
public Field sal = new Field(record, Employee.SALUTATION);
public Field fname = new Field(record, Employee.FIRSTNAME);
public Field lname = new Field(record, Employee.LASTNAME);
public Field employer = new Field(record, Employee.EMPLOYER);
public Field employeeNum = new Field(record, Employee.EMPLOYEENUMBER);
public Field qbe = new Field(record);
public Field[] fields = { title, sal, fname, lname,
employer, employeeNum, qbe };
}
The inheritance strategy is to provide a base persistence-capable class that contains general information suitable for general use. A second persistence-capable class, extending the base class, add the 'sensitive' persistence-capable fields. The field-name class defined against the base class is effectively a view defined against the second persistence-capable class. The IQL query results returned using the base view will exclude the 'sensitive' data functionally contained in the second persistence-capable class.
Without resort to inheritance, a somewhat similar degree of security could be achieved by using the Query#into clause to define the type-specific object returned by an IQL query. Alternately, the Query#select clause could by used to limit the returned data to just the identified fields. Unfortunately, using select creates certain practical difficulties in versioning the data objects and there are certain limitations on the current implementation of the into clause by the persistence managers (IQL standardized support for the into clause will be provided in a subsequent version of Inductor).
The view/inheritance pattern works now and in a manner fully and consistently supported by the persistence managers.
Sensitive data is added by inheritance.
public class EmployeeHR extends Employee {
// field-name references
public static final String SSNUMBER = "ssn");
public static final String DRLICNUMBER = "driversLicenseNumber");
public static final String MEDIDNUMBER = "medicalIDNumber");
// the persistence-capable fields
private String ssn;
private String driversLicenseNumber;
private String medicalIDNumber;
// constructors/getters/setters
...
}
A parallel inheritance view field-name class is then defined:
// can extend a limited view field-name class or a complete field-name class
// -- just depends on what is desired to be visible in this view
public class EmplHR extends EmplName {
public Class record = EmployeeHR.class;
public Field socialSecNumber = new Field(record, Employee.SSNUMBER);
public Field driverLicNumber = new Field(record, Employee.DRLICNUMBER);
public Field medicalIDNumber = new Field(record, Employee.MEDIDNUMBER);
public Field qbeEmplHR = new Field(record);
public Field[] fields = { socialSecNumber, driverLicNumber,
medicalIDNumber, qbeEmplHR };
}
Use is the same as any other field-name class. Notably, the field-name references are effectively promoted from base views into inheritance views. The class type of the objects returned in the query collection results is determined by the 'record' value identified in the view used:
public Collection findEmplHRData(String reqFirstName, String reqLastName,
String reqMedNumber) {
Query q = new Query(new JDODriver());
EmplHR emplHR = new EmplHR();
q.from(emplHR);
q.where(emplHR.fname.startsWith(reqFirstName),
emplHR.lname.eq(reqLastName),
emplHR.medicalIDNumber.gteq(reqMedNumber));
q.orderBy(emplHR.lname.asc());
return q.execute();
}