A key feature of SQL databases is the ability to extract data of related
tables in a single call (this is actually what makes them 'relational').
The SQL term for this feature is the so-called join. As a difference
to most other O/R mapping toolkits, PriDE simply treats a join as an entity
type being assembled from the data of multiple tables. It just has to
be considered that join entities are not suitable for updates and that
the relationship rule must somehow be provided by the application. PriDE
provides several different ways to build joins., depending on the application
needs.
The following example demonstrates an entity class for a join from a
customer and an order table, providing an individual selection of fields
from both tables and making use of a foreign-key relationship:
// Joined entity type providing customer ID, customer name,
// order ID and order volume from the tables 'customer' and // 'orders' in one query public class CustomerOrder extends MappedObject { // The table relation to consider in all queries private static final String JOIN = " AND customer.id=orders.customer_id"; protected static RecordDescriptor red = new
RecordDescriptor //... // Returns the list of all orders for the
specified customer |
If the relationship rule becomes more complex, it is recommended to use
SQLExpression
for a safer expression assembly. Of course this would often require field
identifiers as right-hand side operators and thus a suppression of the
standard SQL formatting. Using the helper class SQLRaw ommits any
formatting of the provided value. The following example shows how to assemble
the relationship rule above using an SQLExpression:
// Empty SQLExpression SQLExpression join = new SQLExpression(DatabaseFactory.getDatabase()); // Join criteria using SQLRaw to suppress SQL formatting of "orders.customer_id" join = join.and("customer.id", new SQLRaw("orders.customer_id")); // join.and above didn't actually produce an "AND" as the expression // was formerly empty. So we add it explicitely here String JOIN = "AND " + join.toString(); |
protected static RecordDescriptor red = new
RecordDescriptor
|
public class DistinctRecordDescriptor extends RecordDescriptor
{ public DistinctRecordDescriptor( Class objectType, String dbtable, RecordDescriptor baseDescriptor, String[][] attributeMap) throws IllegalDescriptorException { super(objectType, dbtable, baseDescriptor, attributeMap); } protected String getResultFields() { return "DISTINCT " + super.getResultFields(); } } |
The code above is suitable for any distinct join definition and will probably
become a core PriDE class in the future. It is simply used instead of the
standard RecordDescriptor type, performing a distinct selection,
e.g.
protected static DistinctRecordDescriptor
red = new DistinctRecordDescriptor |
Entity classes for the data from multiple tables can automatically be
created by the entity generator mentioned in the introduction. The tables of interest
are passed as a comma-separated list instead of just passing a single table
name. Another way to work with joins is the configuration of views in the
database which can be accessed within PriDE like an ordinary table.
In many cases, a join simply queries a table which is already mapped but
has to take this table's relationship to other tables into account. For
these cases, PriDE provides an appropriate copy constructor for record descriptors.
The following example shows a derivation of the customer entity from the
introduction chapter, making use of this feature. It retrieves all customers
having ordered something in the past which requires to take a look on the
related order table without actually returning any data from it:
// Extended customer type to retrieve "active" customers public class ActiveCustomer extends Customer { // An inner join to use for retrieval private static final String JOIN = "customer c inner join orders o on o.customer_id = c.id"; // An extended record descriptor specifying the alias "c" // and the alternate table expression JOIN protected static RecordDescriptor red = new RecordDescriptor(Customer.red, "c", JOIN); protected RecordDescriptor getDescriptor() { return red; } } |
Another reason for the use of joins is the reduction of database interactions
by retrieving the data of multiple tables in a single step. In this case,
the query results can often be represented by an aggregation of already
mapped types. Using the copy constructor for records descriptors as shown
above, also allows to build specialized descriptors for complex entity types.
The following example shows a descriptor retrieving customers and their
potential last order, using an outer join with the order table:
public class JoinedCustomer extends Customer { // Check for existence
of an order part in the result record |
The source code of the examples above can be found in examples/join.
Home | Introduction | Javadoc |