Improved @Computed support for subset record queries, from Zell.
git-svn-id: https://samskivert.googlecode.com/svn/trunk@2072 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -97,47 +97,65 @@ public abstract class ConstructedQuery<T>
|
||||
throw new RuntimeException("createQuery() called with _mainClass == null");
|
||||
}
|
||||
|
||||
// we need to know if there's a entity-level @Computed annotation
|
||||
Computed entityComputed = _mainType.getAnnotation(Computed.class);
|
||||
|
||||
// and we need to look up per-field information on our marshaller
|
||||
DepotMarshaller<?> mainMarshaller = _classMap.get(_mainType);
|
||||
String[] fields = mainMarshaller._allFields;
|
||||
|
||||
StringBuilder query = new StringBuilder("select ");
|
||||
boolean skip = true;
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
for (String field : mainMarshaller.getFieldNames()) {
|
||||
if (!skip) {
|
||||
query.append(", ");
|
||||
}
|
||||
skip = false;
|
||||
|
||||
FieldOverride clause = _disMap.get(fields[ii]);
|
||||
// see if there's a field override
|
||||
FieldOverride clause = _disMap.get(field);
|
||||
if (clause != null) {
|
||||
clause.appendClause(this, query);
|
||||
continue;
|
||||
}
|
||||
|
||||
Computed computed = mainMarshaller._fields.get(fields[ii]).getComputed();
|
||||
if (computed == null) {
|
||||
// make sure the object corresponds to a table, otherwise the whole thing is
|
||||
// computed
|
||||
if (mainMarshaller.getTableName() != null) {
|
||||
// if it's neither overridden nor computed, it's a standard field
|
||||
query.append(getTableAbbreviation(_mainType)).append(".").append(fields[ii]);
|
||||
// figure out the class we're selecting from unless we're otherwise overriden:
|
||||
// for a concrete record, simply use the corresponding table; for a computed one,
|
||||
// default to the shadowed concrete record, or null if there isn't one
|
||||
Class<? extends PersistentRecord> tableClass =
|
||||
entityComputed == null ? _mainType : entityComputed.shadowOf();
|
||||
|
||||
// handle the field-level @Computed annotation, if there is one
|
||||
Computed fieldComputed = mainMarshaller.getFieldMarshaller(field).getComputed();
|
||||
if (fieldComputed != null) {
|
||||
// check if the computed field has a literal SQL definition
|
||||
if (fieldComputed.fieldDefinition().length() > 0) {
|
||||
query.append(fieldComputed.fieldDefinition()).append(" as ").append(field);
|
||||
continue;
|
||||
}
|
||||
throw new SQLException(
|
||||
"@Computed entity field without definition [field=" + fields[ii] + "]");
|
||||
}
|
||||
|
||||
// check if the computed field has a literal SQL definition
|
||||
if (computed.fieldDefinition().length() > 0) {
|
||||
query.append(computed.fieldDefinition() + " as " + fields[ii]);
|
||||
|
||||
} else if (!computed.required()) {
|
||||
// or if we can simply ignore the field
|
||||
skip = true;
|
||||
if (!fieldComputed.required()) {
|
||||
skip = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new SQLException(
|
||||
"@Computed(required) field without definition [field=" + fields[ii] + "]");
|
||||
// else see if there's an overriding shadowOf definition
|
||||
if (fieldComputed.shadowOf() != null) {
|
||||
tableClass = fieldComputed.shadowOf();
|
||||
}
|
||||
}
|
||||
|
||||
// if we get this far we hopefully have a table to select from
|
||||
if (tableClass != null) {
|
||||
String tableName = getTableAbbreviation(tableClass);
|
||||
query.append(tableName).append(".").append(field);
|
||||
continue;
|
||||
}
|
||||
|
||||
// else owie
|
||||
throw new SQLException(
|
||||
"Persistent field has no definition [class=" + _mainType +
|
||||
", field=" + field + "]");
|
||||
}
|
||||
|
||||
if (_fromOverride != null) {
|
||||
@@ -268,7 +286,7 @@ public abstract class ConstructedQuery<T>
|
||||
}
|
||||
_classList = new ArrayList<Class<? extends PersistentRecord>>(classSet);
|
||||
}
|
||||
|
||||
|
||||
/** The persistent class to instantiate for the results. */
|
||||
protected Class<? extends PersistentRecord> _mainType;
|
||||
|
||||
|
||||
@@ -31,9 +31,6 @@ import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -253,7 +250,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the table in which persistence instances of this class are stored. By
|
||||
* Returns the name of the table in which persistent instances of our class are stored. By
|
||||
* default this is the classname of the persistent object without the package.
|
||||
*/
|
||||
public String getTableName ()
|
||||
@@ -261,6 +258,22 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
return _tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the persistent fields of our class, in definition order.
|
||||
*/
|
||||
public String[] getFieldNames ()
|
||||
{
|
||||
return _allFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FieldMarshaller} for a named field on our persistent class.
|
||||
*/
|
||||
public FieldMarshaller getFieldMarshaller (String fieldName)
|
||||
{
|
||||
return _fields.get(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if our persistent object defines a primary key.
|
||||
*/
|
||||
|
||||
@@ -26,10 +26,12 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.samskivert.jdbc.depot.ConstructedQuery;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* Marks a field as computed, meaning it is ignored for schema purposes and it does not directly
|
||||
* correspond to a column in a table, thus its value must be overridden in the {@link ConstructedQuery}.
|
||||
* correspond to a column in a table, thus its value must be overridden in the {@link
|
||||
* ConstructedQuery}.
|
||||
*/
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.TYPE })
|
||||
@@ -40,4 +42,22 @@ public @interface Computed
|
||||
|
||||
/** A non-empty value here is taken as literal SQL and used to populate the computed field. */
|
||||
String fieldDefinition () default "";
|
||||
|
||||
/**
|
||||
* A computed record can shadow a concrete record, which causes any field the former has in
|
||||
* common with the latter and which is not otherwise overriden to inherit its definition. The
|
||||
* shadowed class may also be given at the field level.
|
||||
*
|
||||
* The purpose of shadowing is largely to avoid having to supply a {@link FieldOverride} when
|
||||
* querying for objects that that contain large subsets of some other persistent object's
|
||||
* fields -- in other words, when you use a computed entity to query only some of the columns
|
||||
* from a table.
|
||||
*
|
||||
* The shadowed class must have been brought into the query using e.g. {@link FromOverride}
|
||||
* or {@link Join} clauses. The referenced fields must be simple concrete columns in a table;
|
||||
* they must themselves be computed or overridden or shadowing.
|
||||
*
|
||||
* TODO: Do in fact let the shadowed field be computed, overriden or shadowing.
|
||||
*/
|
||||
Class<? extends PersistentRecord> shadowOf () default PersistentRecord.class;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user