safe query objects: statically typed objects as remotely executable queries by: william cook &...
Post on 20-Dec-2015
239 views
TRANSCRIPT
Safe Query Objects:Statically Typed Objects as Remotely Executable QueriesBy: William Cook & Siddhartha RaiICSE 2005
Introduction
• Fact: Large Applications require persistence.
• Existing solutions:– EJB– Hibernate– JDO
• Problem: individual objects granularity.
Problems
• String encoding
• Runtime checking only
• Complex queries must be weaved by hand…
• Programmers must learn two languages.
New Idea• Safe Query Objects
– Represent a query as a statically typed object. – Combine object-relational mapping with
object-oriented languages.
• We will see:– Statically typed interface to the dynamically
typed functionality in the JDO 1.0 specification.
JDO Background: The Persistence Manager Interface
• Access to persistent objects via instance of the PersistenceManager interface.
• Loading individual objects:– getObjectById
• Creating a new Query:– newQuery
interface javax.jdo.PeristenceManager{Object getObjectById(Object id);Javax.jdo.Query newQuery(Class class);// methods for transactions not listed}
interface javax.jdo.PeristenceManager{Object getObjectById(Object id);Javax.jdo.Query newQuery(Class class);// methods for transactions not listed}
JDO Background: The Query Interface
• Query execution is provided by the Query Interface.
interface javax.jdo.Query{void setFilter(String filter);void setOrdering(String ordering);void declareImports(String imports);void declareParameters(String params);void declareVariable(String vars);Object execute();Object execute(Object arg1);Object executeWithMap(Map parameters);// bookkeeping methods not listed}
interface javax.jdo.Query{void setFilter(String filter);void setOrdering(String ordering);void declareImports(String imports);void declareParameters(String params);void declareVariable(String vars);Object execute();Object execute(Object arg1);Object executeWithMap(Map parameters);// bookkeeping methods not listed}
JDO Background: Candidate Classes
• Candidate classes:– Have structural correspondence to tables in database.
• We will use:
class Employee{ String name; float salary; Department department; Employee manager;}
class Employee{ String name; float salary; Department department; Employee manager;}
class Department{ String name; Collection<Employee> employees;}
class Department{ String name; Collection<Employee> employees;}
JDO Background: Usage
Collection<Employee> execute(PersistenceManager pm){ javax.jdo.Query payCheck = pm.newQuery(Employee.class); payCheck.setFilter(“salary > maneger.salary”); Object result = payCheck.execute(); return (Collection<Employee>)result;}
Collection<Employee> execute(PersistenceManager pm){ javax.jdo.Query payCheck = pm.newQuery(Employee.class); payCheck.setFilter(“salary > maneger.salary”); Object result = payCheck.execute(); return (Collection<Employee>)result;}
The misspelled “maneger” will not be detected until runtime!
Safe Query• Safe query object contains:
– Filtering method– Ordering method
• Safe Query Class– Subclass of SafeQuery<T>– Instantiates RemoteQueryJDO
class PayCheck extends SafeQuery<Employee> instantiates RemoteQueryJDO{
boolean filter(Employee emp){ return emp.salary > emp.maneger.salary; }}
Candidate Class
Compilation Error
Filtering Method
• Defines the “WHERE” condition in the query.
• Must be called filter• Must return a boolean value.
• Free of side effects:– Must not modify states– No iterative constructs
Sorted Results : JDO • Queries often specify sort order for the set of
query results.• Relational query languages:
– ascending/descending order.
• Sorting in JDO:– setOrdering method.
Collection sortEmployees(){ javax.jdo.Query q = pm.newQuery(Employee.class); q.setOrdering(“department.name ascending,”
+ “ salary descending”); return (Collection) q.execute();}
Collection sortEmployees(){ javax.jdo.Query q = pm.newQuery(Employee.class); q.setOrdering(“department.name ascending,”
+ “ salary descending”); return (Collection) q.execute();}
Sorted Results: Safe Query• Safe query defines order method
– Takes a candidate object– Returns a linked list of the of sortable values.
class SortQuery instantiates RemoteQueryJDOextends SafeQuery<Employee>{
Sort order(Employee emp){ return new Sort(emp.department.name,
Sort.Direction.ASCENDING,new Sort(emp.salary,Sort.Direction.DESCENDING));
}}
class SortQuery instantiates RemoteQueryJDOextends SafeQuery<Employee>{
Sort order(Employee emp){ return new Sort(emp.department.name,
Sort.Direction.ASCENDING,new Sort(emp.salary,Sort.Direction.DESCENDING));
}}
Parameterized Queries: JDO
• Needed when a query behavior depends upon one or more input value.
• JDO approach:– declareParameters method
Collection salaryLimitEmployees(double limit){ javax.jdo.Query q = pm.newQuery(Employee.class); q.setFilter(“salary > limit”); q.declareParameters(“Double limit”); Collection r = (Collection)q.execute(new Double(limit)); return r;}
Collection salaryLimitEmployees(double limit){ javax.jdo.Query q = pm.newQuery(Employee.class); q.setFilter(“salary > limit”); q.declareParameters(“Double limit”); Collection r = (Collection)q.execute(new Double(limit)); return r;}
What happens if we omit the call to declareParameters?
Parameterized Queries: Safe Query• Query parameters are defined as standard Java
function parameters.• Declared as arguments to the query constructor• Stored as member variables of the query object.
class SalaryLimit instantiates RemoteQueryJDOextends SafeQuery<Employee>{
double limit; /*parameter*/SalaryLimit(double limit){ this.limit = limit; }boolean filter(Employee employee){ return employee.salary > limit; }}
class SalaryLimit instantiates RemoteQueryJDOextends SafeQuery<Employee>{
double limit; /*parameter*/SalaryLimit(double limit){ this.limit = limit; }boolean filter(Employee employee){ return employee.salary > limit; }}
Dynamic Queries: JDO• Involve filters, parameters or sort orders• Constructed at runtime.
Collection search(String namePrefix, Double minSalary){ String filter = null; String paramDecl = “”; HashMap paramMap = new HashMap(); javax.jdo.Query q =makeQuery(Employee.class); if (namePrefix!= null){ q.declareParameters(“String namePrefix”); paramMap.put(“namePrefix”,namePrefix); filter = and(filter,”(name.startsWith(namePrefix))”); } if (minSalary != null){ q.declareParameters(“double minSalary”); paramMap.put(“minSalary”, minSalary); filter= and(filter, “(salary >= minSalary)”); } q.setFilter(filter); return q.executeWithMap(pramMap);}String and(String a, String b){…}
Collection search(String namePrefix, Double minSalary){ String filter = null; String paramDecl = “”; HashMap paramMap = new HashMap(); javax.jdo.Query q =makeQuery(Employee.class); if (namePrefix!= null){ q.declareParameters(“String namePrefix”); paramMap.put(“namePrefix”,namePrefix); filter = and(filter,”(name.startsWith(namePrefix))”); } if (minSalary != null){ q.declareParameters(“double minSalary”); paramMap.put(“minSalary”, minSalary); filter= and(filter, “(salary >= minSalary)”); } q.setFilter(filter); return q.executeWithMap(pramMap);}String and(String a, String b){…}
Dynamic Queries: Safe Query
class DynQuery instantiates RemoteQueryJDOextends SafeQuery<Employee>{
private String namePrefix; // may be nullprivate Double minSalary; // may be nullDynQuery (String namePrefix, Double minSalary){
this.namePrefix = namePrefix;this.minSalary = minSalary;
}boolean filter(Employee item){
return (namePrefix == null || item.name.startsWith(namePrefix)) && (minSalary == null
|| item.salary >= minSalary);}
}
class DynQuery instantiates RemoteQueryJDOextends SafeQuery<Employee>{
private String namePrefix; // may be nullprivate Double minSalary; // may be nullDynQuery (String namePrefix, Double minSalary){
this.namePrefix = namePrefix;this.minSalary = minSalary;
}boolean filter(Employee item){
return (namePrefix == null || item.name.startsWith(namePrefix)) && (minSalary == null
|| item.salary >= minSalary);}
}
Null Values
• Relational databases allow nullable values– Operations involving null return null
• OOP languages – Allow null object references– Problem 1: Primitive types cannot be null
• Solution: Boxing
– Problem 2: Navigation • NullPointerException treated as false returned from “filtering”• Example: or(emp.department == dept, emp.salary > min) • Solution: …
Implementation• The query translation is encapsulated in the RemoteQueryJDO metaclass which is applied to the safe query by the JavaOpen instantiates keyword.
• OpenJava runs the metaclass at compile time, supplying the definition of the of the query class (e.g. PayCheck) as input.
• The RemoteQueryJDO metaclass examines the user-defined filter method and generates the corresponding execute method.
OpenJava
RemoteQueryJDOMetaclass
Query Class Definition
Execute Method
Compile Time
Advantages
• Queries defined using OO language– Static type checking– Syntax errors (at compilation time)– JDO seems more comfortable
• Observation: Most queries are either static or dynamic with static tables.– Safe query can help.
Disadvantages
• Dynamic queries and data-driven with query definition loaded from DB– Open problem…
• What about creating new tables?
• Updating tables..
The End